コンテナ構築のベスト プラクティス

Last reviewed 2023-02-28 UTC

この記事では、コンテナを構築するための一連のおすすめの方法について説明します。こうした方法は、コンテナの構築(Cloud Build などを使用)とコンテナの実行(Google Kubernetes Engine(GKE)を使用)をより簡単にすることを目的としています。これにより、構築時間の短縮から、より小規模で復元性の高いイメージの作成まで、さまざまな目標を達成できます。

このようなおすすめの方法の重要度は同じではありません。たとえば、本番環境ワークロードを正常に実行するために、必要のないおすすめの方法もあれば、不可欠のおすすめの方法もあります。特に、セキュリティに関連する方法の重要度は主観的なものです。実装するかどうかは、個々の環境と制約によって異なります。

この記事を最大限に活用するには、Docker と Kubernetes に関する知識が必要です。ここで説明されている一部の方法は Windows コンテナにも適用されますが、大部分の方法では、Linux コンテナで作業していることが前提となります。コンテナの実行や操作に関するアドバイスについては、コンテナ運用のおすすめの方法をご覧ください。

コンテナごとに単一のアプリをパッケージ化する

重要度: 高

コンテナを初めて使用するときのよくある誤りとして、多数の異なる処理を同時に実行できる仮想マシン(VM)のようにコンテナを扱うことがあります。コンテナをそのように機能させることはできますが、それではコンテナモデルのメリットの大部分を活用できなくなります。たとえば、従来の Apache / MySQL / PHP スタックの場合を考えてみましょう。1 つのコンテナですべてのコンポーネントを実行したいかもしれません。しかし、この場合のベスト プラクティスは、2 つまたは 3 つの異なるコンテナを使用することです。つまり、1 つは Apache 用、もう 1 つは MySQL 用、そして PHP-FPM を実行している場合はさらに PHP 用のコンテナを用意します。

コンテナは、ホストするアプリと同じライフサイクルを持つように設計されているため、各コンテナに含めるアプリは 1 つのみにする必要があります。コンテナが起動するとアプリが起動し、アプリが停止するとコンテナも停止します。次の図は、このベスト プラクティスを示しています。

カスタム イメージなしの起動プロセスを示す図。

図 1. 左側のコンテナはベスト プラクティスに従い、右側のコンテナは従っていない。

1 つのコンテナ内に複数のアプリがある場合、ライフサイクルが異なっていたり、状態が異なっていたりする可能性があります。たとえば、コンテナが動作していても、そのコア コンポーネントの 1 つがクラッシュしていたり、応答しなかったりする場合があります。追加のヘルスチェックを行わなければ、全体的なコンテナ管理システム(Docker または Kubernetes)によって、コンテナが正常に機能しているかどうかを判別することはできません。Kubernetes の場合で、コア コンポーネントが応答しなくなると、Kubernetes はコンテナの再起動を自動的には行いません。

公開イメージで次のアクションが示される場合がありますが、これらの例には従わないでください。

  • スーパーバイザーなどのプロセス管理システムを使用して、1 つまたは複数のアプリをコンテナで管理する。
  • コンテナでエントリポイントとして bash スクリプトを使用し、それによって、複数のアプリをバックグラウンド ジョブとして生成する。コンテナでの bash スクリプトの適切な使用法については、PID 1、シグナル処理、ゾンビプロセスの適切な処理をご覧ください。

PID 1、シグナル処理、ゾンビプロセスの適切な処理

重要度: 高

コンテナ内のプロセスのライフサイクルを制御するには、主に Linux のシグナルを使用します。これまでのおすすめの方法に従い、アプリのライフサイクルと、それが含まれているコンテナを緊密にリンクさせるために、アプリで Linux のシグナルが適切に処理されていることを確認します。最も重要な Linux のシグナルは、プロセスを終了する SIGTERM です。また、アプリが SIGKILL シグナル(プロセスを異常終了するために使用される)や SIGINT シグナル(Ctrl+C を入力した場合に送信され、通常、SIGTERM のように処理される)を受け取る場合もあります。

