はじめに
Pine & Path は Alder Ridge の来訪者ガイドを別のビジターセンターへ送ろうとしています。現在のイメージは動きますが、アプリケーションと一緒に Go コンパイラー、ソースファイル、ビルドの残骸まで含んでいます。4 本すべてのトレイル、難易度フィルター、詳細ページ、ローカルの画像、完全な GPX ルートのダウンロードを維持した、軽量な実行用イメージを届けましょう。
簡単な Docker イメージのビルドと実行、および Dockerfile の FROM、COPY、ポート公開を理解していることを前提とします。Go のプログラミングは不要です。完成したアプリケーションと素材が用意されています。この実験では、コンパイル、ビルドステージ、実行時のファイル配置、シェルでファイルを書く構文を、使用前に説明します。公園とルートのデータは架空の教材です。
Docker、ビルダーイメージ、参照サービスは準備済みです。コマンドには Workstation、動く見本には Reference Guide、自分の成果物には Your Guide を使います。この実験はチャレンジとは独立しています。読む時間、ビルド、よくある間違いへの対処を含めて約 60 分を見込んでください。これは作者による見積もりです。
動作する出発点のイメージをビルドする
このステップでは、完全なガイドの動作を確認してから、用意された出発点のイメージをビルドします。まず Reference Guide を開きます。4 本のトレイルが表示されます。Easy を選び、Cedar Loop を開いて距離と登りの標高差を読み、Download GPX を使ってください。GPX は名前付きのトラックと地理的な点を含む XML で、このダウンロードは実際のルートファイルです。Back to trails、続いて Moderate を選び、Ridge Path を開きます。すべてのトレイルの表示に戻ってください。文章だけでなく、スタイルシートと模式図にも注目しましょう。これらも実行時に必要なリソースです。

