はじめに
この実験では、docker buildx build コマンドを使用して Docker イメージをビルドおよび管理する実践的な経験を得ます。最初にデフォルト設定でシンプルなイメージをビルドし、Dockerfile を使用してイメージの命令を定義する方法を学びます。
基礎を超えて、ビルド引数(build arguments)の活用やマルチステージビルド内の特定のステージをターゲットにするといったより高度な機能を探求します。また、--cache-from と --cache-to を使用してビルドキャッシュを効果的に管理し、ビルド時間を最適化する方法も学びます。さらに、この実験ではマルチプラットフォームイメージのビルドとレジストリへのプッシュ、ビルドプロセス中にシークレットや SSH エージェントを安全に公開する方法を実践します。
デフォルト設定でシンプルなイメージを構築
このステップでは、Dockerfile を使用してシンプルな Docker イメージをビルドする方法を学びます。Dockerfile は、イメージを構築するためにコマンドラインで実行可能なすべての命令を含むテキストドキュメントです。Docker は Dockerfile から命令を読み取り、自動的にイメージをビルドできます。
まず、この実験の作業ディレクトリである ~/project ディレクトリに移動します。
cd ~/project
次に、シンプルな Dockerfile を作成しましょう。この Dockerfile は ubuntu ベースイメージを基にしたイメージを定義し、このイメージからコンテナを実行すると単純に "Hello, Docker!" と表示します。
nano エディタを使用して、~/project ディレクトリに Dockerfile という名前のファイルを作成します。
nano Dockerfile
Dockerfile に以下の内容を追加します:
FROM ubuntu:latest
CMD ["echo", "Hello, Docker!"]
このシンプルな Dockerfile を分解してみましょう:
FROM ubuntu:latest: この命令は新しいイメージのベースイメージを設定します。Docker Hub の公式 Ubuntu イメージの最新バージョンを使用しています。CMD ["echo", "Hello, Docker!"]: この命令は、このイメージからコンテナが起動されたときに実行されるコマンドを指定します。この場合、echoコマンドに引数 "Hello, Docker!" を渡して実行します。
Ctrl + S を押してファイルを保存し、Ctrl + X を押して nano エディタを終了します。
Dockerfile が準備できたので、docker build コマンドを使用してイメージをビルドできます。コマンド末尾の . は、Docker にカレントディレクトリ (~/project) で Dockerfile を探すように指示します。また、イメージに my-hello-image などの名前でタグを付けます。
docker build -t my-hello-image .
Docker がイメージをレイヤーごとにビルドしていることを示す出力が表示されます。システム上にまだ存在しない場合は最初に ubuntu:latest イメージをプルし、その後 CMD 命令を実行します。
ビルドが完了したら、docker images コマンドを使用して利用可能なイメージをリスト表示し、イメージが正常に作成されたことを確認できます。
docker images
出力に my-hello-image が表示されるはずです。
最後に、新しくビルドしたイメージからコンテナを実行し、CMD 命令の出力を確認しましょう。
docker run my-hello-image
ターミナルに "Hello, Docker!" と出力されるはずです。これにより、イメージが正しくビルドされ、CMD 命令が期待通りに動作していることが確認できます。
ビルド引数とターゲットステージの使用
このステップでは、Dockerfileでビルド引数 (ARG) とターゲットステージを使用して、より柔軟で効率的なビルドを作成する方法を学びます。ビルド引数を使用するとビルドプロセスに変数を渡すことができ、ターゲットステージを使用すると単一のDockerfile内で複数のビルドステージを定義し、特定のステージのみをビルドできます。
まず、~/projectディレクトリにいることを確認してください。
cd ~/project
既存のDockerfileを変更して、ビルド引数とシンプルなマルチステージビルドを含めるようにしましょう。挨拶メッセージの引数を定義し、第 2 ステージで第 1 ステージからファイルをコピーします。
nanoを使用してDockerfileを開きます:
nano Dockerfile
既存の内容を以下に置き換えます:
## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo $GREETING > /app/greeting.txt
## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
CMD ["cat", "/greeting.txt"]
変更点を理解しましょう:
ARG GREETING="Hello from build argument!":GREETINGという名前のビルド引数をデフォルト値で定義します。ビルド時にこのデフォルト値を上書きできます。FROM ubuntu:latest as builder: 最初のビルドステージを開始し、builderと名前を付けます。マルチステージビルドで便利です。RUN echo $GREETING > /app/greeting.txt:builderステージで、/appディレクトリにgreeting.txtファイルを作成し、GREETING引数の値を書き込みます。FROM ubuntu:latest: 第 2 のビルドステージを開始します。デフォルトではこれが最終イメージになります。COPY --from=builder /app/greeting.txt /greeting.txt:builderステージからgreeting.txtファイルを現在のステージのルートディレクトリ (/) にコピーします。これがマルチステージビルドで成果物を転送する方法です。CMD ["cat", "/greeting.txt"]: 最終イメージからコンテナが実行されたときに実行されるコマンドを設定します。/greeting.txtファイルの内容を表示します。
Dockerfileを保存してnanoを終了します。
docker buildコマンドを使用してイメージをビルドしましょう。--build-argフラグを使用してGREETING引数にカスタム値を渡します。このイメージにmy-arg-imageというタグを付けます。
docker build --build-arg GREETING="Greetings from the command line!" -t my-arg-image .
出力を確認してください。提供されたビルド引数を使用してビルドプロセスが実行されるはずです。
ビルドが完了したら、イメージをリスト表示してmy-arg-imageが存在することを確認します。
docker images
my-arg-imageからコンテナを実行して出力を確認します。
docker run my-arg-image
"Greetings from the command line!"と表示され、ビルド引数が使用されたことが確認できます。
次に、builderステージのみをビルドしましょう。これは最終アプリケーションなしでビルド成果物を含む中間イメージを作成するのに便利です。--targetフラグを使用してステージ名を指定します。
docker build --target builder -t my-builder-stage .
再度イメージをリスト表示します。my-arg-imageに加えてmy-builder-stageも表示されるはずです。
docker images
my-builder-stageからコンテナを実行しても、my-arg-imageと同じ出力は得られません。最終ステージのCMD命令がbuilderステージに含まれていないためです (デフォルトコマンドがないため、すぐに開始して終了する可能性が高いです)。
docker run my-builder-stage
これにより、ターゲットステージを使用してDockerfileのどの部分をイメージにビルドするかを制御できることが実証されました。
--cache-from と--cache-to でビルドキャッシュを管理
このステップでは、--cache-from と --cache-to フラグを使用して Docker ビルドキャッシュを管理する方法を学びます。ビルドキャッシュを使用すると、以前のビルドからレイヤーを再利用することで、後続のビルドを大幅に高速化できます。--cache-from はキャッシュソースとして使用するイメージを指定でき、--cache-to はビルドキャッシュをレジストリやローカルディレクトリなどの指定場所にエクスポートできます。
まず、~/project ディレクトリにいることを確認してください。
cd ~/project
キャッシュが通常破棄される変更をシミュレートするため、Dockerfile を少し変更しましょう。シンプルな RUN 命令を追加します。
nano で Dockerfile を開きます:
nano Dockerfile
builder ステージの ARG 命令の後に以下の行を追加します:
RUN echo "Adding a new layer"
更新後の Dockerfile は以下のようになります:
## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding a new layer"
RUN echo $GREETING > /app/greeting.txt
## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
CMD ["cat", "/greeting.txt"]
Dockerfile を保存して nano を終了します。
特定のキャッシュオプションを使用せずにイメージを再度ビルドしましょう。Docker は利用可能な場合、自動的にローカルキャッシュを使用します。
docker build -t my-cached-image .
新しい命令を追加したため、一部のレイヤーが最初からビルドされることが確認できます。これにより後続の命令のキャッシュが無効化されます。
次に、レジストリや別のビルドから以前にビルドしたイメージをキャッシュソースとして使用するシナリオをシミュレートします。デモンストレーションのため、先ほどビルドした my-cached-image を新しいビルドのキャッシュソースとして使用します。
まず、Dockerfile を再度少し変更して別の変更をシミュレートします。
Dockerfile を開きます:
nano Dockerfile
新しい RUN 命令のメッセージを変更します:
RUN echo "Adding another new layer"
更新後の Dockerfile は以下のようになります:
## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding another new layer"
RUN echo $GREETING > /app/greeting.txt
## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
CMD ["cat", "/greeting.txt"]
nano を保存して終了します。
--cache-from フラグを使用して my-cached-image をキャッシュソースとして指定し、イメージを再度ビルドします。
docker build --cache-from my-cached-image -t my-cached-image-from .
Docker が my-cached-image からレイヤーを使用しようとするのが確認できます。最初の RUN 命令に対応するレイヤーは命令が変更されたため再ビルドされますが、一致する場合、後続のレイヤーはキャッシュからプルされる可能性があります。
--cache-to フラグはビルドキャッシュをエクスポートするために使用されます。これは CI/CD パイプラインでビルド間でキャッシュを共有する場合に特に便利です。--cache-to を使用するには、通常 docker-container ドライバーのようなキャッシュエクスポートをサポートするビルドドライバーが必要です。
まず、buildx とよく一緒に使用される Docker Compose をインストールします。
sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
buildx ビルダーインスタンスを作成します。
docker buildx create --use
イメージをビルドし、キャッシュをローカルディレクトリにエクスポートします。local キャッシュエクスポーターを使用します。
docker buildx build --cache-to type=local,dest=./build-cache -t my-exported-cache-image . --load
docker buildx build: buildx ツールを使用してビルドします--cache-to type=local,dest=./build-cache: カレントディレクトリのbuild-cacheという名前のローカルディレクトリにキャッシュをエクスポートします-t my-exported-cache-image: 結果のイメージにタグを付けます.: ビルドコンテキスト(カレントディレクトリ)を指定します--load: ビルドしたイメージをローカルの Docker イメージキャッシュにロードします
ビルドとキャッシュエクスポートを示す出力が表示されます。~/project ディレクトリに build-cache ディレクトリが作成されます。
クリーンなビルド環境をシミュレートし、エクスポートされたキャッシュを使用してビルドしてみましょう。まず、ビルドしたイメージを削除します。
docker rmi my-cached-image my-cached-image-from my-exported-cache-image
エクスポートされたキャッシュをソースとして使用して、再度イメージをビルドします。
docker buildx build --cache-from type=local,src=./build-cache -t my-imported-cache-image . --load
--cache-from type=local,src=./build-cache: ローカルディレクトリbuild-cacheからキャッシュをインポートします
Docker がエクスポートされたキャッシュからレイヤーを使用し、最初からビルドする場合と比較してビルドプロセスが大幅に高速化されることが確認できます。
マルチプラットフォームイメージのビルドとレジストリへのプッシュ
このステップでは、複数のアーキテクチャ(linux/amd64 や linux/arm64 など)向けの Docker イメージをビルドし、コンテナレジストリにプッシュする方法を学びます。マルチプラットフォームイメージのビルドは、アプリケーションが異なる種類のハードウェアで実行できるようにするために不可欠です。前のステップで初期化した Docker Buildx を使用します。
まず、~/project ディレクトリにいることを確認してください。
cd ~/project
このステップでは既存の Dockerfile を使用します。linux/amd64 と linux/arm64 プラットフォーム向けのイメージをビルドしましょう。イメージには名前とバージョン(例:your-dockerhub-username/my-multi-platform-image:latest)でタグを付けます。**your-dockerhub-username は実際の Docker Hub ユーザー名に置き換えてください。** Docker Hub アカウントをお持ちでない場合は、無料で作成できます。
複数のプラットフォーム向けにビルドするには、docker buildx build で --platform フラグを使用します。また、結果のマニフェストリストとイメージをレジストリにプッシュするために --push フラグも必要です。
docker buildx build --platform linux/amd64,linux/arm64 -t your-dockerhub-username/my-multi-platform-image:latest . --push
注: このコマンドを実行するには、まだログインしていない場合 Docker Hub へのログインが必要です。必要に応じて別のターミナルセッションで docker login コマンドを使用してログインできます。
docker login
プロンプトが表示されたら Docker Hub のユーザー名とパスワードを入力してください。
ログイン後、再度 docker buildx build コマンドを実行します。
ビルドプロセスは単一プラットフォームのビルドよりも時間がかかります。これは指定された各アーキテクチャ向けにイメージをビルドするためです。ビルドが完了すると、Buildx は各プラットフォームのイメージを参照するマニフェストリストを作成し、指定した Docker Hub リポジトリにすべてをプッシュします。
Web ブラウザで Docker Hub リポジトリを開くと、マルチプラットフォームイメージがプッシュされたことを確認できます。latest タグが付いた my-multi-platform-image が表示され、"Tags" タブの下に複数のアーキテクチャをサポートしていることが示されるはずです。
または、docker buildx imagetools inspect コマンドを使用して、プッシュしたマニフェストリストを検査できます。your-dockerhub-username はあなたのユーザー名に置き換えてください。
docker buildx imagetools inspect your-dockerhub-username/my-multi-platform-image:latest
出力にはマニフェストリストと、それが指す異なるイメージ(それぞれのアーキテクチャ)が表示されます。
これにより、異なる CPU アーキテクチャで実行できる Docker イメージをビルドしてプッシュする方法が示され、Docker イメージの汎用性が高まります。
ビルドへのシークレットと SSH エージェントの公開
このステップでは、Buildx を使用して Docker ビルドプロセスにシークレットと SSH エージェントを安全に公開する方法を学びます。これは、Dockerfileに直接機密情報を埋め込まずに、ビルドがプライベートリポジトリにアクセスしたり、プライベートパッケージをインストールしたり、認証を必要とする外部サービスとやり取りしたりする必要があるシナリオで重要です。
まず、~/projectディレクトリにいることを確認してください。
cd ~/project
ビルド中にシークレットを使用する方法を示すために、Dockerfileを変更します。この例では、ダミーのシークレットファイルを作成し、ビルド中にその内容を読み取ります。
~/projectディレクトリにmysecret.txtという名前のファイルを作成し、シークレットの内容を記述します。
echo "This is a secret message!" > ~/project/mysecret.txt
nanoでDockerfileを開きます:
nano Dockerfile
builderステージに、--mount=type=secretフラグを使用してシークレットにアクセスする新しいRUN命令を追加します。
## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding another new layer"
RUN echo $GREETING > /app/greeting.txt
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret > /app/secret_content.txt
## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
COPY --from=builder /app/secret_content.txt /secret_content.txt
CMD ["cat", "/greeting.txt", "/secret_content.txt"]
新しい命令を理解しましょう:
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret > /app/secret_content.txt: この命令はシークレットをビルドコンテナにマウントします。--mount=type=secret: シークレットをマウントすることを指定します。id=mysecret: ビルド時に提供するシークレットの ID です。cat /run/secrets/mysecret: ビルドコンテナ内で、シークレットは/run/secrets/mysecretで利用可能です。catを使用してその内容を読み取ります。> /app/secret_content.txt: シークレットの内容をbuilderステージ内の/appディレクトリにあるsecret_content.txtというファイルにリダイレクトします。
また、最終ステージにsecret_content.txtファイルをbuilderステージからコピーするCOPY命令を追加しました。
Dockerfileを保存してnanoを終了します。
docker buildx buildを使用してイメージをビルドし、--secretフラグを使用してシークレットを提供します。
docker buildx build --secret id=mysecret,src=~/project/mysecret.txt -t my-secret-image . --load
--secret id=mysecret,src=~/project/mysecret.txt: このフラグはビルドにシークレットを提供します。id=mysecret:Dockerfileで指定された ID と一致します。src=~/project/mysecret.txt: ローカルマシン上のシークレットファイルへのパスを指定します。
ビルドプロセスは、RUN --mount=type=secret...命令の実行中にmysecret.txtの内容にアクセスできるようになります。シークレットの内容は最終的なイメージレイヤーに保存されません。
ビルドが完了したら、イメージからコンテナを実行します。
docker run my-secret-image
コンソールに挨拶メッセージとシークレットファイルの内容の両方が表示されるはずです。
次に、ビルドに SSH エージェントを公開する方法を示します。これはビルドプロセス中にプライベート Git リポジトリをクローンするのに便利です。
まず、SSH エージェントが実行中で、キーが読み込まれていることを確認します。通常、ssh-add -lで確認できます。エージェントが実行されていない場合やキーが追加されていない場合は、エージェントを起動してキーを追加する必要があります。
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa ## 異なる場合はSSHキーのパスに置き換えてください
Dockerfileを変更して SSH エージェントを使用します。SSH を使用して(存在しない)プライベートリポジトリをクローンしようとするRUN命令を追加します。
Dockerfileを開きます:
nano Dockerfile
builderステージに--mount=type=sshフラグを使用する新しいRUN命令を追加します。
## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding another new layer"
RUN echo $GREETING > /app/greeting.txt
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret > /app/secret_content.txt
RUN --mount=type=ssh git clone git@github.com:your-username/your-private-repo.git || echo "Skipping git clone as this is a demo"
## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
COPY --from=builder /app/secret_content.txt /secret_content.txt
CMD ["cat", "/greeting.txt", "/secret_content.txt"]
注: your-username/your-private-repo.gitをプライベートリポジトリ URL のプレースホルダーに置き換えてください。|| echo "Skipping git clone as this is a demo"の部分は、リポジトリが存在しない場合やアクセス権がない場合にビルドが失敗しないように追加されています。
Dockerfileを保存してnanoを終了します。
docker buildx buildを使用してイメージをビルドし、--sshフラグを使用して SSH エージェントへのアクセスを提供します。
docker buildx build --ssh default -t my-ssh-image . --load
--ssh default: このフラグはデフォルトの SSH エージェントをビルドに公開します。
ビルド中、RUN --mount=type=ssh...命令は Git サーバーとの認証に SSH エージェントを使用できるようになります。
ビルドが完了したら、git clone 操作がスキップされた可能性が高いため、出力は以前と同じですが、イメージを実行できます。
docker run my-ssh-image
これにより、Buildx を使用して Docker ビルドプロセス中にシークレットと SSH エージェントを安全に使用し、機密情報がイメージに埋め込まれるのを防ぐ方法が示されます。
まとめ
この実験では、Dockerfileを使用して Docker イメージを構築する基礎を学びました。まず、Ubuntu をベースとしたシンプルなDockerfileを作成し、基本的なコマンドを実行するイメージを定義しました。その後、docker buildコマンドを使用してこのイメージを構築し、Docker がDockerfile内の命令をレイヤーごとに処理する方法や、生成されたイメージにタグを付ける方法を理解しました。
基本を踏まえて、docker buildx buildコマンドのより高度な機能を探求しました。これには、ビルドプロセスに動的な値を渡すためのビルド引数の活用や、マルチステージビルド内のターゲットステージを活用してイメージサイズとビルド時間を最適化する方法が含まれます。また、--cache-fromと--cache-toを使用してビルドキャッシュを効果的に管理し、後続のビルドを高速化する方法も学びました。さらに、異なるアーキテクチャで実行可能なマルチプラットフォームイメージを構築し、これらのマルチプラットフォームイメージをコンテナレジストリにプッシュする経験を積みました。最後に、ビルドプロセス中にシークレットを安全に公開し、SSH エージェントを活用する方法を発見し、イメージビルドのセキュリティと柔軟性を向上させました。



