Contents
導入:Goアプリのコンテナ化で得られる利点と全体フロー
Goアプリのコンテナ化は、開発と本番の環境差を減らし、デプロイの自動化と供給連鎖の管理を容易にします。ここでは実務で使えるDockerfile、CIワークフロー、CA/TLS・非root実行、SBOM/スキャン/署名までを一貫して示し、Goアプリのコンテナ化を再現性高く安全に進める手順を提供します。
クイックスタート:最小のHTTPサーバーとローカルでの検証
最短で動作確認したい方向けに、最小のHTTPサーバーコードとローカルでのビルド/実行手順を示します。まず動かして問題がないことを確認し、その後Docker化やCIに進んでください。
サンプルHTTPサーバー(main.go)
シンプルなHTTPサーバーで動作確認を素早く行えます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package main import ( "fmt" "log" "net/http" "os" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, world") }) port := os.Getenv("PORT") if port == "" { port = "8080" } log.Fatal(http.ListenAndServe(":"+port, nil)) } |
ローカルでの最短実行手順
ローカル確認用の最小手順です。ワーキングディレクトリはプロジェクトルートを想定します。
-
バイナリビルド
go build -o bin/server . -
ローカルイメージ作成(簡易)
docker build -t myapp:local . -
実行確認
docker run --rm -p 8080:8080 myapp:local
開発向けのホットリロードと実行パターン
開発時の典型的な運用例と注意点を簡潔に示します。
- ホットリロードは air / reflex / CompileDaemon 等を利用すると便利です。
- 開発ではソースをボリュームマウントし、コンテナ内でビルド/実行するパターンがよく使われます。
- 本番イメージは必ず最小化して、デバッグ用イメージは別途用意してください。
Dockerfile実践:マルチステージとマルチアーキ対応
マルチステージとBuildKitを使うことで、マルチアーキ対応かつ小型で安全なイメージを作れます。ここではARGでTARGETOS/TARGETARCHを受け取り、GOOS/GOARCHを動的に設定する実例を示します。
マルチステージDockerfile(マルチアーキ対応)
BuildKitが提供するTARGET*引数を利用して、クロスビルドの不一致を避けます。ベースイメージは具体的なマイナーバージョンで固定し、可能ならダイジェストでピン留めしてください。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# syntax=docker/dockerfile:1.4 ######################################## # Builder ######################################## FROM golang:1.20.7 AS builder # 本番では golang:1.20.7@sha256:<digest> のようにダイジェストで固定してください。 # ダイジェスト取得例: # docker pull golang:1.20.7 # docker inspect --format='{{index .RepoDigests 0}}' golang:1.20.7 ARG TARGETOS ARG TARGETARCH ARG TARGETPLATFORM ARG CGO_ENABLED=0 ENV CGO_ENABLED=${CGO_ENABLED} WORKDIR /src # CAバンドルを最終イメージにコピーするため、builder側でインストールします。 RUN apt-get update && \ apt-get install -y --no-install-recommends ca-certificates git && \ rm -rf /var/lib/apt/lists/* # 依存キャッシュ COPY go.mod go.sum ./ RUN go mod download # ソースをコピー COPY . . # マルチアーキ向けに TARGETOS/TARGETARCH を使ってビルドします。 # CGO_ENABLED を 1 にする場合は追加のクロスコンパイラが必要です(下記参照)。 RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg/mod \ sh -c 'GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \ go build -trimpath -ldflags="-s -w" -o /app/server ./... && \ chmod 0755 /app/server' ######################################## # Final: distroless (推奨) / scratch の例 ######################################## FROM gcr.io/distroless/static:nonroot AS final COPY --from=builder /app/server /app/server # distroless/scratch は CA が無い場合があるため、builder からコピーする COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt # numeric UID/GID を使う設計にしておく(K8s と整合させるため) USER 65532:65532 EXPOSE 8080 ENTRYPOINT ["/app/server"] |
scratch を使う最小例
scratch を使う場合はファイルの所有権と CA を builder 側で整えてからコピーします。scratch では RUN が使えないので事前整備が重要です。
|
1 2 3 4 5 6 7 |
FROM scratch AS final COPY --from=builder --chown=65532:65532 /app/server /app/server COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt USER 65532:65532 EXPOSE 8080 ENTRYPOINT ["/app/server"] |
ベースイメージのバージョン固定について
ベースイメージは具体的なマイナーバージョンで指定してください。可能ならダイジェストで固定します。ダイジェスト取得例:
- docker pull golang:1.20.7
- docker inspect --format='{{index .RepoDigests 0}}' golang:1.20.7
本番用 Dockerfile はダイジェストに置き換えて運用してください。
CAバンドルとTLS(distroless/scratch対応)
distroless や scratch を使う場合、システムのCAが存在しないと外部HTTPS通信が失敗します。builder で ca-certificates をインストールして最終イメージにコピーしてください。必要なら環境変数 SSL_CERT_FILE を最終イメージで設定します。
- builder での準備例: apt-get install -y ca-certificates
- 最終イメージへコピー: COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
- Go の一部バージョン/構成では別のパスを参照する場合があるため、テストで接続確認を行ってください。
非root実行と所有権の設定
最終イメージは非rootで実行するのが安全です。ただしファイル所有権とパーミッションは事前に整えておく必要があります。主な手順は次の通りです。
- builder でビルド後に実行ファイルのパーミッションを設定: chmod 0755 /app/server
- builder で必要なディレクトリを作成し chown する: mkdir -p /app && chown -R 65532:65532 /app
- 最終イメージへは COPY --chown=65532:65532 を利用して所有権を付与する
- Kubernetes では securityContext.runAsUser と合わせる(例: 65532)
例(builder内で実行):
|
1 2 3 |
RUN go build -o /app/server ./... && \ mkdir -p /app && chmod 0755 /app/server && chown -R 65532:65532 /app |
例(最終ステージでのコピー):
|
1 2 3 |
COPY --from=builder --chown=65532:65532 /app /app USER 65532:65532 |
ボリュームマウント時のUID/GID不一致は initContainer で chown するか、ホストとUIDを揃えることで回避します。
CGOと静的ビルド(要点と回避策)
CGO_ENABLED=0 は多くの場合に有効ですが、常に静的バイナリが得られるわけではありません。特に以下の点に注意してください。
- CGO を必要とするサードパーティライブラリがある場合、CGO_ENABLED=0 ではビルドできません。依存を確認してください。
- CGO_ENABLED=1 でクロスコンパイルするにはクロスコンパイラが必要です(例: aarch64 用に gcc-aarch64-linux-gnu 等)。
- glibc と musl の違いにより、glibc 依存のバイナリを musl ベースのイメージで動かすと失敗します。互換性が必要なら debian-slim 系を使ってください。
- buildx と QEMU はユーザーランドの実行をエミュレートしますが、クロスコンパイル時のコンパイラ依存は解決しません。CI で実機検証を行ってください。
必要なら「glibc 静的リンク」や「musl ビルド(musl-cross)」などの専門手順を採用してください。
CI/CD実装例:Buildxマルチアーキ、スキャン、SBOM、署名、シークレット管理
CI でマルチアーキ、スキャン、SBOM、署名を自動化すると安全性が高まります。ここでは変数整合を保った GitHub Actions の動作例を示します。重要な点はレジストリ/イメージ/タグを明確に分けることです。
GitHub Actions ワークフロー(動作例)
下記は動作することを想定した雛形です。実運用ではシークレット管理やツールバージョンを固めてください。必須の権限として id-token: write を付与すると cosign の keyless(OIDC)署名が使えます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
name: CI - Build / Scan / SBOM / Sign on: push: branches: [ "main" ] env: REGISTRY: ghcr.io IMAGE: ghcr.io/yourorg/yourapp TAG: ${{ github.sha }} jobs: build-and-publish: runs-on: ubuntu-latest permissions: contents: read packages: write id-token: write steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up buildx uses: docker/setup-buildx-action@v2 - name: Create build secret file for BuildKit run: | echo "${{ secrets.GIT_TOKEN }}" > /tmp/git_token chmod 600 /tmp/git_token - name: Login to registry uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.REGISTRY_USER }} password: ${{ secrets.REGISTRY_PASSWORD }} - name: Build and push multi-arch image uses: docker/build-push-action@v4 with: context: . push: true platforms: linux/amd64,linux/arm64 tags: ${{ env.IMAGE }}:${{ env.TAG }} cache-from: type=gha cache-to: type=gha,mode=max secrets: | id=git_token,src=/tmp/git_token ssh: default - name: Trivy scan (fail on HIGH/CRITICAL) run: | docker run --rm aquasec/trivy:latest image --exit-code 1 --severity HIGH,CRITICAL ${{ env.IMAGE }}:${{ env.TAG }} - name: Generate SBOM (syft) and upload run: | docker run --rm -v "${{ github.workspace }}:/work" anchore/syft:latest ${IMAGE}:${TAG} -o json > sbom.json env: IMAGE: ${{ env.IMAGE }} TAG: ${{ env.TAG }} - name: Upload SBOM artifact uses: actions/upload-artifact@v4 with: name: sbom path: sbom.json - name: Sign image with cosign (keyless/OIDC) run: | # id-token permission required on job level for keyless cosign sign --keyless ${{ env.IMAGE }}:${{ env.TAG }} |
上記では IMAGE / REGISTRY / TAG を明示して使い回しています。docker/login-action の registry 引数が REGISTRY を参照している点に注意してください。
署名鍵の扱いとセキュリティ(ベストプラクティス)
署名鍵を CI Secrets に直接置くのはリスクが高いです。代替策を優先してください。
- 優先: cosign の keyless(OIDC)署名を使う。GitHub Actions の id-token を使うと一時的な署名が可能です。
- 代替: クラウド KMS(AWS KMS, GCP KMS, Azure KeyVault)に鍵を保管し cosign の KMS 対応を利用する。例: cosign sign --key awskms://arn:...
- もしシークレット(プライベートキー)を置く場合は最小権限、厳格なローテーション、アクセスログ、有効期限を設定してください。
- デプロイ前に必ず署名検証を実行し、署名されていないイメージはデプロイさせない仕組みにしてください(下記の deploy ジョブ参照)。
配布前の自動署名検証(デプロイ前ゲート)
署名を持つイメージのみをデプロイするため、デプロイジョブの冒頭で検証を行ってください。例:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
jobs: deploy: runs-on: ubuntu-latest needs: build-and-publish steps: - name: Verify cosign signature run: | cosign verify --keyless ${{ env.IMAGE }}:${{ env.TAG }} - name: Deploy to cluster run: | # 検証が通ったらデプロイ処理を実行 kubectl -n prod set image deployment/myapp myapp=${{ env.IMAGE }}:${{ env.TAG }} |
検証に失敗した場合、ジョブは停止します。
BuildKit secret / ssh マウントの具体例
BuildKit の secret/ssh マウントは認証情報をイメージに残さずにプライベート依存を取得するのに便利です。Dockerfile と CI 側の整合サンプルを示します。
Dockerfile 側での secret マウントの使い方
Dockerfile 内で secret は /run/secrets/
|
1 2 3 4 5 6 |
# builder ステージの一部 RUN --mount=type=secret,id=git_token \ sh -c 'GIT_TOKEN="$(cat /run/secrets/git_token)" && \ git -c http.extraHeader="Authorization: Bearer $GIT_TOKEN" clone https://github.com/yourorg/private-mod.git /tmp/private-mod && \ rm -rf /tmp/private-mod' |
上記はトークンを直接標準出力に出さないようにしています。BuildKit はこのファイルをイメージレイヤに含めません。
GitHub Actions 側での secrets 供給(整合例)
BuildKit の secret をファイル経由で渡す例です。ssh の場合は ssh-agent を用いて buildx に転送できます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
- name: Write GIT_TOKEN to file run: | echo "${{ secrets.GIT_TOKEN }}" > /tmp/git_token chmod 600 /tmp/git_token - name: Build and push (use secret) uses: docker/build-push-action@v4 with: context: . push: true tags: ${{ env.IMAGE }}:${{ env.TAG }} secrets: | id=git_token,src=/tmp/git_token ssh: default |
SSH で private repo を使う場合の典型パターン:
|
1 2 3 4 5 6 7 8 9 10 11 |
- name: Set up SSH agent uses: webfactory/ssh-agent@v0.5.4 with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - name: Build with ssh forwarding uses: docker/build-push-action@v4 with: ssh: default ... |
Dockerfile 側は次のように ssh を利用します:
|
1 2 |
RUN --mount=type=ssh git clone [メールアドレス削除]:yourorg/private-mod.git /tmp/private-mod |
注意点と検証
- ビルドログに認証情報が流出しないよう、RUN コマンド内でトークンを echo しないでください。
- BuildKit secret はイメージに含まれませんが、誤った RUN ログやファイル操作で漏れることがあるため確認してください。
- ビルド完了後に一時ファイルが残っていないことをローカルで確認してください。
運用チェックリストとよくあるトラブル対応(実務向け)
運用で必ず確認したい項目と、よく出る障害の速やかな対処法をまとめます。ポイントは自動化と検証の導入です。
主要な運用チェック項目
- go.mod / go.sum を必ずコミットし、依存が固定されているか確認する。
- CI で SBOM を生成しアーティファクトとして保存する。
- 脆弱性スキャンの閾値(例: HIGH/CRITICAL)を定め、結果でパイプラインを失敗させる。
- 署名(cosign)と検証を CI に組み込み、未署名イメージはデプロイ不可にする。
- マルチアーキの manifest を docker buildx imagetools inspect で検証する。
- Kubernetes の securityContext で runAsNonRoot / runAsUser を設定する(例: 65532)。
- Liveness/Readiness、requests/limits を設定し、ログは stdout/stderr に集約する。
よくあるトラブルと対処(抜粋)
- permission denied(ファイルアクセス)
-
COPY --chown を使い最終イメージの所有権を正しく設定します。ボリュームマウントのUID差も確認してください。必要なら initContainer で chown します。
-
x509 証明書エラー(HTTPS)
-
scratch/distroless では CA がないことが多いです。builder で ca-certificates を入れ、/etc/ssl/certs/ca-certificates.crt を最終イメージにコピーしてください。アプリで SSL_CERT_FILE を参照している場合はパスを合わせます。
-
CGO 関連のビルド失敗
-
CGO_ENABLED とビルド環境(クロスコンパイラの有無)を確認します。可能なら CGO を無効化して静的ビルドを行い、どうしても CGO が必要な場合は debian 系ベースで glibc を利用する方が安定します。
-
buildx のマルチアーキ不整合
-
Dockerfile で ARG TARGETOS/TARGETARCH/TARGETPLATFORM を使い、ビルド時に GOOS/GOARCH を動的に設定してください。ビルド後に docker buildx imagetools inspect で manifest を確認します。
-
Delve のリモートデバッグ不可
- 本番で ptrace 等を有効にするのは避けてください。開発専用のデバッグイメージを用意し、必要な権限だけを開けて使います。
参考コマンド集
-
ローカルビルド:
go build -o bin/server . -
ローカルイメージ作成:
docker build -t myapp:local . -
マルチアーキビルド&プッシュ (buildx):
docker buildx build --platform linux/amd64,linux/arm64 -t/ / : --push . -
マニフェスト確認:
docker buildx imagetools inspect/ / : -
脆弱性スキャン (Trivy, 例):
docker run --rm aquasec/trivy:latest image --exit-code 1 --severity HIGH,CRITICAL/ / : -
SBOM生成 (Syft, 例):
docker run --rm anchore/syft:latest/ / : -o json > sbom.json -
署名 (Cosign - Keyless/OIDC):
cosign sign --keyless/ / : -
署名検証 (Cosign):
cosign verify --keyless/ / :
まとめ
- Goアプリのコンテナ化は、環境差を減らし自動化と供給連鎖管理を可能にします。
- Dockerfile は ARG TARGETOS/TARGETARCH を使いマルチアーキ対応にし、ベースイメージはマイナーバージョンまたはダイジェストで固定してください。
- distroless/scratch を使う場合は CA バンドルとファイル所有権を builder 側で整え、COPY --chown で最終イメージに反映してください。
- CI では buildx、Trivy、Syft、Cosign を組み合わせ、署名検証をデプロイ前ゲートに入れてください。