Contents
Go言語 Docker コンテナ:サンプルプロジェクト構成と概略フロー
ここでは最小限のサンプル構成と、リポジトリを手元で動かすまでの流れを示します。開発(Dev Container/ホットリロード)からビルド、CI、Kubernetes デプロイまでを想定しています。
ファイル構成
一般的なファイル配置例を示します。各ファイルは後段でテンプレート例を示します。
- cmd/server/main.go
- go.mod / go.sum
- internal/ や pkg/
- Dockerfile.dev
- Dockerfile
- .devcontainer.json / .devcontainer/Dockerfile
- docker-compose.yml
- .github/workflows/ci.yml
- k8s/deployment.yaml, k8s/service.yaml
サーバー(main.go)例
本番運用では liveness/readiness とアプリの実装を一致させることが重要です。以下は /, /healthz, /ready を実装し、優雅なシャットダウンを行う簡易例です。
|
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 |
package main import ( "context" "fmt" "log" "net/http" "os" "os/signal" "time" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello from Go in Docker") }) mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintln(w, "ok") }) mux.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintln(w, "ready") }) port := getEnv("PORT", "8080") srv := &http.Server{ Addr: ":" + port, Handler: mux, } go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("server error: %v", err) } }() quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt) <-quit ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Printf("shutdown error: %v", err) } } func getEnv(key, def string) string { if v := os.Getenv(key); v != "" { return v } return def } |
|
1 2 3 4 |
module example.com/myapp go 1.22 |
開発用と本番用 Dockerfile の設計と注意点
開発用と本番用は目的が異なります。再現性、最小化、セキュリティを考慮したサンプルと、よくあるミスの対処法を示します。
開発用 Dockerfile の例と注意点
開発用はホットリロードやデバッグを優先します。外部ツールのインストールはバージョンをピン留めしてください。GO111MODULE は Go 1.16 以降では不要です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# Dockerfile.dev FROM golang:1.22 AS dev ARG AIR_VERSION=1.41.0 ENV PATH=$PATH:/go/bin WORKDIR /work # go module を使う前提。必要なら GOPROXY を設定する RUN go env -w GOPROXY=https://proxy.golang.org || true # バージョンは必ずセマンティックバージョンでピン留めする RUN go install github.com/cosmtrek/air@${AIR_VERSION} COPY go.mod go.sum ./ RUN go mod download COPY . . EXPOSE 8080 CMD ["air", "-c", ".air.toml"] |
注意点と運用ルールの例:
- go install で @latest を使わないこと。CIで明示的にバージョンを更新するポリシーを定めます。
- ENV GO111MODULE は削除しています。Go 1.16+ ではモジュールがデフォルトです。
- ホットリロード用ボリュームのマウントは OS により書き方が異なります。Bash(macOS/Linux)と PowerShell(Windows)でのコマンド例を示します。
Bash/macOS/Linux の起動例:
|
1 2 3 4 5 6 |
docker build -f Dockerfile.dev -t myapp-dev . docker run --rm -p 8080:8080 \ -v "$(pwd):/work:cached" \ -v go-mod-cache:/go/pkg/mod \ myapp-dev |
PowerShell の起動例(Windows):
|
1 2 3 4 5 6 |
docker build -f Dockerfile.dev -t myapp-dev . docker run --rm -p 8080:8080 ` -v ${PWD}:/work:cached ` -v go-mod-cache:/go/pkg/mod ` myapp-dev |
本番用マルチステージ Dockerfile(CA 証明書と multi-arch 対応)
本番イメージは小さく安全にする一方、HTTPS クライアントや証明書を使う場合は CA 証明書が必要です。scratch ベースは CA を持ちません。distroless を検討するか、builder から証明書をコピーしてください。マルチアーキは buildx の TARGETOS/TARGETARCH を使うのが現実的です。
例:distroless を使う方法(BuildKit 対応、multi-arch)
|
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 |
# syntax=docker/dockerfile:1.4 FROM golang:1.22 AS builder ARG TARGETOS ARG TARGETARCH WORKDIR /src COPY go.mod go.sum ./ RUN --mount=type=cache,target=/go/pkg/mod \ go env -w GOPROXY=https://proxy.golang.org && \ go mod download COPY . . # ビルドは TARGETOS/TARGETARCH を使う RUN --mount=type=cache,target=/root/.cache/go-build \ CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ go build -trimpath -ldflags="-s -w" -o /app ./cmd/server # builder に ca-certificates を入れておき、最終イメージへコピーする RUN apt-get update && apt-get install -y ca-certificates && update-ca-certificates FROM gcr.io/distroless/static:nonroot COPY --from=builder /etc/ssl/certs /etc/ssl/certs COPY --from=builder /app /app USER nonroot EXPOSE 8080 ENTRYPOINT ["/app"] |
scratch を使う場合は builder から証明書を明示的にコピーします。
|
1 2 3 4 5 6 7 |
FROM scratch COPY --from=builder /app /app COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt USER 1000 EXPOSE 8080 ENTRYPOINT ["/app"] |
注意点:
- CGO が必要な場合は静的ビルドが難しくなります。CGO を使うなら debian-slim 等を最終イメージに検討してください。
- マルチアーキビルドは buildx で行い、Dockerfile 側は TARGETOS/TARGETARCH を使ってビルドするのが整合性を保つ方法です。
.dockerignore の扱い
Dockerfile がビルドコンテキストから除外されないように注意してください。例えば "Dockerfile*" を含めると Dockerfile が無視され、docker build が失敗します。次は推奨例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
.git .gitignore node_modules vendor *.log .vscode .DS_Store bin build dist .env |
Dockerfile を除外したい特別な理由がある場合は、個別ファイル名で制御してください。例:不要な補助スクリプトだけを ignore するなど。
Dev Container とローカルワークフロー(Delve とホットリロード)
Dev Container は開発再現性を高めます。Delve によるデバッグは便利ですが権限まわりの扱いが重要です。共有環境での運用や CI での利用は注意してください。
.devcontainer の設定例と注意点
.devcontainer.json の例と、権限に関する注意を示します。runArgs に --cap-add=SYS_PTRACE を付けると Delve が使えますが、セキュリティリスクが伴います。
|
1 2 3 4 5 6 7 8 9 10 11 |
{ "name": "Go Dev", "build": { "dockerfile": "Dockerfile.dev" }, "workspaceFolder": "/work", "extensions": ["golang.go"], "forwardPorts": [8080], "mounts": ["source=go-mod-cache,target=/go/pkg/mod"], "runArgs": ["--cap-add=SYS_PTRACE"], "remoteUser": "vscode" } |
注意点:
- --cap-add=SYS_PTRACE や seccomp の緩和は開発用に限定してください。共有ランナーや本番クラスターでの許可は避けます。
- Kubernetes 上で ptrace を許可するには cluster 側のポリシー緩和が必要です。PSP は廃止されているため、Pod Security Admission や Gatekeeper 等の設定が関与します。
Delve の安全な利用方法
Delve はローカル開発での利用を推奨します。CI や共有環境での有効化は避けるか、限定的に行ってください。
- ローカルのみ:--cap-add=SYS_PTRACE を付与して起動し、作業後にコンテナを破棄する。
- CI 上:可能な限り使わない。代替としてログ、トレース、ユニットテストでの検証を優先する。
- Kubernetes:開発用の専用ネームスペースか、ローカルの kind/minikube などで試す。
Delve の実行例(コンテナ内、開発環境のみ):
|
1 2 |
dlv debug ./cmd/server --headless --listen=:2345 --api-version=2 --accept-multiclient |
BuildKit/buildx/キャッシュ戦略と再現性
ビルド速度と再現性は CI のコストと安定性に直結します。buildx とキャッシュを使い、モジュールキャッシュを明示的に扱います。
buildx とマルチアーキの実践上の注意
buildx を使えば複数プラットフォームのイメージを生成できます。ただし CGO を使用する場合やネイティブライブラリがある場合は、ターゲット向けに適切にツールチェインを用意する必要があります。Dockerfile では TARGETOS / TARGETARCH を使うことで buildx と整合します。
- CGO 無し(CGO_ENABLED=0)の場合は emulation で問題なく動くことが多いです。
- CGO が必要な場合は cross-compile ツールチェインやターゲット実機が必要になることがあります。
GitHub Actions でのモジュールキャッシュ設定
actions/cache の path に ${{ env.GOMODCACHE }} を直接書くと、env が未定義で動作しないことがあります。事前に GOMODCACHE を設定しておくと確実です。例を示します。
|
1 2 3 4 5 6 7 8 9 10 11 |
- name: Determine GOMODCACHE run: echo "GOMODCACHE=$(go env GOMODCACHE)" >> $GITHUB_ENV - name: Cache Go modules uses: actions/cache@v4 with: path: | ~/.cache/go-build ${{ env.GOMODCACHE }} key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} |
こうすることで runner 上の実際の GOMODCACHE をキャッシュ対象にできます。
CI/CD と供給連鎖セキュリティ(SBOM・スキャン・署名・シークレット管理)
CI での SBOM 生成、脆弱性スキャン、署名は供給連鎖セキュリティの中心です。ツールの認証やシークレットの扱いを明確にしておく必要があります。
syft/trivy の実行と認証
syft/trivy でプライベートレジストリのイメージを扱う場合は、事前にレジストリへログインが必要です。CI では docker/login-action で認証を行ってから実行してください。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- name: Login to registry uses: docker/login-action@v2 with: registry: ${{ secrets.REGISTRY }} username: ${{ secrets.REGISTRY_USER }} password: ${{ secrets.REGISTRY_PASS }} - name: Generate SBOM run: syft docker://${{ secrets.REGISTRY }}/myorg/app:${{ github.sha }} -o spdx-json=sbom.json - name: Scan image run: trivy image --exit-code 1 --severity HIGH,CRITICAL ${{ secrets.REGISTRY }}/myorg/app:${{ github.sha }} |
trivy はリモートスキャン時にレジストリ認証が必要です。CI キーは最小権限に限定してください。
cosign の実装例(鍵の扱いと keyless)
cosign での署名は鍵の扱いが重要です。秘密鍵をシークレットに入れてそのままコマンドラインに渡すのは避けます。実運用パターンは主に 2 種類です。
1) シークレットを一時ファイルにして署名(ファイル化してから使用)
|
1 2 3 4 5 6 7 8 9 10 11 12 |
- name: Write cosign key run: | echo "${{ secrets.COSIGN_KEY_BASE64 }}" | base64 --decode > cosign.key chmod 600 cosign.key - name: Sign image with cosign run: cosign sign --key cosign.key ${{ secrets.REGISTRY }}/myorg/app:${{ github.sha }} - name: Remove cosign key run: | shred -u cosign.key || rm -f cosign.key |
注意点:
- secrets には秘密鍵を base64 エンコードして保存すると扱いやすくなります。
- 署名後は必ずファイルを削除し、runner 上に残らないようにします。
2) Keyless(OIDC)を使う方法(推奨)
Keyless は OIDC を使い、短時間のトークンで署名します。GitHub Actions で使う場合はジョブに id-token: write 権限が必要です。keyless によって長期的な秘密鍵管理のリスクを低減できます。
|
1 2 3 4 5 6 7 |
permissions: contents: read id-token: write - name: Sign image keyless run: cosign sign --keyless ${{ secrets.REGISTRY }}/myorg/app:${{ github.sha }} |
必要に応じて sigstore/cosign の公式アクションを利用すると実装が簡単になります。どちらの場合でも、署名鍵の取り扱いとローテーション方針を文書化してください。
シークレット管理の実務例
- CI: GitHub Secrets / OIDC を基本にする。長期鍵はできるだけ Vault 等で管理し、CI では短期トークンを取得して利用する。
- Kubernetes: ExternalSecrets / Secret Store CSI Driver / sealed-secrets 等を使い、クラスタ内での平文 Secret の露出を最小にする。
- ローカル: .env ファイルは必ず .gitignore に入れる。実働環境では Vault などの専用シークレットマネージャを採る。
Kubernetes デプロイと運用チェックリスト
Kubernetes では Pod のセキュリティ設定、ヘルスチェックの整合性、イメージ署名、リソース制限などが重要です。PSP は廃止されているため代替手段を使います。
改訂済み Deployment 例(ヘルスチェックと securityContext)
以下は main.go の /healthz と /ready に合わせた最小限の Deployment 例です。非 root 実行や capability の削減を行っています。
|
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 |
apiVersion: apps/v1 kind: Deployment metadata: name: myapp spec: replicas: 2 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - name: myapp image: registry.example.com/myorg/app:sha-abcdef imagePullPolicy: IfNotPresent ports: - containerPort: 8080 livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 10 periodSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 resources: requests: cpu: "100m" memory: "128Mi" limits: cpu: "500m" memory: "512Mi" securityContext: runAsNonRoot: true runAsUser: 1000 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL imagePullSecrets: - name: regcred |
補足と運用上の要点:
- PodSecurityPolicy は廃止されています。代替として Pod Security Admission(builtin)や Gatekeeper/OPA を検討してください。
- liveness/readiness がアプリ実装と一致していることを必ず確認してください。実装が未整備だと Pod が起動済みと判断されないことがあります。
- イメージ署名の検証は admission controller 等で自動化できます。
運用チェックリストとトラブルシュート
ここでは主要な確認事項と代表的な対処法を示します。
- 非 root 実行を確認する。エラー時は UID/GID のズレを確認する。
- liveness/readiness のエンドポイント実装と Pod の設定を一致させる。
- scratch を使う場合は CA 証明書をコピーするか distroless を使う。HTTPS が動作しないケースを避ける。
- Delve や CAP_SYS_PTRACE は開発環境に限定する。CI/本番では無効化する。
- CGO が原因でビルドが通らない場合は debian-slim 等でビルドするか、クロスビルド用のツールチェインを用意する。
- ログは stdout/stderr に出力し、メトリクスは /metrics で公開するなど標準に従う。
まとめ
ここまでの要点を整理します。各項目は実務テンプレートへ反映してください。
- 開発環境はホットリロードとデバッグを優先し、外部ツールはバージョンをセマンティックにピン留めする。
- 本番イメージはマルチステージで最小化するが、HTTPS を使う場合は CA 証明書の扱いを明示する(scratch のまま放置しない)。
- buildx を使ったマルチアーキは TARGETOS/TARGETARCH を Dockerfile 側で受け取り、CGO 要否を考慮する。
- CI では GOMODCACHE を事前に設定して actions/cache を確実に動かす。syft/trivy はレジストリ認証に注意する。
- cosign は鍵をファイル化して一時的に利用するか、可能なら keyless(OIDC)を使い鍵管理リスクを下げる。
- Kubernetes では PSP の代替で Pod Security Admission や securityContext を使い、liveness/readiness の実装と設定を合わせる。
まずはサンプルリポジトリをクローンして、ローカルの Dev Container と CI を動かしながら上のチェックポイントを一つずつ確認してください。