ベースイメージの選定 — ディストリ公式に寄せるか最小イメージで削るか

コンテナのベースイメージ選定は、セキュリティ対応速度・運用負荷・チーム間の一貫性を左右する設計判断の起点です。最小イメージへの移行で攻撃面を絞れる一方、互換性やデバッグ環境を失うリスクがあります。ディストリ公式イメージは手厚いエコシステムを持ちますが、更新サイクルを設計しなければ肥大化と脆弱性蓄積の温床になります。本記事では、用途別の選定判断・比較整理・社内標準化の設計・更新波及の仕組みを一貫して解説します。

手元で作ったイメージが太り更新も追えなくなる構造

レイヤーが積み上がる仕組みと肥大化の起点

コンテナイメージはレイヤーの積み重ねで構成されます。ベースの上にランタイム・依存ライブラリ・アプリケーションコードと順に重ねていくうちに、最初は100MB台だったイメージが気づけば1GBを超えるという状況は現場でよく見られます。問題の起点は多くの場合、「動作確認中に追加したデバッグツール」や「一時的に入れたパッケージ」が本番イメージにそのまま紛れ込むことです。レイヤー構造の特性上、後のレイヤーでファイルを削除しても元のレイヤー自体はイメージに残ります。この仕組みを理解しないままシングルステージ構成で開発を続けると、コンパイラやビルドツール一式が本番イメージに残り続け、攻撃面を不必要に広げる結果になります。手を動かしながら構造を確認する場面はセミナー・実機環境で補うとして、まず設計段階でこの特性を前提として置くことが重要です。

更新サイクルが途切れると何が起きるか

ベースイメージはディストリビューション側が定期的にパッチを適用して更新しています。DockerfileのFROM行で特定タグを固定参照している場合、ビルドを再実行しない限り更新は取り込まれません。10環境以上を管理するプロジェクトでは、「最後にビルドした日付」がイメージごとにばらばらになり、半年前のベースを参照するイメージと最新ベースのイメージが同一クラスタ上に混在する状態が生まれます。脆弱性スキャンをCI/CDに組み込んだ際、古いベースのイメージほどCVEの検出数が多くなります。ある構築チームでは、スキャン導入後に既存イメージの70%以上に中〜高の深刻度の脆弱性が残っていることが判明し、対応工数が当初計画の数倍に膨らんだという事例があります。問題は脆弱性そのものよりも、更新サイクルが設計されていないことで発覚が遅れる点にあります。

「動いているから変えない」が積み上げるコスト

本番で安定稼働しているサービスのイメージは変えたくない、という力学は現場では自然に働きます。しかし更新を先送りにするほど差分が広がり、いざ対応しようとした際のテスト範囲が膨らみます。2年間イメージを固定したまま運用していたあるチームは、ベースをアップグレードした際に依存ライブラリとの非互換が20件以上発生し、修正に3週間以上を要したという話を聞きます。先送りは選択ではなく、対応コストの先払いを拒否しているにすぎません。更新コストが高くなる前に、設計段階で「どのサイクルでベースを再ビルドするか」を明示しておくことが、後々の技術的負債を防ぐ最も効果的な手段です。

最小イメージが効く用途と公式ベースが要る用途の判断

最小イメージが本領を発揮するケース

distrolessやscratchベースの最小イメージが適合するのは、ランタイムが静的にコンパイルされたバイナリだけで成立する場合です。GoやRustで書かれたマイクロサービスは、依存ライブラリをバイナリに内包できるため、シェルもパッケージマネージャも含まない最小イメージで動作します。この構成ではイメージサイズが10〜30MB程度に収まり、攻撃面をほぼゼロに近づけられます。セキュリティ要件が厳格な金融系・医療系の本番基盤では、シェルが存在しないだけでコンテナへの侵入後の横展開を困難にできます。コンテナブレイクアウトを前提とした多層防御の文脈では、最小イメージへの移行はセキュリティアーキテクチャの一部として位置づけられます。

公式ベースが必要になるケース

