DockerfileにおけるCMD指令の動作原理
コンテナランタイムにおいてプロセスを起動する際、DockerfileのCMD指令は初期コマンドとその引数を定義します。この指令はdocker run実行時に引数リストとして容易に置換可能であり、Go言語のようなコンパイル済みバイナリを実行するシーンで柔軟な起動制御を実現するために利用されます。Exec形式(JSON配列形式)で使用した場合、シェルを経由せずに直接execシステムコールが実行されるため、シグナル処理やメモリ効率面でも優位性があります。
Go側の実装と引数処理ロジック
コンテナ内部で動作するバイナリは、標準入出力やOSレベルの引数を適切に扱えるよう実装する必要があります。以下の例では位置引数を検証し、不正な起動時に明示的に終了ステータスを返すように再構成されています。
package main
import (
"fmt"
"os"
)
func main() {
runParams := os.Args[1:]
if len(runParams) == 0 {
fmt.Fprintln(os.Stderr, "失敗:実行引数が必須です")
os.Exit(1)
}
fmt.Printf("取得したパラメータ: %q\n", runParams[0])
}
依存関係管理ファイルはモジュール名を一意に変更し、以下の通り定義します。
module internal/go-cmd-runtime
go 1.21
マルチステージビルドによるDockerfile設計
イメージサイズを最適化するため、ビルド環境と実行環境を分離するマルチステージビルドを採用します。静的リンク有効化と最小限のベースイメージを組み合わせた実装例は以下の通りです。
# 第1ステージ:コンパイル環境
FROM golang:1.21-alpine AS comp-env
WORKDIR /workdir
COPY go.mod ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
-a -installsuffix cgo \
-o /opt/app-bin/server ./main.go
# 第2ステージ:ランタイム環境
FROM scratch
COPY --from=comp-env /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=comp-env /opt/app-bin/server /srv/run
EXPOSE 9090
# デフォルト実行コマンドと引数の設定
CMD ["/srv/run", "init-payload"]
scratchベースのイメージはOSカーネル以外のファイルを一切含まないため、攻撃urfaceを最小限に抑えられます。CMDはExec形式で記述しており、第一要素が実行パス、以降の要素が引数となります。
イメージビルドと実行時引数のオーバーライド
プロジェクトディレクトリ上で以下の手順でイメージを生成します。
docker build -t cli-container .
生成されたイメージからコンテナを起動する際、末尾に指定された文字列が既存のCMD引数を完全上書きします。これはDockerが最終的なコマンドライン配列をマージ而非追記するためです。
docker run --rm cli-container override-payload
上記実行時のStdoutには「取得したパラメータ: "override-payload"」と表示されます。Go側ではos.Argsが[]string{"override-payload"}として読み込まれ、期待通りに処理フローが分岐します。本手法は設定ファイルの差し替えや環境ごとの起動フラグ制御など、CI/CDパイプライン内での動的なデプロイ制御にも応用可能です。