プロセス識別子(PID)は、Linux カーネルによって各プロセスに割り当てられる一意の識別子です。PID には名前空間が指定されています。つまり、コンテナには、ホストシステムの PID にマップされる独自の PID のセットが含まれています。Linux カーネルを起動したときに最初に開始されるプロセスに PID 1 が設定されます。通常のオペレーティング システムの場合、このプロセスは init システム(systemd や SysV など)です。同様に、コンテナで最初に開始されるプロセスに PID 1 が設定されます。Docker や Kubernetes はシグナルを使用してコンテナ内のプロセスと通信しますが、特に重要な処理はプロセスの終了です。Docker と Kubernetes はどちらも、コンテナ内で PID 1 を持つプロセスにしかシグナルを送信できません。

コンテナの観点から、PID と Linux シグナルにより、考慮すべき 2 つの問題が生じます。

問題 1: Linux カーネルはシグナルをどのように処理するか

Linux カーネルは PID 1 を持つプロセスに対して、他のプロセスの場合とは異なる方法でシグナルを処理します。シグナル ハンドラはこのプロセスに自動的には登録されません。つまり、SIGTERM や SIGINT などのシグナルはデフォルトでは無効です。また、デフォルトでは SIGKILL を使用してプロセスを強制終了する必要があるため、正常なシャットダウンを行えません。アプリによっては、SIGKILL を使用するとエラーが生じたり、(データストアに対する)書き込みの中断や、モニタリング システムでの不要なアラートが生じたりする可能性があります。

問題 2: 従来の init システムは孤立したプロセスをどのように処理するか

systemd などの従来の init システムは、孤立したゾンビプロセスの削除(除去)にも使用されます。孤立したプロセス(親を失ったプロセス)は PID 1 を持つプロセスに再接続され、孤立したプロセスが終了すると、それらを除去します。通常の init システムではそのように動作します。しかしコンテナ内では、この除去処理は PID 1 を持つプロセスが担当することになります。そのプロセスによって除去が適切に処理されない場合、メモリなどのリソースが不足する危険性があります。

このような問題には、いくつかの一般的な解決策があります。次のセクションでそれについて概説します。

解決策 1: PID 1 として実行し、シグナル ハンドラを登録する

この解決策は、最初の問題のみに対処できます。アプリによって子プロセスが制御された方法で生成される場合(このような状況はよくあります)、この解決策は有効であり、2 番目の問題を回避できます。

この解決策を実装する最も簡単な方法は、Dockerfile で CMDENTRYPOINT 命令を使用してプロセスを開始することです。たとえば、次の Dockerfile では、nginx は最初に開始される唯一のプロセスです。

FROM debian:11

RUN apt-get update && \
    apt-get install -y nginx

EXPOSE 80

CMD [ "nginx", "-g", "daemon off;" ]

場合によっては、プロセスが適切に実行されるように、コンテナ内に環境を準備する必要があります。この場合、起動時にコンテナによってシェル スクリプトが開始されるようにすることをおすすめします。このシェル スクリプトにより、環境が準備され、メインプロセスが開始されます。ただし、この手法を使用すると、プロセスではなく、シェル スクリプトに PID 1 が設定されます。そのため、シェル スクリプトからプロセスを開始するには、内部コマンド exec を使用する必要があります。exec コマンドによって、必要なプログラムにスクリプトが置き換えられます。そのプロセスは PID 1 を継承します。

解決策 2: Kubernetes でプロセス Namespace の共有を有効にする

Pod でプロセス Namespace の共有を有効にすると、Kubernetes はその Pod 内のすべてのコンテナに単一のプロセス Namespace を使用します。Kubernetes Pod インフラストラクチャ コンテナが PID 1 になり、孤立したプロセスを自動的に除去します。

解決策 3: 専用の init システムを使用する

古い Linux 環境のように、init システムを使用してこの問題に対処することもできます。ただし、systemd や SysV のような通常の init システムは単にこの問題に対処するには複雑で、規模が大きすぎるため、コンテナ専用に作成されている tini のような init システムを使用することをおすすめします。

