HERP では多くの成果物が Nix を用いてビルドされている.例として,アプリケーションの Docker image,npm ライブラリの tarball,Helm chart を元にした Kubernetes の manifest ファイルなどが挙げられる.
"purely functional package manager" である Nix を利用すると高い再現性を持ったビルドを実現できるが,purely functional であるがゆえに,private なリソースに依存したビルドには一工夫が必要になる.private なリソースにアクセスするためには概して何らかの credential が必要になるからだ.そのような credential はビルド手順*1に残したくないし,仮に残したとしても,一定時間で expire したりするのでいずれにしても再現性が得られない.
もちろん,ビルド時に private なリソースを利用できないのでは営利企業内で利用するのは難しい.そこで,部分的に再現性を諦めるための抜け道が用意されている.本稿では fetchurl
関数の netrcPhase
と netrcImpureEnvVars
オプションを利用する方法を紹介する.
なお,本稿で単に fetchurl
と書いた場合には,builtins.fetchurl
ではなく NixOS/nixpkgs
リポジトリに定義されている fetchurl
関数を指すものとする.
問題設定#
最近,アプリケーションが依存する社内用パッケージのレジストリとして AWS CodeArtifact を利用し始めたので,これを具体例に挙げて解説する.
https://mydomain-123456789.d.codeartifact.ap-northeast-1.amazonaws.com:443/npm/myrepository/private-package/-/private-package-0.1.0.tgz
という URL から private-package
というパッケージを取得することを考える.また,パケッジマネジャーには Yarn を利用する.
netrcPhase
と netrcImpureEnvVars
#
fetchurl
は HTTP を通じてリソースを取得するための関数である.fetchurl
を利用して,認可を要する private なリソースにアクセスしたい場合,netrc ファイルが利用できる*2.
fetchurl
は内部的に curl
コマンドを利用してリソースを取得する*3.また,fetchurl
には,curl
が利用する netrc ファイルを生成する手順を伝えるための netrcPhase
というオプション*4が用意されている.このオプションを指定すると,curl
コマンドに --netrc-file $PWD/netrc
というオプションを追加で渡してくれる*5.
また,重要な点として,ビルド時の環境に設定されている環境変数のうち,netrcImpureEnvVars
オプション*6に指定した環境変数は netrcPhase
の中で利用することができる.
これらを勘案すると,最終的に以下のような derivation を書けば目的が達成できることになる.
pkgs.fetchurl {
name = "private-package";
url = "https://mydomain-123456789.d.codeartifact.ap-northeast-1.amazonaws.com:443/npm/myrepository/private-package/-/private-package-0.1.0.tgz";
# ここにホワイトリスト形式で指定した環境変数は netrcPhase 内で参照できる
netrcImpureEnvVars = [
"CREDENTIAL"
];
# ./netrc ファイルを作成する
# ここではビルド時の環境で設定されている環境変数 $CREDENTIAL が参照できる
netrcPhase = ''
do_something_with ''$CREDENTIAL
'';
}
netrc ファイルの内容#
fetchurl
に netrcPhase
オプションを指定し,netrc ファイルを生成する手順を与えれば,生成された netrc ファイルを内部で使われている curl
が利用するように取り計らってくれるということがわかった.次に問題になるのは,netrcPhase
でどのような netrc ファイルを生成すればよいのかという点である.そこで Nix のことは一旦忘れ,どのような netrc ファイルを記述すれば,以下のような curl
コマンドで CodeArtifact 上の package を取得できるかを明らかにしたい.
$ curl -LO --netrc-file ./netrc https://mydomain-123456789.d.codeartifact.ap-northeast-1.amazonaws.com:443/npm/myrepository/private-package/-/private-package-0.1.0.tgz
これには,天下り的だが,以下のような内容を記述すればよい.
ここで <authorization token>
は $ aws codeartifact get-authorization-token
で取得できる認可トークンである*7.
tarball を取得する derivation#
curl
経由で tarball を取得できたので,次は fetchurl
を利用して同じ tarball を取得する derivation を作ってみよう.これまでの内容を組み合わせると,以下のような derivation を書けばよいことになる.
必要な環境変数を設定した上でこの derivation をビルドすると,ビルド中に CodeArtifact から tarball をダウンロードすることができる.
$ export AWS_CODEARTIFACT_AUTHORIZATION_TOKEN=$(aws codeartifact get-authorization-token --domain mydomain --domain-owner 123456789 --query authorizationToken --output text)
$ nix-build ./private-package.nix
Yarn との統合#
実際の開発においては private-package
以外にも多数のライブラリに依存するので,上記のような derivation をいちいち手で書くわけにはいかない.yarn2nix
を利用すれば,yarn.lock
から自動的に derivation を生成してくれる.
$ nix-shell -p yarn2nix --run 'yarn2nix > ./nix/yarn.nix'
上記のコマンドを実行すると,以下のようなファイルが生成されているはずである.
ここで,引数の fetchurl
には netrcImpureEnvVars
および netrcPhase
が指定された状態のものが渡される必要があるので,fetchurl
を override しつつ callPackage
する.
この derivation は,以下のような記述をすることで,Yarn のオフラインキャッシュとして利用できる.
謝辞#
広告#
株式会社 HERP では,プロダクト開発に集中できる環境を整備できるエンジニアを募集しています.
脚注#
*1: Nix であれば derivation
*2: https://nixos.wiki/wiki/Enterprise
*7: codeartifact:GetAuthorizationToken
だけでなく sts:GetServiceBearerToken
の実行権限も必要なことに注意されたい.