前回に引き続き、Docker イメージタグにまつわるアンチパターンをいくつか紹介します。 その後、陥らないための対応策をいくつか紹介します。

さらにアンチパターンいくつか

前回では semantic versioning をアンチパターンとして紹介しました。 類似のアンチパターンを追加で列挙しておきます。

  • git の commit hash や tag 名を含める。
  • ビルドの日付を含める。

これらは複数回 docker build を行った場合に、イメージに含まれるコンテンツが同じである保証はできません。

そもそも Docker イメージタグでは、一意性を保証できない

何が混乱の原因なのか。今まで書いてきたアンチパターンで、そろそろ察しがついてきた読者もいらっしゃると思います。

Docker イメージにまつわる誤解の中で、最も危険なものは「Docker イメージのタグで、イメージに含まれるコンテンツを固定できる(はず)」です。

タグは同じでも、元となる Dockerfile を書き換えれば中身は変わります。これは当然ですね。

加えて、 Dockerfile を変更しなくても パッケージ管理ツール(aptapknpmpip など)や、curl コマンドやCOPY 命令による外部依存性が存在すれば、再ビルド時に同じ中身になる保証はありません。言われてみれば当然と思われるかもしれませんが、盲点なようです。

やや余談ですが、Docker イメージに限らず、ビルドの再現性については、しばしば重要視され「決定論的ビルド」という考え方や、基づくツールも存在します。 しかし決定論的ビルドを重視したツールは今のところ一般的ではありませんし、徹底するにもコストがかかります。 決定論的ビルドツールに興味のある読者は、Bazel Guix 辺りをキーワードに調べていただくと必要な情報に辿り着きやすいかもしれません。

すぐできる対応策

2023年夏時点では、決定論的ビルドツールの導入は敷居が高いので、簡単で効果的な対応策を 2 つほど紹介します。

タグの上書き push 禁止

いくつかの Docker レジストリには、既に同じタグがあるイメージの push を拒否する設定が、用意されています。 同じタグで中身が違い得るという問題は、この設定で解決します。

注意点

ただし、タグを削除する機能も Docker レジストリに用意されているはずです。タグ削除機能の利用制限を掛けないと、運用時に失敗する可能性があります。

sha256 hash の利用

docker ではイメージの指定方法として、タグ以外にダイジェスト(ハッシュ)も利用できます。 「ハッシュとは何か」という深いところは本稿では割愛しますが、「ハッシュが同じイメージは、中身も同じ」であることが(確率論的ながら、実用上は確実に)言えます。

例えば Dockerfile で…

FROM ubuntu:20.04

…と書いているところを…

FROM ubuntu:20.04@sha256:3246518d9735254519e1b2ff35f95686e4a5011c90c85344c1f38df7bae9dd37

…とダイジェストを追加すれば、使われるイメージは固定されます。 ダイジェスト指定は Dockerfile だけでなく、docker run 時のイメージ指定や、Kubernetes Pod の image: でも指定可能です。

イメージのダイジェストは、Web UI を提供している Docker レジストリ製品であれば容易に確認可能なはずです。

また docker pull でイメージを手元においている場合は、 docker images --digests でダイジェストを確認できます。

注意点

いくつかの Docker レジストリ製品では「タグ付けが外れたイメージを削除する」という機能が用意されています。この機能を有効化していると、下記のような事故が起こりがちです。

  1. hoge:tag@sha256:2345.... でダイジェスト指定して使っている。
  2. 誰かが docker build -t tag hoge && docker push hoge:tag する。
  3. レジストリ上の hoge:tagsha256:2345.... 以外のダイジェストと紐付けられる。(タグ付けが外れる)
  4. レジストリが、 sha256:2345.... をタグが外れたイメージとして認識し、削除する。
    1. が困る。

タグ付けが外れたイメージも削除しない設定にすれば、この種の事故は起きなくなります。一方で、使われていないイメージを保持し続ける可能性もあり、ストレージのコストが高くなります。

このコスト問題に対する、現実的な落とし所の一つとして「イメージ固定の重要度を精査し、ダイジェスト指定が重要なイメージと、そうでもないイメージとでレジストリを分ける」という手があります。

まとめ

docker のイメージタグに対する誤解から生じがちなリスクと、それを回避する方法について、ざっと書きました。 タグとダイジェストの性質を十分に理解していないと「何もしないのに壊れました (実は知らぬ間に docker image の中身が微妙に差し替わっている)」という事態を引き起こしがちです。

本稿で、読者の Docker コンテナ生活が少しでも平穏に近づけば、幸いです。