Pythonや動的言語のランタイムは共有ライブラリへの依存が複雑で、最小イメージでの動作には追加設定が必要になることがあります。エンタープライズ向けのミドルウェアやレガシーアプリケーションは、特定のglibcバージョンやシステムライブラリを前提としているケースが多く、ディストリ公式イメージのほうが互換性の維持が容易です。サポート契約の観点でも、Red Hat UBIベースのイメージはRed Hatのサポート範囲に含まれるため、商用環境でのインシデント対応に有効に機能します。CVE対応のSLAが明示されている点は、コンプライアンス審査でも重要な選定根拠になります。

判断の境界となる問いかけ

どちらを選ぶか迷ったとき、整理すべき問いは2点です。「このコンテナに侵入された場合に何ができると困るか」というセキュリティリスクの観点と、「このランタイムは静的リンクで成立するか」という技術的互換性の観点です。前者が厳格であるほど最小イメージの方向へ引き寄せられ、後者が複雑であるほど公式ベースの安全性が増します。迷う場合は、まず公式ベースから始め、セキュリティ審査の指摘や運用の実態に合わせて段階的に削っていくアプローチが現実的です。最初から最小化を目指して調査・デバッグ環境まで消してしまうと、後から取り戻すコストが上がります。

ディストリ公式・軽量・最小イメージの特性比較

比較の観点と見方

ベースイメージの選定では、イメージサイズだけでなく、互換性・攻撃面・CVE対応品質・デバッグのしやすさを多角的に評価する必要があります。以下の比較表は三つの主要カテゴリの特性を整理したものです。自プロジェクトの要件と照らし合わせる判断材料として活用してください。

観点 ディストリ公式(Ubuntu / Debian / UBI) 軽量(Alpine Linux) 最小(distroless / scratch)
イメージサイズ 100〜800MB程度 5〜50MB程度 5〜30MB程度
シェル あり(bash / sh) あり(ash) なし
パッケージマネージャ apt / yum / dnf apk なし
glibcの互換性 高い 低い(musl-libc使用) ランタイム依存
攻撃面の広さ 広い 中程度 最小
CVE対応品質 ディストリ公式パッチ・SLA明示あり Alpine maintainer対応(遅れることがある) アプリ依存ライブラリのみ
デバッグしやすさ 高い 中程度 低い(外部手段が必要)
主な適用場面 互換性重視・商用サポート要件あり 軽量化と互換性のバランス重視 静的バイナリ・高セキュリティ要件

Alpineの位置づけと注意点

Alpineはサイズと互換性のバランスを取った選択肢として広く使われています。glibcではなくmusl-libcを採用しているため、glibcを前提としたバイナリやライブラリとの互換性に問題が出ることがあります。Pythonの一部ライブラリや特定のネイティブ拡張ではコンパイルエラーや動作不良が発生するケースが報告されており、採用前に動作検証が必要です。また、Alpine用のパッケージはDebianやRHEL系に比べてメンテナンスリソースが少なく、CVE対応が遅れる場合があります。軽量化の恩恵と引き換えに、互換性と更新品質のトレードオフが生じる点を認識した上で選定することが求められます。

distrolessの実態と選択肢

distrolessはパッケージマネージャもシェルも含まず、アプリケーションの実行に必要な最低限のランタイムのみを収録したイメージ群です。Java・Python・Node.js・Go向けのバリアントが存在し、それぞれのランタイムが含まれています。scratchと異なりCA証明書やタイムゾーンデータが含まれているものが多いため、追加設定なく動作するケースが多い点が利点です。最小イメージの入門として、まずdistrolessを試し、要件に合わせてscratchベースへ移行するステップを踏むと、移行コストを抑えながら段階的に攻撃面を絞れます。

共通ベースを社内で管理するか各チームに委ねるかの決め方

チーム任せが招く分散の問題