Workstation に切り替えます。開始ディレクトリは /home/labex/project/trail-guide です。pwd は現在のディレクトリを表示し、ls は項目を一覧表示します。プロジェクトを離れた場合は、次の cd で戻れます。
cd /home/labex/project/trail-guide
ls
用意された構成は次のとおりです。
| ソースのパス | 実行時の用途 |
|---|---|
app/main.go |
コンパイルする Go プログラム。実行時にソースは不要 |
content/trails.json |
4 本すべてのトレイルとその関係 |
content/routes/ |
4 つの GPX ダウンロード |
public/ |
スタイルシートとオリジナルの模式図 |
author-notes/, build-cache/ |
下書きと残骸。実行時のリソースではない |
これらの素材と参照サービスは変更しないでください。編集するのは Dockerfile、追加するのは .dockerignore、作成するのはこのコースのイメージとコンテナーです。Dockerfile.incomplete は次のステップ用に用意された失敗例です。
cat はテキストファイルを表示します。既存の Dockerfile を読みましょう。
cat Dockerfile
これは、準備済みの Go コンパイラーイメージ dk02-go-builder:1 から始まります。WORKDIR /guide はイメージ内の作業ディレクトリを選びます。COPY . . は現在のビルドコンテキストをそのディレクトリへコピーするため、最終的なアプリケーションに不要なファイルも入ります。最後の ENTRYPOINT は、コンパイル済みのプログラムを直接起動する JSON 配列です。
ビルドコマンドでは、そのコマンドに対して CGO_ENABLED=0 を設定するため、この標準ライブラリだけを使う Go の例は C ライブラリを必要としません。go build は用意されたプログラムをコンパイルします。-trimpath は実行ファイルからローカルのソースディレクトリの情報を除き、-o /guide/guide は出力名を指定します。そのため、このプログラムは空の実行用ベースに適しています。ほかのプログラムには共有ライブラリ、証明書、その他のデータが必要な場合があり、空のベースが万能というわけではありません。
まずコンパイラーのバージョンを読みます。docker run --rm は一時コンテナーを作り、コマンドの終了後に削除します。イメージ名の後の語句で、実行するコマンドを選びます。
docker run --rm dk02-go-builder:1 go version
出力には Linux 上の Go 1.26 と表示されます。これは Workstation 上ではなく、コンテナー内でコンパイラーを実行しています。
出発点のイメージをビルドします。docker build は Dockerfile を読み、-t は結果にタグを付け、末尾の . は現在のディレクトリをビルドコンテキストに指定します。
docker build -t trail-guide:sample .
ビルドの成功を待ってください。タグが識別するのはイメージであり、実行中のコンテナーではありません。バイト単位のサイズを見るには、docker image inspect でメタデータを読み、--format で 1 つのフィールドを選びます。シングルクォートはテンプレートをそのまま渡します。
docker image inspect trail-guide:sample --format '{{.Size}}'
値は準備されたコンパイラーイメージによって変わります。出発点のイメージは動きますが、コンパイラーとソースも含んでいます。サイズが小さいだけでは、置き換えたイメージの完全性は証明できません。
ビルド用ファイルと実行用ファイルを分ける
このステップでは、あるステージでプログラムをビルドし、別のステージで実行ファイルと実行時のリソースだけを届けます。Workstation の同じプロジェクトディレクトリから始めます。ステージは FROM 命令から始まります。AS build と名前を付けると、後の COPY --from=build でコンパイラーや以前のファイルシステムレイヤーを継承せずに成果物を取得できます。
まず、実際に不完全な成果物を確認します。用意された Dockerfile.incomplete はプログラムをコンパイルして公開用の画像をコピーしますが、トレイルのデータを含みません。
cat Dockerfile.incomplete
-f オプションでこの別の Dockerfile を選びます。
docker build -f Dockerfile.incomplete -t trail-guide:incomplete .
コンパイルはトレイル一覧を読まないため、ビルドは成功します。次の実行は、意図的に終了ステータス 1 で失敗する想定です。
docker run --rm trail-guide:incomplete
open content/trails.json: no such file or directory というエラーを読んでください。実行ファイルはありますが、実行時のデータがありません。ビルドの成功とアプリケーションの完全性は別の結果です。失敗した一時コンテナーは削除済みです。そのコンテナーの修復やアプリケーションの変更ではなく、通常の Dockerfile を直します。
ヒアドキュメントはテキストのブロック全体を保存します。以下では、cat が行を供給し、> が Dockerfile に書き込み、<<'DOCKERFILE' が DOCKERFILE だけの行で終わるリテラルのブロックを示します。開始マーカーを引用すると、保存時のシェル展開を防げます。編集可能な Dockerfile だけを置き換えてください。
cat > Dockerfile <<'DOCKERFILE'
FROM dk02-go-builder:1 AS build
WORKDIR /src
COPY app/main.go ./main.go
RUN CGO_ENABLED=0 go build -trimpath -o /out/guide main.go
FROM scratch
WORKDIR /guide
COPY --from=build /out/guide ./guide
COPY content/ ./content/
COPY public/ ./public/
ENTRYPOINT ["/guide/guide"]
DOCKERFILE
ファイルを追ってみましょう。第 1 ステージはソースを /out/guide にコンパイルします。2 番目の FROM は第 1 ステージを継承せず、Docker の空のファイルシステムベースである scratch から始めます。実行ファイルは /guide/guide になり、トレイルデータと GPX は content の配置を、スタイルと画像は public の配置を保ちます。プログラムは作業ディレクトリを基準として、これらの相対パスを読みます。実行ファイルだけのコピーでは足りなかった理由です。
最終イメージにシェルやコンパイラーはありません。この例では想定どおりです。内部でシェルを動かそうとせず、アプリケーションを調べてください。
ビルドコンテキストは別の論点です。.dockerignore はビルダーへ送る前に、一致するコンテキストのパスを除外します。先頭のドットは隠しファイル名を表します。次のルールはアプリケーションや実行時リソースを除外せず、下書きと古いキャッシュを省きます。
cat > .dockerignore <<'IGNORE'
author-notes/
build-cache/
IGNORE
2 ステージの Dockerfile は実行用ファイルを明示的に選び、ignore ファイルは不要な素材の送信も防ぎます。コンテキストのファイルを除外しても、継承したレイヤーにすでに保存されたバイトは消せません。ビルドレイヤーを残さないのは、分離した実行用ステージです。
配布用イメージをビルドします。
docker build -t trail-guide:lean .
docker image inspect trail-guide:lean --format '{{.Size}}'
サンプルの値と比べてください。目標は、完全なガイドを持つ小さな実行環境であり、数値を限界まで小さくすることではありません。COPY が失敗したら、不足しているソースのパスを読み、構成表と現在のディレクトリを照合します。Dockerfile または ignore のルールを直して再ビルドしてください。不完全なイメージを隠すために代用データを作ったり、ソースフォルダーをマウントしたりしないでください。
配布したガイドを実行して作り直す
このステップでは、ソースフォルダーから独立してイメージを実行し、ガイド全体を使います。Workstation から始めます。プログラムはコンテナーのポート 8080 で待ち受けます。8090:8080 を公開すると Workstation のポート 8090 で利用でき、Your Guide に表示されます。-d はサーバーをバックグラウンドに残し、--name はこのコースのコンテナーに名前を付けます。マウントは追加しないでください。必要なリソースはイメージ自身に含める必要があります。
docker run -d --name trail-guide -p 8090:8080 trail-guide:lean
出力はコンテナー ID です。このコンテナーはガイドを提供するために動き続けます。docker logs はプログラムの出力を読み、--tail 5 は直近の 5 行に制限します。
docker logs --tail 5 trail-guide
Guide listening on :8080 と表示される想定です。これは公開したホストのポートではなく、コンテナーのポートです。以前の試行でコンテナー名が使用中なら、後述のこのコースだけを対象とする削除コマンドを使い、起動コマンドを再実行します。リソース不足のエラーなら、先に Dockerfile を直して再ビルドする必要があります。
Your Guide に切り替え、再読み込みします。4 本すべてのトレイルが表示されるはずです。Easy を選ぶと Cedar Loop と River Walk だけになります。Cedar Loop を開き、情報と地図を調べて GPX をダウンロードします。Back to trails で戻り、Moderate を選び、Ridge Path を開いてそのルートもダウンロードします。すべてのトレイルの表示に戻ってください。ローカルの画像とスタイルが読み込まれ、トラックは選んだトレイルのものである必要があります。Reference Guide は独立して利用できるままです。

