LoginSignup
69
37

More than 1 year has passed since last update.

Nixとはなにか、パッケージマネージャとしての使い方の紹介

Last updated at Posted at 2021-06-05

Nixってなに?

Nixの説明と簡単な使いかたを紹介したいと思います。

NixというとパッケージマネージャやOSとしてのNixOSの意味がありますが、ここではパッケージマネージャとしてのNixを説明します。

公式のマニュアルはこちらです。

Nixをパッケージマネージャとして使う場合は手元のWindows, Mac, LinuxでOSのインストールなしに利用することができます。

パッケージマネージャだけでなく、開発環境を含めた開発フローについてpythonを使った例を示します。

NixではパッケージのことをDerivationと呼びますので注意してください。Nixでは独特の用語が多いので用語集をつけました。参考にしてください。

OSのバージョンを気にしないソフトのインストール

あるソフトを使う場合にOSのバージョンを気にしたり、関連するソフトをインストールするかと思います。
MacのユーザーとLinuxのユーザーの開発環境が違っていてつらいという状況もあるでしょう。
同じソフトだけどバージョン違いのものをいれて試したいこともあるでしょう。
Nixをつかうとシステムを汚さずに使いたいソフトをどこででもインストールできます。

例えば、gccをインストールしたい場合はMacやLinuxにかかわらず、次のコマンドでインストールできます。

nix-env -i gcc

どこにインストールするのか?

プログラムをインストールケースは次のようなものがサポートされています。

  • システムのユーザー全員
  • システムのあるユーザー
  • あるユーザーのある開発環境だけ
    • nix-shellコマンドを使う。例えばpythonが欲しいときはnix-shell -p pythonとすればpythonがあるshellの環境ができます。exitをするとそのpythonへの参照が消えます。

ハッシュによるバージョン管理

Nixはパッケージの依存関係をハッシュで管理しています。
パッケージを作るのに必要なパッケージのリストとパッケージのソースコードのアーカイブのハッシュをつくってそれをもとに出力先を決めます。詳しくはこちらを参照してください。ハッシュによって再現性を担保します。