各チームが独自にベースイメージを選定する構成は、短期的には柔軟ですが、組織が拡大するにつれて問題が表面化します。5チーム以上が並行開発する環境では、ベースイメージの種類が10を超え、脆弱性スキャンの対象・更新サイクル・CVE対応のタイミングがすべてばらばらになります。あるチームが2年前のAlpineを使い続け、別のチームはUbuntu最新を毎月ビルドしている、という状態ではセキュリティポリシーの一貫した適用が困難です。規制対応やセキュリティ監査が入ったとき、各イメージの更新履歴やCVE対応状況を証拠として揃えるコストが跳ね上がります。

共通ベース(ゴールデンイメージ)管理に必要な体制

社内で共通ベースを維持するには、管理体制を最低限整えることが出発点です。具体的には、更新頻度の設計(月次定期ビルドとCVSS 7.0以上のCVE検出時の緊急ビルド)、ダウンストリームチームへの通知フロー、社内レジストリへのプッシュと古いタグの退役ルール、の3点が整っていることが条件です。担当を「誰でも」にするとドライバーが不在になるため、特定のチームまたは担当者に責任を集約するのが現実的です。共通ベースの管理コストは思いのほか高くなく、仕組みが整えば月に数時間程度の工数で維持できます。

段階的な標準化の進め方

すでに各チームがバラバラにイメージを持っている状態からの標準化は、全チームの一斉移行よりも段階的なアプローチが現実的です。新規プロジェクトから共通ベースを適用し、既存は次のメジャーバージョンアップや大きなリファクタリングのタイミングで移行するという方針を明示するだけで、時間をかけて収束していきます。移行済みか否かをレジストリのタグ命名規則や管理台帳で可視化することで、進捗管理とチームへのフィードバックが容易になります。強制よりも、移行した際のメリット(CVE対応の自動化・ビルド工数の削減)を示すほうが、現場の協力を得やすい傾向があります。

ベース更新を下流のイメージへどう波及させるか

更新の起点となる検知設計

ベースイメージの更新は、タグの変化で検知する方法とダイジェスト(SHA256)の変化で検知する方法の2つがあります。「latest」タグを参照する構成は意図しないタイミングで変化が取り込まれるリスクがあるため、メジャーバージョンを固定しつつパッチを自動取得できるタグ命名規則を設計することが重要です。ダイジェストによる固定は再現性を高めますが、更新を明示的にトリガーする手間が生じます。どちらを選ぶかは再現性とセキュリティ応答速度のトレードオフであり、本番への適用頻度や承認フローに合わせて使い分けます。

自動ビルドパイプラインの設計

ベースイメージが更新されたとき、それを参照するダウンストリームのイメージを自動でビルド・テスト・プッシュするパイプラインを設計しておくことで、更新伝播を手動オペレーションに依存しない構成が実現します。共通ベースを持つ場合、ベースのビルド成功をトリガーにダウンストリームのビルドをカスケードで起動する設計が有効です。イメージ数が20を超える環境では、全ビルドを直列で走らせると完了まで数時間かかることがあるため、並列ビルドの上限をCI基盤の容量に合わせて設計しておく必要があります。ビルドが滞ったときに通知を受けられる仕組みも合わせて組み込むと、障害の発見が遅れません。

段階的な検証とロールアウト

更新後のイメージは、ステージング環境での動作確認を経て本番に適用する手順を標準化することが重要です。特にベースイメージの更新はランタイムやシステムライブラリのバージョンも変わるため、アプリケーションの動作に予期しない影響が出ることがあります。カナリアデプロイやブルーグリーン切り替えと組み合わせることで、問題を早期に検出しつつ影響範囲を限定できます。更新ごとに差分を記録したChangelog(含有パッケージのバージョン変更・CVE対応内容)をレジストリのタグに紐づけておくと、問題発生時の原因追跡が容易になります。

削りすぎて調査も対処もできなくなる境界

最小化の行き過ぎが招く問題

distrolessやscratchベースに移行した後、本番環境でアプリケーションが予期せぬ動作をした場合、シェルも診断ツールも存在しないコンテナに対してどう調査するかという問題が生じます。ログが正常に出力されていれば外から確認できますが、プロセス状態のチェックやネットワーク疎通確認のような操作は、コンテナ内にツールがなければ実行できません。「本番に入れない・調べられない」という状況は、障害対応のMTTR(平均復旧時間)を大幅に引き上げ、SLAへの影響を広げます。最小化によるセキュリティ向上と、障害時の調査能力の喪失は表裏の関係にあり、どちらを優先するかを事前に決めておく必要があります。