専用の init システムを使用する場合、その init プロセスに PID 1 が設定され、以下の操作が行われます。

  • 正しいシグナル ハンドラを登録する。
  • アプリに対してシグナルが機能することを確認する。
  • 結果として生じたゾンビプロセスを除去する。

この解決策は、docker run コマンドの --init オプションを使用して、Docker 自体で実施できます。この解決策を Kubernetes で実施するには、init システムをコンテナ イメージにインストールし、コンテナのエントリポイントとしてそれを使用する必要があります。

Docker のビルド キャッシュの改善

重要度: 高

Docker のビルド キャッシュは、コンテナ イメージの構築を高速化できます。イメージはレイヤごとに作成され、Dockerfile では、各命令によって得られたイメージにレイヤが作成されます。可能であれば、Docker はビルド中に前のビルドのレイヤを再利用し、コストのかかるステップをスキップします。Docker がそのビルド キャッシュを使用できるのは、以前のすべてのビルドステップでそれが使用されていた場合のみです。通常、この動作はビルドを高速化するという点では好ましいことですが、いくつかの状況を考慮する必要があります。

たとえば、Docker ビルド キャッシュのメリットを十分に活用するには、頻繁に変更されるビルドステップを Dockerfile の末尾に置く必要があります。そのようなステップを先頭に置くと、Docker は、あまり頻繁に変更されない他のビルドステップに、そのビルド キャッシュを使用できなくなります。通常、新しい Docker イメージはソースコードの新しいバージョンごとに作成されるため、Dockerfile の可能な限り後のほうでソースコードをイメージに追加します。次の図では、STEP 1 を変更すると、Docker が再利用できるのは FROM debian:11 ステップからのレイヤのみであることがわかります。ただし、STEP 3 を変更すれば、Docker が STEP 1STEP 2 のレイヤを再利用できます。

Docker ビルド キャッシュの使用方法の例

図 2. Docker ビルド キャッシュの使用方法の例。緑色は再作成できるレイヤ。赤は再作成する必要のあるレイヤ。

レイヤを再利用する場合は注意点がもう 1 つあります。ビルドステップがローカル ファイル システムに格納されているなんらかの種類のキャッシュに依存している場合、そのキャッシュは同じビルドステップで生成される必要があります。このキャッシュが生成されない場合、以前のビルドで取得された古いキャッシュを使用してビルドステップが実行される可能性があります。この動作は、apt や yum などのパッケージ マネージャーで非常に頻繁に見られます。パッケージのインストールと同じ RUN コマンドでリポジトリを更新する必要があります。

次の Dockerfile ファイルで 2 番目の RUN ステップを変更すると、apt-get update コマンドは再実行されず、apt キャッシュが古いままになります。

FROM debian:11

RUN apt-get update
RUN apt-get install -y nginx

代わりに、2 つのコマンドを 1 つの RUN ステップにマージします。

FROM debian:11

RUN apt-get update && \
    apt-get install -y nginx

不要なツールの削除

重要度: 中

攻撃者からアプリを保護するために、不要なツールを削除して、アプリの攻撃対象を減らすようにしてください。たとえば、システム内にリバースシェルを作成するために使用できる netcat のようなユーティリティを削除します。コンテナに netcat が存在しなければ、攻撃者は別の方法を探す必要があります。

このベスト プラクティスは、コンテナ化されていない場合でも、あらゆるワークロードに適用できます。違いは、このベスト プラクティスでは、従来の VM やベアメタル サーバーではなく、コンテナ用に最適化されていることです。

不要なツールをなくせば、デバッグ プロセスも改善できます。たとえば、このベスト プラクティスを十分に推し進めれば、徹底的なログ、トレース システムおよびプロファイリング システムがほぼ必須になります。実際には、ローカル デバッグツールは特権が高い場合が多く、使用することはできません。

ファイル システムのコンテンツ

このおすすめの方法の最初の部分では、コンテナ イメージのコンテンツを扱います。イメージに含める内容は、可能な限り少なくします。静的にリンクされた単一のバイナリにアプリをコンパイルできる場合、このバイナリをスクラッチ イメージに追加すれば、アプリのみが含まれる最終イメージを作成できます。イメージにパッケージ化されるツールの数を減らすことで、潜在的な攻撃者がコンテナ内で行えることを削減できます。詳細については、可能な限り小さいイメージを作成するをご覧ください。