次に、同じイメージでコンテナーを置き換えられることを示します。Workstation に切り替えます。docker rm -f は指定したこのコースのコンテナーだけを停止して削除し、イメージは削除しません。dk02-reference は動かしたままにしてください。
docker rm -f trail-guide
マウントなしで新しいインスタンスを作ります。
docker run -d --name trail-guide -p 8090:8080 trail-guide:lean
Your Guide に切り替え、再読み込みして、Easy/Cedar Loop と Moderate/Ridge Path の閲覧およびダウンロードを繰り返します。この新しいコンテナーには Workstation のソースフォルダーを渡していません。実行環境はイメージから得ています。
コンテナーは動かしたままにしてください。今後 Dockerfile を変更してタグを再ビルドしても、既存コンテナーは更新されません。新しいイメージで実行するには、自分のこのコースのコンテナーだけを削除して作り直します。サービス停止や接続失敗と、不明なルートに対してガイド自身が表示する Trail not found ページは異なります。
まとめ
コンパイラー用ステージと空の実行用ステージを分離し、アプリケーションが実際に必要とするリソースをコピーし、下書きをビルドコンテキストから除外しました。軽量なイメージは Workstation のマウントなしで、完全な一覧、フィルター、詳細、画像、GPX ダウンロードを提供します。コンテナーの作り直しにより、このイメージがソースフォルダーを借りて動くプロセスではなく、再利用できる配布物であることを確認しました。



