はじめに
この記事では、AWS の開発環境を Nix で管理することのメリットを紹介します。
私は普段 macOS で開発をしており、つい最近になって「Nix」を使い始めました。最初はかっこよさから導入しましたが、今ではNixをとても素晴らしいものとして認識しています。
特に AWS の開発では、awscli や terraform などのツールが必要になります。これらを Homebrew で管理していたときに感じていた「気持ち悪さ」が、Nix によって解消されました。
Homebrew で感じていた気持ち悪さ
awscli2 をインストールすると Python が入る
AWS CLI v2 を Homebrew でインストールすると、依存関係として Python がインストールされます。
brew install awscli
これ自体は仕方のないことですが、問題はこの Python が Homebrew のグローバルな環境に入ることです。
$ which python3
/opt/homebrew/bin/python3
私は Python のプロジェクトでは uv や mise を使って Python のバージョンを管理しています。しかし、awscli の依存で入った Python が $PATH に存在することで、意図しない Python が使われることがありました。
terraform のバージョン管理
terraform はプロジェクトによって使用するバージョンが異なることがあります。Homebrew では基本的に最新バージョンがインストールされるため、プロジェクトごとにバージョンを切り替えるには tfenv などの追加ツールが必要でした。
brew install tfenv
tfenv install 1.5.0
tfenv use 1.5.0
ツールのバージョンを管理するためのツールを管理する、という状況に違和感を感じていました。
Nix による解決
Nix Flakes でプロジェクトごとに環境を定義
Nix Flakes を使うと、プロジェクトのルートに flake.nix を置くだけで、そのプロジェクト専用の開発環境を定義できます。
例えば、terraform を使う AWS プロジェクトでは以下のように書きます。
# flake.nix
{
description = "AWS infrastructure project";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
devShells.default = pkgs.mkShell {
packages = with pkgs; [
awscli2
terraform
jq
];
};
}
);
}
この環境に入るには、プロジェクトディレクトリで以下を実行します。
nix develop
direnvなどの機能を使うことでさらに便利に管理もできます。
Python がグローバルに入らない
Nix でインストールした awscli2 の依存関係は、Nix Store (/nix/store/) の中に閉じ込められます。
$ nix develop
$ which aws
/nix/store/xxxxxxxx-awscli2-2.x.x/bin/aws
Homebrew のように /opt/homebrew/bin/python3 がグローバルに見える、ということがありません。シェルを抜ければ、そのツールは $PATH から消えます。
これが、私が Nix を使い始めて最も感動した点です。
プロジェクトごとに terraform のバージョンを固定
terraform のバージョンをプロジェクトごとに固定したい場合は、flake.nix で特定のバージョンを指定します。
{
description = "Legacy AWS project with terraform 1.5.0";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# 特定のコミットを指定してバージョンを固定
nixpkgs-terraform-1_5.url = "github:NixOS/nixpkgs/a3ed7406349a9335cb4c2a71369b697cecd9d351";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, nixpkgs-terraform-1_5, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
pkgs-tf = nixpkgs-terraform-1_5.legacyPackages.${system};
in
{
devShells.default = pkgs.mkShell {
packages = [
pkgs.awscli2
pkgs-tf.terraform # 1.5.0
pkgs.jq
];
};
}
);
}
tfenv のような追加ツールは不要です。flake.nix と flake.lock をリポジトリにコミットしておけば、チームメンバー全員が同じバージョンの terraform を使うことができます。
direnv との連携
毎回 nix develop を打つのは面倒なので、direnv と連携させます。
プロジェクトのルートに .envrc を作成します。
# .envrc
use flake
これで、ディレクトリに入るだけで自動的に Nix の開発環境がロードされます。
$ cd my-aws-project
direnv: loading .envrc
direnv: using flake
$ which terraform
/nix/store/xxxxxxxx-terraform-1.5.0/bin/terraform
$ cd ..
direnv: unloading
$ which terraform
terraform not found
ディレクトリを抜ければ環境も消える。これが Nix の「再現性」と「分離性」です。
まとめ
Nix を使って AWS の開発環境を管理することで、以下のメリットがありました。
| 観点 | Homebrew | Nix |
|---|---|---|
| 依存関係の分離 | グローバルに入る | Nix Store に閉じ込められる |
| バージョン管理 | tfenv などの追加ツールが必要 | flake.nix で完結 |
| 再現性 | Brewfile である程度可能 | flake.lock で完全に再現可能 |
| プロジェクト間の分離 | 難しい | ディレクトリごとに異なる環境を定義可能 |
特に「awscli2 をインストールしたら Python がグローバルに入る問題」が解消されたのは大きかったです。
Nix の学習コストは決して低くありませんが、一度理解してしまえば開発環境の管理が劇的に楽になります。Homebrew の管理に疲れている方は、ぜひ試してみてください。