ハッシュでつくられたパッケージを中央のレポジトリ(https://cache.nixos.org) でキャッシュすることでバイナリのパッケージを配布しています。個人的なパッケージのキャッシュにはローカルのストレージ、AWSのS3、cachixが使えます。

パッケージ作成が簡単

Nixはもっとも簡単にパッケージが作れものじゃないかなと思っています。 単にシェルの結果だけを生成するパッケージは次のようなものです。

runCommand "hello" {} ''
  mkdir $out
  echo hello > $out/hello.txt
'';

まとめ

Nixの利点をまとめると以下のようになります。

  • 再現性
  • バイナリキャッシュ
  • 独自パッケージの提供が簡単(中央のリポジトリがなくてもパッケージを提供できる。)
  • MacでもLinuxでも同じパッケージを利用可能

いいことばかりだけではなく、Nixではなにがつらいのか以下に紹介しておきます。

  • 使いたいパッケージが公式サイトで提供がない。
  • コミュニティーが小さい。
  • 既存のパッケージのコードがよくわからないが、読めないと生きていけない。
  • nix expressionのデバッグが難しい。
  • ストレージを多く使う傾向がある。
  • セキュリティパッチを当てられない。セキュリティの対応にはソフトの再インストールが必要。
  • 成熟してない。次から次に新しいパッケージの管理ツールがでてくるので学習コストが高め。

Dockerとの違いは?

再現性というとDockerを思い浮かべるでしょう。Nixも同じような用途で使うことができますが、Nixはパッケージ管理ソフトです。時にはHomebrewのように時にはDockerのように使えます。
細かいDockerとの違いはこちらの記事や下記の表が参考になるでしょう。

Nix Docker
実行環境 通常のユーザー環境 コンテナ
実行権限 ユーザーと同じ権限 ルート権限
MacOSでの利用 MacOSのネイティブ(homebrew的な立ち位置で使える。) コンテナ内のLinux
再現性の担保方法 依存するファイルのハッシュで担保(実行したときによらず同じ結果になるはず) ビルドしたときのスナップショットで担保
ビルドの再現性 いつ実行しても同じものができる。 実行したときの最新のファイルがインストールされる。ダウンロードするファイルが違う可能性がある。
ビルドプロセス Nix言語で記述:aptやyumは使えない。 Dockerfileで記述:aptやyumが使える。
キャッシュする単位 パッケージごと スナップショット全体(各ビルドのステップごとにキャッシュができますが、一部を変更するとそれ以降はすべてやり直しになります。)

Bazelとの違いは?

Bazelはgoogleが開発したビルドツールですね。
Googleでは非常によく使われているようで、例えばGoogle ChromeのビルドにBazelは使われています。

Bazelはビルドに必要とするファイルをすべて洗い出してサンドボックス環境でビルドを行います。
そのため、再現性が高く、効率よくビルドした結果をキャッシュするビルドツールです。この考えはnixのビルド方法に近いです。

こちらの記事にあるようにBazelを使って再現性が高い機械学習のパイプラインを作った例があります。
https://www.spotdraft.com/engineering-blog/how-we-used-bazel-to-streamline-our-ai-development

Bazelはビルドツールとして設計されていますが、Nixはパッケージマネージャとして設計されています。
使うターゲットの違いがあります。

どんな用途使うといいと思っているか?

以下のようなケースでnixを使うといいと考えています。
nixではカスタマイズしたツールをインストールするのが簡単です。
nixでは開発環境の構築はnix-shellを実行するだけです。
環境構築が複雑になってきたらnixをつかうことを検討してもいいかもしれません。
コンテナのほうが向いている場合は無理に使う必要はないです。

  • エディタなどのツールの設定ファイルの管理(.vimrcや.emacs)
  • セットアップが複雑なプロジェクト(まずaptでなにかインストールして、それから言語特有のプログラムを実行してとか。)
  • 複数の言語やライブラリを利用するプロジェクト(rustで作られたライブラリをpythonから利用するとか)

インストール方法

下記を実行するだけです。LinuxでもMacでも同様です。Windowsの場合はWSL2上で実行しましょう。

$ curl -L https://nixos.org/nix/install | sh

アンインストール方法

  1. ~/.profile や ~/.bash_profile の. "$HOME/.nix-profile/etc/profile.d/nix.sh"の行を消す。
  2. rm -rf $HOME/{.nix-channels,.nix-defexpr,.nix-profile,.config/nixpkgs} sudo
  3. rm -rf /nix
  4. マルチユーザー用にインストールしているならnixbld[0-9]のユーザーを削除

パッケージの管理の仕組み

パッケージは次のフローで作成されます。
通常はnix-buildコマンドで次の手順が一度に実行されます。

  1. nix言語で書かれたものをdrvファイルにコンパイルします。その結果/nix/store/*.drvファイルができます。この時点では実際のビルドはなにも始まりません。
  2. drvファイルをつかってパッケージの中身を作成します。bashとかpythonとかいろいろなコマンドを使って実際のビルドが行われます。その結果、/nix/store/*(drvがついてないディレクトリ)ができあがります。

パッケージマネージャの使い方

パッケージマネージャとしてのコマンドを紹介します。
こちらのコマンドはユーザーの環境内でグローバルに設定するために使います。
開発環境で使うツールはshell.nixファイルを作りnix-shellコマンドを起動して利用します。

パッケージのインストールにこれまでnix-envを使ってきましたが、nix profileというコマンドが試験的に導入されています。二つの使い方を紹介しますが、互いに互換性がないので初めてつかうかたはnix profileから始めてもいいかもしません。

  • nix-envをつかう場合
    • インストール
      • nix-env -i "パッケージ名"
    • 特定のチャネルのパッケージをインストール
    • アンインストール
      • nix-env -e "パッケージ名"
    • インストール済みのパッケージの一覧
      • nix-env -q
    • インストール可能なパッケージの一覧
      • nix-env -qa
  • nix profileを使う場合
    • インストール
      • nix profile install nixpkgs# "パッケージ名"
    • 特定のチャネルのパッケージをインストール
      • nix profile install nixpkgs/release-20.09#firefox
    • アンインストール
      • nix profile listで削除したいパッケージのpositionを調べて下記を実行
      • nix profile remove "position"
    • インストール済みのパッケージの一覧
      • nix profile list

ディスクがなくなってきたときはnix-collect-garbageを実行してGCをします。

パッケージの構成

パッケージの作り方

Nix Expression Language(Nix言語)

パッケージを作る前にパッケージを書くためのNixに慣れる必要があります。
詳細はこちらのwikiにありますが、特徴は次のようなものです。

  • 関数型言語
  • 遅延評価
  • 動的型付け
  • ファイル全体が一つの式
  • 式の型は次がすべて
    • 文字列型
    • 整数型
    • 浮動小数点型
    • パス
    • ブーリアン
    • null
    • リスト
    • 辞書型
    • 関数(ラムダ)
    • derivation

例えば次のようなdefault.nixファイルがある場合を考えます。
ファイルですが、一つの関数になっていて引数は{}がついているので辞書型で
その辞書の要素(キー)にpkgsというのがあります。
pkgsというパッケージの辞書の中でstdenvのキーのところにmkDerivationというパッケージ(derivation)をつくる関数があって、引数として辞書をとります。
この場合はnameという要素(キー)にpackage1という文字列をいれて、パッケージ(derivation)の名前をpackage1として宣言しています。

{pkgs}:
pkgs.stdenv.mkDerivation {
  name = "package1";
##以下省略
}

Derivation

Sandbox

ファイル構成

再現性の高いパッケージの作り方(NivとFlakes)

Nixによるパッケージは再現性が高いですが、nixpkgsなど関連するパッケージを固定しておかないと環境によってはインストールされるものが違ってきます。

固定する方法は二通りあって、nivとflakesです。

  • niv
    • gitで管理されたレポジトリをまとめてくれて、nix上で使えるようにするツール。
  • flakes
    • nixコマンドに統合された次世代型パッケージ管理ツール。
    • 従来のdefault.nixと互換性がない。

ユーザー環境の管理方法

開発環境の作り方

開発環境の作り方はいくつかあります。

  • nix-shell -p "パッケージをスペース区切りで列挙"
    • 例、nix-shell -p sqlite gcc
  • shell.nixまたはdefault.nixファイルを置き、derivationを出力するようにする。
    • 例として次のshell.nixを作りnix-shellコマンドを実行します。
{pkgs ? import <nixpkgs> {}}: # nix-shellへの引数(?は引数がない場合のデフォルトで今回はnixpkgsのパッケージ(システムのデフォルトが使われる。))
pkgs.mkShell { # mkShellはソースの無いderivationを作るために使う便利関数
  buildInputs = [ pkgs.sqlite pkgs.gcc ]; # 開発環境で使うパッケージのリスト
}
  • flake.nixファイルを置き、devShellをターゲットにderivationを出力するようにする。
    • 例として次のflake.nixを作りnix developコマンドを実行します。
{ # ソース: https://nixos.wiki/wiki/Flakes
  description = "my project description";

  inputs.flake-utils.url = "github:numtide/flake-utils";

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem
      (system:
        let pkgs = nixpkgs.legacyPackages.${system}; in
        {
          devShell = import ./shell.nix { inherit pkgs; };
        }
      );
}

Pythonの使い方

  • nixpkgsから直接使う
    • 公式の標準の方法

    • pros

      • 使い方が簡単
        • 単にパッケージを並べるだけでパッケージをインストールずみのpythonが使えます。
        • サンプル:nix-shell -p 'python38.withPackages(ps: with ps; [ numpy toolz requests ])' --run python3
      • インストールそのもので失敗しにくい。
      • wheelも扱える。
    • cons

      • cache.nixos.orgに機械学習系のパッケージがキャッシュされてないのでフルスクラッチからビルドが走りインストールに時間がかかかる。たとえば、transformersとpandasを使うプロジェクトで12時間くらいかかる。(どういう条件でキャッシュされるのかはよくわかっていません。)
  • mach-nix
    • requirements.txtを使うプロジェクトの場合
  • poetry2nix
    • pyproject.tomlを使うプロジェクトの場合
    • pros
      • cache.nixos.orgにキャッシュがなくてもwhlなどを利用でき、インストールが他より速い。(nixをつかわない場合と同じくらい?)
    • cons
      • poetryそのものでパッケージのインストールができないとできない。

カスタマイズしたツールの使い方(Sixel対応のScreenのインストール)

nixpkgsが提供していないソフトのパッケージの仕方を見ていきます。
その例としてsixelに対応したgnu screenのパッケージを行います。

  1. nixpkgsに類似のパッケージがないか探す。 
  2. flake.nixの雛形の生成をするためにnix flake initを実行
  3. flake.nixのinputsのところにインストールしたいソフトのURLをはる
  4. flake.nixのdefaultPackage.x86_64-linux にnixpkgsのmkDerivationのところを貼り付け、srcのところinputsのものに変更する。
  5. nix profile install github:<ユーザーID>/<レポジトリ名>でインストール
    • nix profile install github:junjihashimoto/screen-nix

CUDAの使い方

CUDAのためのドライバのセットアップ方法はNixOSとそれ以外では違います。
Ubuntu上でNixを使っている場合はドライバの設定は不要ですが、次のライブラリのセットアップを行ってください。

NixOS以外の場合

nixではnvidiaのライブラリ(libcuda.soなど)を/run/opengl-driver/libは以下に置くことになっています。
CUDAを使う前に下記のコマンドを実行しましょう。systemdで起動時に設定したい場合はこちらを参照してください。

sudo mkdir -p /run/opengl-driver/lib
sudo ln -s /usr/lib/x86_64-linux-gnu/libcuda* /run/opengl-driver/lib/
sudo ln -s /usr/lib/x86_64-linux-gnu/libnvidia-* /run/opengl-driver/lib/

WSL2の場合は次のようになります。

sudo mkdir -p /run/opengl-driver/lib
sudo ln -s /usr/lib/wsl/lib/lib*.* /run/opengl-driver/lib/

NixOSの場合

OpenGLの使い方

NixOS以外の場合

OpenGLを動かす場合も/run/opengl-driver/libは以下に置くことになっているのですが、上記のCUDAのやりかたではうまく動かないのでnixGLを使いましょう。

NixOSの場合

OpenGLの使い方

デバッグの仕方

nix-buildに関すること

キャッシュ

別のディレクトリにキャッシュを保存する方法

キャッシュの参照先の追加方法

NFSで/nix/storeを共有する方法(シングルユーザーモード)

https://nixos.wiki/wiki/NFS
こちらにあるようにシングルユーザーモードのときは/nixのディレクトリをNFS上に置くことができます。
これにより、すべてのサーバーでパッケージが共有できます。

fstabに次のような設定をいれるといいようです。

<host_or_ip>/nix /nix nfs nofail,x-systemd.device-timeout=4,local_lock=all 0 0
しかし、`local_lock=flock`のオプションをつけるようにありますが、これは同一サーバー内でのロックに限定する話のように見えます。(https://linux.die.net/man/5/nfs) 複数のサーバーで使う場合は`local_lock=none`もしくはこれを付けないほうがいいかもしれません。 また、[こちらのしくじりの話](https://techblog.raccoon.ne.jp/archives/1635140633.html)からするとロックのときにキャッシュをうまく破棄させるために`noac`のオプションが必要なようです。

NFSで/nix/storeを共有する方法(マルチユーザーモード)

マルチーユーザーモードで単純にNFSで/nixをマウントすると、nix-daemonというデーモンと通信するための/nix/var/nix/daemon-socket/socketのファイルが作られるため、マシンごとにこのソケットのファイルがつくれなくなり、nix-daemonが起動できなくなります。
なので、次のようにbindの設定をいれるとローカルの/var/run/nix-daemon-socketのディレクトリにソケットのファイルができるので、nix-daemonが起動できるようになります。

<host_or_ip>/nix /nix nfs nofail,x-systemd.device-timeout=4,local_lock=all 0 0
/var/run/nix-daemon-socket    /nix/var/nix/daemon-socket          none    bind        0       0

分散ビルド

複数のマシンをつかってビルドを高速にする場合には、https://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html を参照ください。
わかりにくい気がしますが、次の手順でセットアップします。

  1. sshできるnixをインストールしたサーバーを用意。
  2. /etc/nix/machinesに分散ビルド用のサーバーを列挙

/etc/nix/machinesのフォーマット

#sshのユーザー名@ホスト マシンのタイプ sshの鍵 並列化の数 優先度 (kvmをつかうかどうかのフラグ)
nix@scratchy.labs.cs.uu.nl  i686-linux      /home/nix/.ssh/id_scratchy_auto        8 1 kvm
nix@itchy.labs.cs.uu.nl     i686-linux      /home/nix/.ssh/id_scratchy_auto        8 2
nix@poochie.labs.cs.uu.nl   i686-linux      /home/nix/.ssh/id_scratchy_auto        1 2 kvm benchmark

ビルドは一台のホストにビルド結果を集約しつつ、必要に応じてリモートのサーバーにファイルをコピーしてリモートのサーバーでビルドし、それをまたホストにコピーするという手順で行うようです。

セキュリティ

用語集

  • nix : パッケージマネージャのコマンド
  • nixpkgs : nixのパッケージ一覧をまとめたもの。 https://github.com/nixos/nixpkgs で提供しています。
  • derivation : パッケージのこと、debianであればdebファイルのこと。
  • NixOS : nixをパッケージマネージャとしてつかうlinuxのディストリビューション
  • Nix言語 : パッケージを記述する言語、nix expressionともいう。
  • チャンネル:パッケージ一覧のセットorバージョン、ubuntuだったら18.04とか20.04とか。
  • Nixストア: パッケージをインストールしているディレクトリ(/nix/store
  • drvファイル

参考文献

69
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
69
37