Dockerfileを一行ずつ積む
このラボは本物のVM上で動きます
この環境はPodではなく、KubeVirtが起動した仮想マシンです。Linuxカーネルが別に動き、systemdが実際にサービスを管理し、dockerは模倣ではなく本物のDockerエンジンです。docker runで起動したコンテナは実際にプロセスになり、docker execもdocker logsもそのまま動作します。
以前はこのラボがPodの中で動いていました。カーネル権限をすべて落とした環境だったため、コンテナを起動するステップが塞がれており、イメージアーカイブを自分で展開してみるという回り道で学んでいました。もう回り道は必要ありません。
知っておくべきことが2つあります。
- 最初の起動に1分ほどかかります。VMが起動してDockerをインストールするためです。Podのラボ(通常40秒)より遅くなります。
- ブラウザープレビューはありません。VMへの接続は、採点用のポート1つしか開いていません。Webサーバーを起動した場合は、VMの中で
curlを使って確認してください。
目標
Dockerfileの命令を1つずつ追加しながら、それぞれがイメージのどの部分を変えるのかをinspectで確認し、最後にレビューのチェックリストを通過するファイルを作ります。
なぜ重要なのか
Dockerfileは設定ファイルのように見えますが、実際には順序が意味を持つビルドスクリプトです。同じ命令でも、位置によってレイヤーが増え、キャッシュが壊れ、シークレットが残り続けます。このラボの目的は、命令を暗記することではなく、「この行はレイヤーを作るのか、設定を刻むのか」を毎回区別する習慣をつけることです。その区別1つで、サイズの問題とセキュリティの問題のほとんどを予防できます。
ステップ
各ステップで新しいタグでビルドし、前のステップの結果も残しておきます。
/root/build1を作成し、/root/build1/Dockerfileの最初の行をFROM alpine:3.20として書きます。labhub/app:v1でビルドします。/root/build1/app.shを作成してapp-runningを出力させ、DockerfileにWORKDIR /appとCOPY app.sh /app/を追加して、labhub/app:v2でビルドします。CMDをJSON配列形式で追加してapp.shが実行されるようにし、labhub/app:v3でビルドします。実行すると、app-runningが出力される必要があります。ENV APP_ENV=prodとARG VERSIONを追加し、ARGで受け取った値をLABEL app.versionに刻みます。--build-arg VERSION=2.1.0でlabhub/app:v4をビルドします。ENTRYPOINTを配列形式にし、CMD ["default-arg"]をデフォルト値にして、引数なしで実行するとdefault-argが、override-argを渡すとoverride-argが出力されるようにしたうえで、labhub/app:v5でビルドします。LABEL org.opencontainers.image.title=labhub-appとEXPOSE 8080を追加して、labhub/app:v6でビルドします。- 最終的な
/root/build1/Dockerfileが、次の5項目をすべて満たすように整えて、labhub/app:v7でビルドします。FROMにタグが固定されており、latestではありませんWORKDIRが最初のCOPYより前にありますENTRYPOINTまたはCMDがJSON配列ですENV/ARGの名前に、TOKEN・SECRET・PASSWORD・API_KEYが含まれません
参考
docker image inspect labhub/app:v4 | jq '.[0].Config'で、設定フィールドを一目で確認できます。docker build --build-arg VERSION=2.1.0 -t labhub/app:v4 /root/build1の形で引数を渡します。- よくある間違い1:
CMD app.shのようにシェル形式で書くと、配列ではなく文字列1つになります。 - よくある間違い2: ステップ5でARGの値をENVで渡すと、イメージにそのまま残ります。LABELに刻んでください。
ベースイメージを固定する
/root/build1を作成し、/root/build1/Dockerfileの最初の行をFROM alpine:3.20として書きます。
FROMにタグを明記してください。latestは、昨日のビルドと今日のビルドが変わる原因になります。
最初のビルド
labhub/app:v1でビルドします。
ビルドコンテキストには、Dockerfileがあるディレクトリを指定します。-tで名前を付けないと、あとで見つけにくくなります。
作業ディレクトリとファイルのコピー
/root/build1/app.shを作成してapp-runningを出力させ、DockerfileにWORKDIR /appとCOPY app.sh /app/を追加して、labhub/app:v2でビルドします。
WORKDIRはイメージ設定(Config.WorkingDir)に刻まれ、COPYはレイヤーを作ります。2つの命令の順序が、相対パスの意味を決めます。
デフォルトコマンドを指定する
CMDをJSON配列形式で追加してapp.shが実行されるようにし、labhub/app:v3でビルドします。実行すると、app-runningが出力される必要があります。
シェル形式で書くと/bin/sh -cで包まれ、PID 1がシェルになります。配列形式が必要な理由は、前のコースで見ました。
ビルド引数と環境変数の違い
ENV APP_ENV=prodとARG VERSIONを追加し、ARGで受け取った値をLABEL app.versionに刻みます。--build-arg VERSION=2.1.0でlabhub/app:v4をビルドします。
ARGはビルド中だけ存在し、ENVはイメージに残ります。ビルド引数で受け取った値をイメージに残すには、LABELに刻んでください。
ENTRYPOINTとCMDの組み合わせ
ENTRYPOINTを配列形式にし、CMD ["default-arg"]をデフォルト値にして、引数なしで実行するとdefault-argが、override-argを渡すとoverride-argが出力されるようにしたうえで、labhub/app:v5でビルドします。
ENTRYPOINTは固定され、CMDは実行時の引数で置き換えられます。どちらも配列形式で書いてください。
標準ラベルとポートの宣言
LABEL org.opencontainers.image.title=labhub-appとEXPOSE 8080を追加して、labhub/app:v6でビルドします。
OCI標準のラベルキーは、org.opencontainers.image.で始まります。EXPOSEは、ポートを開くのではなく、文書化するための命令です。
レビューのチェックリストを通過させる
最終的な/root/build1/Dockerfileが、次の5項目をすべて満たすように整えて、labhub/app:v7でビルドします。
FROMにタグが固定されており、latestではありませんWORKDIRが最初のCOPYより前にありますENTRYPOINTまたはCMDがJSON配列ですENV/ARGの名前に、TOKEN・SECRET・PASSWORD・API_KEYが含まれません
ここまでのDockerfileを、5項目に合わせて整えてください。採点では、ファイルの内容とビルド結果の両方を確認します。