💤

nixでユーザ環境の構築やってみる

2023/12/24に公開

nixなるものが夢が詰まってそうだったので使ってみましたが、思ってたより色々ややこしくて覚えられないので記録に残しておきます。今回はユーザ環境、つまり、プロジェクト単位ではなくPC全体で共通で使っているパッケージの管理方法についてです。

今回はKVM上にnixosバージョン21.11の仮想環境を構築しその中で作業しています。

nixについて

nixといってもツールがいくつか分かれているみたいです。今回はユーザごとのPCの環境の構築のためにnixを利用するためnix-envというツールを使います。似たようなものにnix-devというものがありますが、こちらはプロジェクトごとのパッケージ管理に利用する目的のツールみたいです。pythonのvirtual-envみたいなもんだと思ってます(チュートリアル軽く触っての感想です)。これに対してnix-envはユーザごとのグローバルな領域(というよりセッションか?)に対してパッケージをインストールすします。

nix-envによるパッケージ管理

nix-envの具体的な使い方について説明していきます。

パッケージのインストール

まずはパッケージのインストール方法です。公式から利用可能なパッケージはここから検索が可能です。今回はvimとgitをインストールします。以下のコマンドで簡単にインストールできます。

$ nixenv --install vim
$ nixenv --i git

インストールされたパッケージは、ユーザの~/.nix-profileに配置されるようです。nixosではここにPATHが通っているので、コマンドが実行するときにはここが探索されるようでした。~/.nix-profile以下にはbinやlibなどのディレクトリが設置されているため、おそらく共有ライブラリ等もここに設置されると思われます。

ロールバック

nixの特徴として、パッケージをインストールするとその前後の状態をスナップショット的に保持することで、インストール前の状態にロールバックを行うことができます。以下のコマンドを実行するとバージョンみたいなのが確認できます。currentが現在のバージョンを表しており、初期状態が1(記事書く前に他のものインストールしちゃったので、正確には初期じゃないかも)、vimをインストールしたときが2、gitをインストールしたのが3でcurrentとなっていることがわかります。

$ nix-env --list-generations
    1   2023-12-24 01:09:10
    2   2023-12-24 15:33:59
    3   2023-12-24 15:34:55  (current)

ロールバックは以下のコマンドを実行することで、1つ前のバージョンに戻すことが可能なようです。

$ nix-env --rollback
switching profile from version 3 to 2
$ nix-env --list-generations
    1   2023-12-24 01:09:10
    2   2023-12-24 15:33:59  (current)
    3   2023-12-24 15:34:55
$ git --version
git: command not found

ロールバックするとgitコマンドは実行できなくなりましたが、バージョンの情報は残っているので前のバージョンに戻せばgitも使えるみたいです。

$ nix-env --switch-generation 3
switching profile from version 2 to 3
$ nix-env --list-generations
    1   2023-12-24 01:09:10
    2   2023-12-24 15:33:59  (current)
    3   2023-12-24 15:34:55
$ git --version
git version 2.42.0

アンインストールするとどうなるか試しましたが、次のようにバージョンが進みました。

$ nix-env --uninstall git
$ nix-env --list-generations
    1   2023-12-24 01:09:10
    2   2023-12-24 15:33:59
    3   2023-12-24 15:34:55
    4   2023-12-24 15:54:56  (current)
$ git --version
git: command not found

当然バージョンを3に戻せば問題なくgitが利用できます。おそらくバイナリは削除されずに残っており、バージョンを切り替えればすぐに戻せる状態ということだと思っています(調べてない)。

$ nix-env --switch-generation 3
$ nix-env --list-generations
    1   2023-12-24 01:09:10
    2   2023-12-24 15:33:59
    3   2023-12-24 15:34:55  (current)
    4   2023-12-24 15:54:56
$ git --version
git version 2.42.0

さてじゃあ、この状態でvimをunintallするとどうなるでしょうか?どうやらバージョンは5に進むようです。更に、5の状態ではgitコマンドが存在しています。てっきり4がなくなるかと思いましたが、そういうわけではないようです。
ちなみにロールバックするとgitが無い時のバージョンである4に移動するようです。