ファイル システムのセキュリティ

イメージにツールを入れないだけでは不十分です。潜在的な攻撃者が独自のツールをインストールできないようにする必要もあります。次の 2 つの方法を組み合わせることができます。

  • コンテナ内で root として実行しないようにする: この手法は、セキュリティの最初のレイヤになります。具体的には、root が所有するファイルを、イメージに組み込まれたパッケージ マネージャー(apt-getapk など)で変更できないように設定します。この手法を利用するには、sudo コマンドを無効にするか、アンインストールする必要があります。この件については、root として実行しないでより広範囲にわたって説明します。

  • 読み取り専用モードでコンテナを起動する: これには、docker run コマンドから --read-only フラグを使用するか、Kubernetes で readOnlyRootFilesystem オプションを使用します。

可能な限り小さいイメージを作成する

重要度: 中

より小さいイメージを作成することには、アップロードやダウンロードに要する時間が短縮されるというメリットがあります。これは、Kubernetes の Pod のコールド スタートでは特に重要な要素となります。イメージが小さいほど、ノードはより短い時間でダウンロードできるようになります。しかし、最終的なイメージにビルド依存関係や改善されていないレイヤを誤って含めてしまうことがあるため、小さいイメージの作成は困難な場合があります。

可能な限り小さいベースイメージを使用する

ベースイメージは、Dockerfile の FROM 命令で参照されるものです。Dockerfile 内のその他のすべての命令は、このイメージの上に構築されます。ベースイメージが小さいほど、得られるイメージは小さくなり、ダウンロードをより短時間で行えるようになります。たとえば、alpine:3.17 イメージは、ubuntu:22.04 イメージよりも 23 MB 小さくなります。

スクラッチ ベースイメージを使用することもできます。これは空のイメージであり、その上に独自のランタイム環境を構築できます。アプリが静的にリンクされたバイナリになっている場合、スクラッチ ベースイメージを次のように使用できます。

FROM scratch
COPY mybinary /mybinary
CMD [ "/mybinary" ]

次の Kubernetes のベスト プラクティスの動画では、セキュリティの脆弱性にさらされることなく、小さなコンテナを構築するための追加戦略について説明しています。

イメージ内の不要なものを削減する

イメージのサイズを削減するために、イメージ内で本当に必要とされるもののみをインストールするようにします。パッケージを余分にインストールしておき、後のステップでそれを削除するほうが好都合に思えることもあります。しかし、その手法では不十分です。Dockerfile の各命令によってレイヤが作成されるため、イメージを作成したステップではなく、後のステップでイメージからデータを削除しても、全体的なイメージのサイズは削減されません(データはより深いレイヤに隠れているだけであり、そこにまだ存在します)。次の例を考えてみましょう。

