blog.anqou.net
rss
author
tags

Nix で pause が入った Docker イメージを作る

Kubernetes ではしばしば、何もせずに待機し続ける Pod を立ち上げたくなることがあります。この際 bash -c "sleep infinity" などを使用してもよいのですが、プロセスに PID 1 が割り当てられてしまう影響[1]で Pod 終了時に 30 秒待機しないといけないという不便があります[2]

このようなときに便利なのが github.com/kubernetes/kubernetes にある pause コマンドです。シグナルハンドラが適切に設定されており SIGTERMSIGINT が送られてきたときに即座に終了できます。

ということでこのコマンドを組み込んだ Docker イメージを Nix で作ります。 Nixpkgs にある kubernetes パッケージは multiple-output package になっていて kubernetes.pause で pause コマンドを参照できるようになっています。そこで以下のように書くと pause コマンドが入った Docker イメージが作れます。

{
  dockerTools,
  kubernetes,
  runtimeShell,
  ...
}:
dockerTools.buildLayeredImage {
  name = "ghcr.io/ushitora-anqou/pause";
  tag = "0.1.0";
  created = "now";
  contents = [
    kubernetes.pause
  ];
  enableFakechroot = true;
  config = {
    Entrypoint = ["pause"];
  };
}

ただしこのバイナリは 40MB 近くもあります:

❯ docker images
                                                                                                            i Info →   U  In Use
IMAGE                                      ID             DISK USAGE   CONTENT SIZE   EXTRA
ghcr.io/ushitora-anqou/pause:0.1.0         3d28987da42c       39.4MB             0B

これを縮めたい場合は、以下のように自前で静的ビルドし remove-references-to を使って不要な依存を消す必要があります[3]

{
  fetchurl,
  glibc,
  libgcc,
  removeReferencesTo,
  stdenv,
}:
stdenv.mkDerivation (finalAttrs: {
  pname = "pause";
  version = "0.1.0";
  src = fetchurl {
    url = "https://raw.githubusercontent.com/kubernetes/kubernetes/v1.36.1/build/pause/linux/pause.c";
    hash = "sha256-FKP+IatfjmKTeitFPnbzuUsNPHwEw9Cbw3GV9UwWleo=";
  };
  nativeBuildInputs = [glibc.static];
  unpackPhase = ":";
  buildPhase = ''
    runHook preBuild
    gcc -Os -Wall -Werror -static -DVERSION=v${finalAttrs.version} -o pause $src
    strip pause
    ${removeReferencesTo}/bin/remove-references-to -t ${glibc.libgcc} -t ${glibc} pause
    runHook postBuild
  '';
  installPhase = ''
    runHook preInstall
    mkdir -p $out/bin
    cp pause $out/bin
    runHook postInstall
  '';
})

kubernetes.pause の代わりに callPackage ./pause.nix {} のように上記ビルドを埋め込むようにすると、741KB まで縮みました:

❯ docker images
                                                                                                            i Info →   U  In Use
IMAGE                                      ID             DISK USAGE   CONTENT SIZE   EXTRA
ghcr.io/ushitora-anqou/pause:0.1.0         179ee524f495        741kB             0B    U

注釈

  1. あまりいい資料が見つからなかったのですがこのサイトが参考になりそう。

  2. この記事を書くときに timeout infinity sleep infinity で代用できることを知りました

  3. Nix で Docker イメージを作ると nixpkgs にあるソフトウェアを自由に使えるので便利なんですが、このあたりのランタイムの依存管理が面倒なのが難点です。逆に言うと、イメージサイズに糸目をつけなければとりあえず動きはするのでそこは便利です。