$ nix-env --uninstall vim
$ vim --version
vim: command not found
$ nix-env --list-generations
    1   2023-12-24 01:09:10
    2   2023-12-24 15:33:59
    3   2023-12-24 15:34:55
    4   2023-12-24 15:54:56
    5   2023-12-24 15:57:02  (current)
$ git --version
git version 2.42.0

$ nix-env --rollback
$ git --version
git: command not found

スクリプトによる管理

ここまでだと正直そこまで旨味ないかな?という気持ちですね。しかし、nixの本当によいところはこれらパッケージをnix言語という独自の言語で記述したスクリプトを使って、バージョン等を複数の環境で固定することができる点です。

今度はpython3とrubyをnix言語でインストールできるようにしてみましょう。とりあえずinsuko.nix(名前は何でもよい)という名前のファイルを作ってそこに以下の内容を書き込みます。

{ pkgs ? import <nixpkgs> {} }:
[
    pkgs.python3
    pkgs.ruby
]

以下のコマンドを実行することでrubyとpython3がインストールされます。

$ nix-env -if ./insuko.nix

記述したスクリプトの内容を見ていきます。
1行目ですが、これは.nixファイルに対する引数を定義しています。具体的にはimport関数でパッケージの情報が入っているchannelを読み込ませて、その結果を引数pkgsに格納しています。ちなみに?がつくことによってデフォルト引数としてimport関数の結果を格納しています。
そして2行目以降ですが、これはlistによってpkgsで参照しているchannelに存在するパッケージを指定しています。この指定によってnix-envはパッケージを読めるみたいです。ちなみに、インストールするパッケージが1つならlistは不要です。

更に別のパッケージをインストールしたい場合は、listに追記してもう一度スクリプトを読めば自動的にインストールされます。

バージョンを固定する

importする際に<nixpkgs>という値を引数なし({})で読み込んでいます。この<nixpkgs>はデフォルトではローカルに取り込んでいるnixpkgsのディレクトリへのパスを指しています。これによてimportによってnixpkgsディレクトリのdefault.nixというファイルが読み込まれるようです。nix-env -i pckageNameでパッケージをインストールするのは、このimport <nixpkgs>と同じファイルを呼んでパッケージをインストールしています。<nixpkgs>の指すパスは*$NIX_PATH*で決定されます。

さて<nixpkgs>を指定するとローカルに設置したnixpkgsのリポジトリを参照することになるため、環境ごとに設置したnixpkgsのバージョンが異なってしまうとインストールされるパッケージのバージョンも変わってしまいます。これを防ぐために、バージョンを固定した記述を行うことができます。具体的には以下のようにnixpkgsのtarballを読み込むなどすることによってバージョンを固定できます。

let
  nix23-11 = import (fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-23.11") {};
  nix22-11 = import (fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-22.11") {};
in
[
    nix23-11.python3
    nix23-11.ruby
    nix22-11.rustc
]

先程からちょっと変えて、引数ではなく単純な変数宣言に変更している点に注意してください(単に例示するために変更しただけで深い意味はないです)。letinの中で宣言した変数を後続のリストの作成で利用しているだけですね。あとはtarballをリモートからfetchしてきてそれぞれのバージョンのnixpkgsからパッケージを指定してinstallを行うといった具合です。

ちなみに、上記の例でrustcのバージョンを上げたい場合は単にnix23-11経由でrustcをインストールすればnixがいい感じにバージョンを上げてくれます。

shellスクリプトやansibleじゃだめなのか?

正直わけわかんない部分が多く公式のドキュメントも優しくはないので、ansibleはともかくshellスクリプトのほうが簡単かもしれないですね。ちょっと使ってみましたレベルの所感としては、パッケージマネージャーを複数のLinuxディストリビューションとmacで統一できる点とnixだけインストールすれば使えるという点で嬉しいかも?って感じですね。現在試験的に導入されている各ツールが正式に稼働したらなにか色々変わってくるかもしれません。私はPC買ったときにコマンド1つで環境構築の夢を諦められないのでしばらくは戯れようと思います。

最後に

nixは便利っちゃ便利ですが学習が非常に難しいですね。まだnix言語やnixがパッケージを取り扱うための仕組みを理解しきれてないのでふわっとしている部分が多いです。そのうちわかったら追記していったりします。

Discussion