本番と開発・デバッグで構成を分ける設計

マルチステージビルドを活用することで、本番イメージは最小化しつつ、開発・デバッグ用ターゲットではツールを含む構成を持てます。同一のDockerfileから、本番向けのdistrolessターゲットと診断ツールを含むdebugターゲットを生成する設計は、現場での採用例として挙がります。本番に最小イメージを使いながら、問題発生時は診断用イメージをエフェメラルコンテナとしてPodに差し込む運用パターンも、Kubernetes環境では有効な選択肢です。デバッグ手段の代替を事前に設計しておくことで、最小化のメリットを損なわずに運用できます。

削ってよいものと残すべきものの判断

削除の判断基準は「攻撃者に利用されるリスクがあるか」と「運用上の必要性があるか」の2軸です。シェルやパッケージマネージャは攻撃面として削る価値が高い一方、タイムゾーンデータやCA証明書バンドルは多くのアプリケーションで暗黙的に依存しており、削ると予期しない動作を引き起こします。distrolessイメージにはCA証明書やタイムゾーンデータが含まれているものもあるため、含有物を確認した上で選定することが重要です。「何が入っているか」ではなく「何が入っていないか」を意識する姿勢が、最小イメージ運用の基本です。

ベースイメージ設計チェックリスト

選定前・設計フェーズ

  • アプリケーションのランタイム特性を確認した(静的バイナリか、動的言語依存か)
  • セキュリティ要件を確認した(シェルの有無・攻撃面の許容範囲・サポートSLA要件)
  • glibcへの依存を確認した(Alpine採用時にmusl-libc非互換のリスクを事前検証済みか)
  • 商用サポートの必要性を確認した(UBI / RHELの採用要否)
  • 最小イメージを選ぶ場合、デバッグ手段の代替を設計した(エフェメラルコンテナ / debugビルドターゲット)
  • 共通ベースの管理体制を確認した(ゴールデンイメージの有無と管理担当者の所在)

ビルド・運用フェーズ

  • FROM行でlatestを使わず、バージョン固定またはダイジェスト固定を選択した
  • マルチステージビルドを採用し、ビルド依存物が本番イメージに残らない構成にした
  • ベース更新のサイクルを定義した(月次定期ビルド・CVE緊急ビルドのいずれか以上)
  • 脆弱性スキャンをCI/CDパイプラインに組み込んだ
  • 共通ベース更新からダウンストリームへの自動ビルド伝播を設計した
  • ステージング環境での動作検証を更新フローに組み込んだ
  • イメージの含有物(CA証明書・タイムゾーンデータ等)を確認した
  • 古いタグの退役・クリーンアップのルールを定義した

判断フロー:どのベースを選ぶか

  1. ランタイムが静的バイナリとして完結するか → Yes → distroless / scratch を検討する
  2. glibcへの依存があるか → Yes → Alpineを除外し、ディストリ公式へ絞り込む
  3. 商用サポートのSLAが必要か → Yes → UBI / RHELベースを選定する
  4. 攻撃面の最小化が最優先か → Yes → distrolessを本番に適用し、debugターゲットを別途設計する
  5. 上記のいずれも該当しない場合 → Debian slim / Ubuntu minimal から始め、要件に応じて段階的に絞る

▶ 手を動かして本番まで到達する

設計の型は掴めても、本番でTerraformを通しきる経験は別物です

ここまでの設計判断は、読めば腹に落ちます。ですが「自分の手で本番まで通しきる自信」は、それとは別の話になります。

2日間の集中ハンズオンで、AWS上にIaCで構築しきる経験を持ち帰れます。設計判断を、動くコードと本番環境の手応えに変える場です。

ベースイメージ選定とビルドをIaCで型化するTerraformハンズオンへ →

Terraformマスターセミナー|2日間ハンズオンでAWS上にIaCで本番構築