🚀

Nixで整える開発環境

2024/12/16に公開

開発を素早く、品質高く行うには、日頃の開発環境を整えるのが非常に重要です。しかし、開発環境を整え、それを維持し続けるのは、かなり手間と労力がかかります。今回は、Nixというツールを使った、維持しやすい開発環境の整え方を紹介していきます。

Nixって?

Nixというのは、パッケージ管理とシステム設定管理のためのOSSツールです。標語は「確実かつ再現可能なパッケージ管理」で、一旦どこかの環境でパッケージが入れられたら、同じ手順で何度でも入れられるし、同じ環境ならどこでも同じ手順で入るという体験を目指しています。

この標語は他のパッケージ管理ツールでよく感じる問題意識からきています。例えば、macOS向けのパッケージ管理の大御所、Homebrewでは、パッケージ毎に常に最新のバージョンがビルドされ提供されるわけですが、これだと古いバージョンを維持したいといった使い方はできず、その時その時で入るパッケージが変わり再現性がありません。また、それぞれのパッケージが他のパッケージに依存して構成されていますが、依存パッケージがそれぞれ独立にバージョンアップされていくため、パッケージAのバージョンに変更がなかったとしても、その依存パッケージBに変更が入り、パッケージAの動作が変わってしまうといったことがたまに起こったりと、パッケージ管理の不確実性に常に怯えなければいけません。Nixはこれをパッケージビルド環境を個々のパッケージで完全に独立させて管理し、差し戻しを常に可能にすることで、解決するツールとなっています。

ただ、Nixの魅力はそれだけに留まりません。パッケージ管理をより柔軟にするための設定記述と、その記述に特化したNix言語を提供することで、パッケージ管理の制御可能な部分を増やし、より柔軟に楽にする体験も提供してくれます。また、多数のプラグインも提供され、日に日にパッケージ管理を超えたシステム設定基盤として成長を続けています。今回はその片鱗をお見せしていきたいと思います。

Nixをインストールする

弊社ではmacOSがデファクトの開発用OSとなってるので、macOSでのシステム設定をターゲットにしていきます。Nixのインストールは、公式サイトの手順に則るのでもいいですが、Nixエコシステムを利用したエンタープライズ向けサービスを提供しているDeterministic Systems社提供の非公式Nixインストーラを使うとアンインストール手順なども用意されているので、今回はそっちを使っていきます。以下をまず実行していきましょう:

curl -sSf -L https://install.determinate.systems/nix | \
  sh -s -- install

root実行のためのパスワードなど聞かれるので、適宜入力していきましょう。インストールされると、「Nix Store」という名前の仮想ボリュームが作られ、/nix にマウントされることになります。Nixはこの /nix ディレクトリで、パッケージ管理を行っていきます。

後は、シェルを再起動することで、自動的に /nix のデフォルトの bin パスなどが読み込まれます。シェルを再起動し、以下でインストールが正常に完了したか確認してみましょう:

$ nix search nixpkgs '^curl$'
* legacyPackages.aarch64-darwin.curl (8.11.0)
  Command line tool for transferring files with URL syntax
...

これは、curl という名前のパッケージを https://github.com/nixos/nixpkgs に登録されているものから探すコマンドになります。無事パッケージが表示されれば、インストール成功です。

Nixパッケージを使う

まずはNixでパッケージをインストールして、使ってみましょう。先ほど検索に引っかかった、curl コマンドをインストールして、使ってみます:

$ nix shell nixpkgs#curl
$ curl --version
curl 8.11.0 ...
$ which curl
/nix/store/78b40bakdk6ldzd2cnzqqc6k2ffq6q80-curl-8.11.0-bin/bin/curl
$ exit
exit

nix shell はNixでインストールされたパッケージのプログラムを自由に使えるシェル環境を作ってくれるコマンドで、使いたいパッケージを指定することで、そのパッケージをインストールしてプログラム用の環境を整えてくれます。これは、元々使っていた環境を全く汚染することがないので、お試しでパッケージ入れて、使ってみたい時に便利です。

インストールされたパッケージは、/nix/store 配下に保存されます。もし不要になったら、

nix store gc

を実行することで、使われていないパッケージは一掃することができます。

ただ、現環境を汚染しないと言っても、nix shell を一々立ち上げるのは面倒ですし、シェルプログラムは独自の物を使いたいこともありますよね? もちろん、その点も解決する方法が用意されていますので、次はそちらを見ていきましょう。

ユーザ環境を整える

Nixコミュニティでは、Home Managerというプラグインが提供されており、これを使用すればNixパッケージと連携してユーザ環境を、宣言的に整備することができます。

まずは、どこかに作業用のディレクトリを作って、以下のファイルを flake.nix という名前で置いてみましょう:

flake.nix
{
  description = "A fake to setup user environment";

  inputs = {
    nixpkgs = {
      url = "github:nixos/nixpkgs?ref=nixos-unstable";
    };

    home-manager = {
      url = "github:nix-community/home-manager";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = inputs@{
    self,
    nixpkgs,
    home-manager,
  }: let
    username = "<ユーザ名>";
    homedir = "/Users/${username}";
    system = "aarch64-darwin";

    pkgs = import nixpkgs {
      inherit system;
    };
  in {
    homeConfigurations.${username} = home-manager.lib.homeManagerConfiguration {
      inherit pkgs;

      modules = [
        {
          home.stateVersion = "24.11";

          home.username = username;
          home.homeDirectory = homedir;

          programs.home-manager.enable = true;

          home.packages = [
            pkgs.curl
          ];
        }
      ];
    };
  };
}

そして、以下を実行すると、Nix経由でインストールした curl が使用できるようになります:

$ nix run home-manager -- switch --flake .
$ curl --version
curl 8.11.0 ...
$ which curl
/Users/workuser/.nix-profile/bin/curl

この設定はFlakesというNixの機能を使っています。FlakesはNix言語で書かれた設定をファイル分割し、複数の外部ファイル(Flake)に依存させつつ、再利用性と再現性を担保できるような機能になっています。上記実行時、flake.lockというファイルができたと思いますが、ここには依存するFlakeの一覧と参照時のリビジョンが記録されており、次回以降このリビジョン情報により参照当時と同じ依存を使うことで再現性が担保されるようになっています。Nix自体のプラグイン管理機構といった感じです。今回はこの管理機構を使って、パッケージ群 nixpkgs と、Home Manager プラグインを利用しています。この Flake システムにより、多くのプラグインを楽に利用できるのもNixの魅力でしょう。

さて、上記では Home Manager により単にパッケージをインストールして、nix shell なしに利用できるようにするだけでした。しかし、Home Manager の力はそれだけではありません。例えば、次のように設定を変えてみましょう:

            programs.home-manager.enable = true;

+           programs.poetry = {
+             enable = true;
+             settings = {
+               virtualenvs.create = true;
+               virtualenvs.in-project = true;
+             };
+           };
+
            home.packages = [
              pkgs.curl
            ];

そして、Home Managerによる設定をしてみると、

$ nix run home-manager -- switch --flake .
$ which poetry
/Users/workuser/.nix-profile/bin/poetry
$ cat '/Users/workuser/Library/Application Support/pypoetry/config.toml'
[virtualenvs]
create = true
in-project = true

というように、poetry をNix経由でインストールし、設定ファイルまで整備してくれます。gitssh、その他様々なツールの整備もサポートされており、Home Managerのマニュアルで設定方法が確認できるので、ぜひ使ってみるといいでしょう。

macOSの設定を整える

さて、ここまではNixパッケージを使った開発環境整備の方法を紹介してきたわけですが、実はNixコミュニティで提供されてるものはそれだけではありません。なんと、macOSの設定なんかもNix Darwinプラグインを使うことで宣言的に管理できます。

先ほどの flake.nix を以下のように変更してみます:

flake.nix
{
  description = "A fake to setup user environment";

  inputs = {
    nixpkgs = {
      url = "github:nixos/nixpkgs?ref=nixos-unstable";
    };

    home-manager = {
      url = "github:nix-community/home-manager";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    nix-darwin = {
      url = "github:LnL7/nix-darwin";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = inputs@{
    self,
    nixpkgs,
    home-manager,
    nix-darwin,
  }: let
    username = "<ユーザ名>";
    homedir = "/Users/${username}";
    hostname = "<ホスト名>";
    system = "aarch64-darwin";

    pkgs = import nixpkgs {
      inherit system;
    };
  in {
    darwinConfigurations.${hostname} = nix-darwin.lib.darwinSystem {
      inherit system;

      modules = [
        {
          system = {
            stateVersion = 5;

            defaults = {
              SoftwareUpdate.AutomaticallyInstallMacOSUpdates = true;
              NSGlobalDomain.AppleShowAllExtensions = true;

              finder = {
                AppleShowAllFiles = true;
                AppleShowAllExtensions = true;
                _FXShowPosixPathInTitle = true;
              };

              dock = {
                autohide = true;
              };
            };
          };

          nix = {
            useDaemon = true;
            optimise.automatic = true;

            settings = {
              experimental-features = "nix-command flakes";
            };
          };

          users = {
            users.${username}.home = homedir;
          };
        }
        home-manager.darwinModules.home-manager
        {
          home-manager.useGlobalPkgs = true;
          home-manager.useUserPackages = true;
          home-manager.users.${username} = {
            home.stateVersion = "24.11";

            home.username = username;
            home.homeDirectory = homedir;

            programs.home-manager.enable = true;

            programs.poetry = {
              enable = true;
              settings = {
                virtualenvs.create = true;
                virtualenvs.in-project = true;
              };
            };

            home.packages = [
              pkgs.curl
            ];
          };
        }
      ];
    };
  };
}

そして、今度は以下を実行してみます:

$ nix run nix-darwin -- switch --flake .

すると、Finderで全ての隠しファイルが表示される設定になる他、OSアップデートの自動化、Dockの自動非表示設定が入るなど、macOSの設定が行われることになります。もちろん、Home Managerの設定も一緒に行われます。他にも様々な設定が用意されていて、Nix Darwinのマニュアルから確認できます。Homebrewとの連携などもできるので、Homebrewの資産もそのまま活かしつつ、Nixでできる部分はNixでといった分担も行えます。

使いこなせば、手動での設定なしにPC乗り換えも夢ではないでしょうし、自分が何を設定したのか生きた備忘録としても活用できるようになるでしょう。

最後に

というわけで、開発環境を整えるのに便利なツール、Nixについて紹介しました。結構周辺ツール整っていて、自分で頑張ってシェルスクリプトなどで dotfiles 整備してる方も、Nixに切り替えると幸せになるかもしれません。また、弊社の開発で必須となるPHPのインストールは頭を悩ますことも多いと思いますが、Nixを使うことで手間がなくなるので、また機会があればNixでPHPを扱う方法なども紹介できればと思っています。

もし興味が出てきたら、ぜひNix使ってみてください。では、今回はこれにて。

Discussion