好ましくない Dockerfile 好ましい Dockerfile
FROM debian:11
RUN apt-get update && \ apt-get install -y \ [buildpackage] RUN [build my app] RUN apt-get autoremove --purge \ -y [buildpackage] && \ apt-get -y clean && \ rm -rf /var/lib/apt/lists/*
FROM debian:11
RUN apt-get update && \ apt-get install -y \ [buildpackage] && \ [build my app] && \ apt-get autoremove --purge \ -y [buildpackage] && \ apt-get -y clean && \ rm -rf /var/lib/apt/lists/*

好ましくない Dockerfile の場合、[buildpackage] と、/var/lib/apt/lists/* 内のファイルは、最初の RUN に対応するレイヤ内に依然として存在します。結果として得られるイメージで、レイヤに含まれているデータにアクセスできない場合でも、そのレイヤはイメージの一部であるため、残りのレイヤとともにアップロードおよびダウンロードされる必要があります。

得られたイメージには、[buildpackage]/var/lib/apt/lists/* 内のファイルはどこにも存在せず、より深いレイヤに隠れていることもありません。

イメージレイヤの詳細については、Docker のビルド キャッシュの改善をご覧ください。

イメージ内を整理するもう 1 つの良い方法は、マルチステージ ビルド(Docker 17.05 で導入)を使用することです。マルチステージ ビルドを使用すれば、同じ Dockerfile を使用しながら、最初の「ビルド」コンテナにアプリをビルドし、結果を別のコンテナで使用できます。

Docker マルチステージ ビルドプロセス

図 3. Docker のマルチステージ ビルドプロセス

次の Dockerfile では、hello バイナリが最初のコンテナにビルドされ、2 番目のコンテナに取り込まれます。2 番目のコンテナはスクラッチに基づいているため、結果のイメージには hello バイナリのみが含まれ、ビルド中に必要なソースファイルやオブジェクト ファイルは含まれません。スクラッチ イメージに外部バイナリがなくても機能できるようにするには、バイナリが静的にリンクされている必要があります。

FROM golang:1.20 as builder

WORKDIR /tmp/go
COPY hello.go ./
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags '-s' -o hello

FROM scratch
CMD [ "/hello" ]
COPY --from=builder /tmp/go/hello /hello

共通レイヤでイメージを作成してみる

Docker イメージをダウンロードする必要がある場合、Docker はまず、イメージ内に存在するレイヤの一部がユーザー側にすでに存在するかどうかをチェックします。そのようなレイヤがすでに存在する場合、そのイメージはダウンロードされません。現在ダウンロードしようとしているイメージと同じベースを持つ別のイメージを以前にダウンロードした場合に、このようになります。この結果、2 番目のイメージについては、ダウンロードするデータ量は大幅に少なくなります。

組織レベルでは、開発者に共通の標準ベースイメージを提供することで、このようにダウンロード量を削減できるメリットを得られます。システムは各ベースイメージを 1 回ダウンロードするだけで済みます。最初のダウンロードの後は、各イメージにそれぞれ固有のレイヤのみを追加する必要があります。つまり、イメージに共通の部分が多いほど、より短時間でダウンロードできます。

共通レイヤでイメージを作成してみる

図 4. 共通レイヤでイメージを作成する。

脆弱性がないかイメージをスキャンする

重要度: 中

ソフトウェアの脆弱性は、ベアメタル サーバーと仮想マシンの世界でよく知られている問題です。このような脆弱性に対処する一般的な方法は、各サーバーにインストールされているパッケージを一覧表示する集中インベントリ システムを使用することです。上流オペレーティング システムの脆弱性フィードを登録して、脆弱性がサーバーに影響を与えたときに通知し、それに応じてパッチが適用されるようにします。

ただし、コンテナは不変と想定されるため(詳細については、コンテナのステートレス性と不変性を参照)、脆弱性に対処するためにパッチを適用しないでください。ベスト プラクティスは、イメージを再作成してパッチを組み込み、それを再デプロイすることです。コンテナはサーバーよりもライフサイクルが非常に短く、明確に定義された ID も少数です。このため、コンテナ内の脆弱性を検出するために同様の集中インベントリ システムを使用するのは、あまり良い方法とは言えません。

この問題に対処するために、Artifact Analysis では、公開モニタリング対象パッケージのイメージをスキャンしてセキュリティの脆弱性を検出できます。次のオプションが用意されています。

自動脆弱性スキャン

この機能を有効にすると、コンテナ イメージのパッケージに関する脆弱性が識別されます。イメージが Artifact Registry または Container Registry にアップロードされると、イメージがスキャンされ、イメージを push してから最大 30 日間、新しい脆弱性を検出するためにデータが継続的にモニタリングされます。次のようないくつかの方法で、この機能によって報告された情報に対処できます。

  • 脆弱性を一覧表示し、修正プログラムがある場合は脆弱性を修正するプロセスを開始する、cron のようなジョブを作成します。
  • 脆弱性が検出されるとすぐに、Pub/Sub 統合を使用して、組織が使用するパッチ適用プロセスを開始します。
On-Demand Scanning API

有効にすると、ローカル イメージ、または Artifact Registry もしくは Container Registry に保存されているイメージを手動でスキャンできます。この機能は、ビルド パイプラインの早い段階で脆弱性を検出して対処するのに役立ちます。たとえば、ビルド後にイメージをスキャンするために Cloud Build を使用し、スキャンによって特定の重大度の脆弱性が検出された場合、Artifact Registry へのアップロードをブロックできます。自動脆弱性スキャンも有効にしている場合は、レジストリにアップロードしたイメージも Artifact Registry によってスキャンされます。

パッチ適用プロセスを自動化し、イメージを作成するために最初に使用された既存の継続的インテグレーション パイプラインを利用することをおすすめします。継続的なデプロイメント パイプラインを信頼できる場合、準備が整った時点で修正されたイメージが自動的にデプロイされるようにすることもできます。ただし、大部分のユーザーはデプロイ前に手動で検証することを希望します。次の手順で行えます。

  1. Artifact Registry にイメージを保存し、脆弱性スキャンを有効にします。
  2. Artifact Registry から新たな脆弱性を定期的に取得し、必要に応じてイメージの再作成を開始するジョブを構成します。
  3. 新しいイメージが作成されたら、継続的デプロイメント システムによってステージング環境にそれらをデプロイします。
  4. ステージング環境に問題がないか、手動で確認します。
  5. 問題が見つからなければ、本番環境へのデプロイを手動で開始します。

イメージに適切なタグを付ける

重要度: 中

通常、Docker イメージは名前とタグという 2 つのコンポーネントによって識別されます。たとえば、google/cloud-sdk:419.0.0 イメージの場合、google/cloud-sdk が名前、419.0.0 がタグです。Docker コマンドで指定しなかった場合は、デフォルトで latest というタグが使用されます。名前とタグのペアは、常に一意なものになります。ただし、必要に応じて別のイメージにタグを再割り当てできます。

イメージを作成するときは、自分の判断でイメージにタグを正しく付けてください。一貫性があり、矛盾しないタグ付けポリシーに従ってください。イメージのユーザーが簡単に理解できるよう、タグ付けポリシーをドキュメントにしてください。

コンテナ イメージを使用すると、ソフトウェアの部分をパッケージ化してリリースできます。イメージにタグを付けると、ユーザーはソフトウェアの特定のバージョンを見つけてダウンロードできるようになります。このため、コンテナ イメージのタグ付けシステムを、ソフトウェアのリリース ポリシーに確実にリンクしてください。

セマンティック バージョニングを使用したタグ付け

ソフトウェアをリリースする一般的な方法として、ソースコードの特定のバージョンにバージョン番号を(git tag コマンドなどで)「タグ付け」できます。セマンティック バージョニング仕様により、バージョン番号を簡単に処理できます。このシステムでは、ソフトウェアに次のような 3 つの部分で構成されるバージョン番号 X.Y.Z が付けられます。

  • X はメジャー バージョンで、互換性のない API 変更があった場合にのみ増加します。
  • Y はマイナー バージョンで、新機能があった場合に増加します。
  • Z はパッチ バージョンで、バグ修正があった場合に増加します。

マイナー バージョン番号またはパッチ バージョン番号の増加は、下位互換性のある変更に対して行われる必要があります。

このシステム、または同様のシステムを使用する場合、次のポリシーに従ってイメージにタグを付けます。

  • latest タグは、常に最新の(できれば安定した)イメージを参照します。このタグは、新しいイメージが作成されるとすぐに移動されます。
  • X.Y.Z タグは、ソフトウェアの特定のバージョンを指します。これを別のイメージに移動しないでください。
  • X.Y タグは、ソフトウェアの X.Y マイナー ブランチの最新パッチリリースを指します。これは、新しいパッチ バージョンがリリースされると移動されます。
  • X タグは、X メジャー ブランチの最新マイナー リリースの最新パッチリリースを指します。これは、新しいパッチ バージョンまたは新しいマイナー バージョンがリリースされると移動されます。

このポリシーを使用すると、ユーザーは使用するソフトウェアのバージョンを柔軟に選択できるようになります。特定の X.Y.Z バージョンを選択し、イメージが決して変更されないようにしたり、あまり具体的には特定せずにタグを選択して、自動的に更新したりすることができます。

Git commit ハッシュを使用したタグ付け

高度な継続的デリバリー システムを使用し、ソフトウェアを頻繁にリリースする場合は、セマンティック バージョニング仕様で説明されているバージョン番号を使用しないはずです。このような場合、一般的には、Git commit SHA-1 ハッシュ(またはその短いバージョン)をバージョン番号として使用し、バージョン番号を処理します。設計上、Git commit ハッシュは不変で、ソフトウェアの特定のバージョンを参照します。

この commit ハッシュはソフトウェアのバージョン番号として使用できますが、ソフトウェアのその特定のバージョンで作成された Docker イメージのタグとして使用することもできます。このようにすることで、Docker イメージを追跡できるようになります。この場合、イメージタグは不変であるため、所定のコンテナ内で実行されているソフトウェアの特定のバージョンをすぐに把握できるからです。継続的デリバリー パイプラインで、デプロイメントに使用するバージョン番号の更新を自動化してください。

公開イメージの使用を慎重に検討する

重要度: 該当なし

Docker の大きな利点の 1 つは、あらゆる種類のソフトウェアに対して、非常に多数のイメージが公開されていることです。こうしたイメージを使用すると簡単に開始できます。しかし、組織のコンテナ戦略を作成しているときに、制約によっては公開されているイメージで対応できない場合があります。公開イメージでは対処できない可能性のある制約の例を以下に示します。

  • イメージの内部を正確に制御したい。
  • 外部リポジトリを使用したくない。
  • 本番環境で脆弱性を厳密に制御したい。
  • すべてのイメージの基本オペレーティング システムを同じにしたい。

このすべての制約に対応する方法は同じで、独自のイメージを作成する必要がありますが、残念ながらコストがかかります。独自のイメージを作成することは、イメージの数が限定されている場合は問題ありませんが、この数は急速に増加する傾向があります。そのようなシステムを大規模に管理する可能性があることを考慮し、以下の方法を検討してみてください。

  • まれにしか作成されないイメージに対しても、信頼できる方法でイメージを自動作成する。これを実現するには、Cloud Build でトリガーを作成する方法が適しています。
  • 標準化されたベースイメージを採用する。Google は、使用可能ないくつかのベースイメージを提供しています。
  • ベースイメージに対する更新を「子」イメージに自動的に伝搬する。
  • イメージで脆弱性に対処する。詳細については、脆弱性がないかイメージをスキャンするをご覧ください。
  • 組織内のさまざまなチームによって作成されたイメージに内部標準を適用する。

作成してデプロイするイメージに、次のツールでポリシーを適用できます。

  • container-diff はイメージのコンテンツを分析します。2 つのイメージを比較することもできます。
  • container-structure-test は、定義した一連のルールをイメージのコンテンツが遵守しているかどうかをテストできます。
  • Grafeas はアーティファクト メタデータ API の一種であり、イメージに関するメタデータを保存して、後で、そのイメージがポリシーに準拠しているかどうかをチェックできます。
  • Kubernetes にはアドミッション コントローラがあり、これを使用して、Kubernetes でワークロードをデプロイする前に、多数の前提条件をチェックできます。

ベースイメージとして Debian や Alpine などの公開イメージを使用し、そのイメージの上にすべてを構築するハイブリッド システムを採用することもできます。または、一部の重要度の低いイメージには公開イメージを使用し、それ以外の場合には独自のイメージを作成することもできます。こうした問題には正解も不正解もなく、自力で対処する必要があります。

ライセンスに関する注意事項

Docker イメージにサードパーティのライブラリやパッケージを組み込む前に、各ライセンスでその行為が許可されていることを確認してください。サードパーティ ライセンスで再配布が制限されている場合もあり、その制限は Docker イメージを公開レジストリに公開する場合に適用されます。

次のステップ

Google Cloud に関するリファレンス アーキテクチャ、図、ベスト プラクティスを確認する。Cloud アーキテクチャ センターをご覧ください。