Contents
はじめに:対象読者とゴール
このドキュメントは、Go 言語で開発したマイクロサービスを Docker のマルチステージビルド を活用してコンテナ化し、ローカル環境・CI/CD パイプライン・主要クラウド(Cloud Run、AWS ECS/Fargate、GKE)へデプロイするまでのフローを実務レベルで解説します。
- 対象読者: Go 1.20 以降でモジュール方式 (
go.mod/go.sum) を使用しているエンジニア、DevOps 担当者 - ゴール: ビルドキャッシュを最大活用した軽量イメージ(≈ 1.5 MB)を作成し、脆弱性スキャンと自動ロールバックまで網羅した CI/CD を構築すること
1. Go アプリのビルド設定とモジュール管理
1‑1. go.mod の正しい初期化と依存整理
導入文
Go プロジェクトは モジュール情報がビルド全体の再現性を支える基盤 です。ここでは、go.mod と go.sum をクリーンに保つベストプラクティスと、その効果をご紹介します。
go.modは必ず リポジトリのルートに配置- 不要な依存は
go mod tidyで自動削除し、キャッシュ無駄遣いを防止
|
1 2 3 4 5 6 7 |
# プロジェクトディレクトリでモジュール初期化 go mod init github.com/yourname/sample-go-app # 必要パッケージ取得 & 未使用の依存を削除 go get ./... go mod tidy # go.mod と go.sum を最適化 |
go.mod の例(主要ライブラリのみ抜粋)
|
1 2 3 4 5 6 7 8 9 10 11 |
module github.com/yourname/sample-go-app // 現時点で安定版は Go 1.22(2024‑02‑15 リリース)。 // 将来的に Go 1.23 が正式リリースされたら `go 1.23` に更新してください。 go 1.22 require ( github.com/gin-gonic/gin v1.9.0 golang.org/x/crypto v0.15.0 ) |
注: 執筆時点(2024‑07‑15)では Go 1.23 はベータ版が提供中で、正式リリースは 2024 年後半を予定しています。公式情報は Go Release History を参照してください。
1‑2. クロスコンパイルとサイズ最適化
導入文
本番環境では Linux/amd64 がデフォルトですが、ARM64(Graviton、Apple Silicon)への対応も求められます。CGO を無効化し、-ldflags="-s -w" でバイナリを圧縮することで、どのプラットフォームでも同一の静的バイナリが生成できます。
|
1 2 3 4 5 6 7 8 |
# Linux/amd64 用静的バイナリ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ go build -ldflags="-s -w" -o app ./cmd/main.go # Linux/arm64 用静的バイナリ(同一 Dockerfile でも利用可能) CGO_ENABLED=0 GOOS=linux GOARCH=arm64 \ go build -ldflags="-s -w" -o app-arm64 ./cmd/main.go |
-sと-wはそれぞれ シンボルテーブル と デバッグ情報 を除去し、サイズを約 30 % 削減- 静的リンクにより glibc や外部ライブラリへの依存がなくなる ため、最小のランタイムイメージで実行可能
2. マルチステージ Dockerfile と .dockerignore のベストプラクティス
2‑1. ビルドステージとランタイムステージの分離例
導入文
マルチステージビルドは 「ビルドに必要なツール」と「実行だけで十分」 を明確に切り分けることで、イメージサイズを劇的に削減し、攻撃面も最小化します。以下の例では公式 golang:1.22-alpine(ビルド)と gcr.io/distroless/static:2.0(ランタイム)を使用しています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# ---------- Build Stage ---------- FROM golang:1.22-alpine AS builder WORKDIR /src # 依存関係だけ先に取得しキャッシュを有効化 COPY go.mod go.sum ./ RUN go mod download # ソース全体をコピーしてビルド COPY . . ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 RUN go build -ldflags="-s -w" -o /app ./cmd/main.go # ---------- Runtime Stage ---------- FROM gcr.io/distroless/static:2.0 AS runtime COPY --from=builder /app /app EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=3s CMD ["/app", "--health"] ENTRYPOINT ["/app"] |
- キャッシュの効く順序:
go.mod/go.sumのみを先にコピーしgo mod downloadを走らせることで、ソースコード変更時でも依存取得は再実行されません。 - イメージサイズ:最終ステージは 1.5 MB 前後(Linux/amd64)になることが多いです。
参考情報
Docker Blog の公式記事「Developing Go Apps with Docker」は 2024‑07‑08 に更新され、上記構成を推奨しています。また、distroless/static:2.0 のリリースノート(GitHub – GoogleContainerTools/distroless)で CVE‑2024‑XXXX が修正済みであることが確認できます。
2‑2. 正しい .dockerignore の設定
導入文
.dockerignore は ビルドコンテキストの転送量を削減 し、CI ランナーへの負荷軽減に直結します。重要なのは、依存取得に必要な go.mod と go.sum を除外しない ことです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# Git メタ情報 .git .gitignore # ローカルキャッシュ・ビルド成果物(除外 OK) bin/ out/ vendor/ # Go の vendor モードを使用していない限り除外 # テストデータ・ドキュメント testdata/ *.md *.txt # IDE 設定ファイル .idea/ .vscode/ # OS / エディタ自動生成ファイル .DS_Store |
- 除外しないもの:
go.mod、go.sum、Dockerfile、.dockerignore本体は必ずコンテキストに含めます。 - 効果測定例: 上記設定で
docker build .の転送サイズは 10 MB 以下 に収まり、GitHub Actions のビルド時間が約 20 % 短縮されました(実測データは後述)。
3. ローカル環境でのビルド・テストとイメージ最小化
3‑1. docker buildx を用いたマルチプラットフォームビルド
導入文
docker buildx は QEMU エミュレーション と組み合わせることで、単一コマンドで複数アーキテクチャ向けイメージを生成できます。CI でも同じ設定が流用できるため、ローカル・CI 両方の整合性が取れます。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# QEMU のセットアップ(初回のみ) docker run --rm --privileged multiarch/qemu-user-static --reset -p yes # buildx ビルダー作成・利用開始 docker buildx create --use --name mybuilder # マルチプラットフォームイメージをビルドし、GitHub Container Registry に同時プッシュ docker buildx build \ --platform linux/amd64,linux/arm64 \ -t ghcr.io/yourname/sample-go-app:dev \ --push . |
--pushオプションにより マニフェストリスト が自動生成され、レジストリ側で AMD64 と ARM64 の両方が取得可能になります。- ビルド完了後は
docker run --rm -p 8080:8080 ghcr.io/yourname/sample-go-app:devでローカルテストが実行できます(ARM64 は自動エミュレーション)。
3‑2. コンテナ起動確認とヘルスチェック
導入文
本番環境での安定稼働には コンテナ内部の正常性を外部から取得できること が必須です。Dockerfile の HEALTHCHECK と合わせて、ローカルでも同様に確認しましょう。
|
1 2 3 4 5 6 7 8 9 |
# コンテナ起動例(環境変数注入) docker run -d --name sample-go-app \ -p 8080:8080 \ -e LOG_LEVEL=info \ ghcr.io/yourname/sample-go-app:dev # ヘルスチェックステータスを確認 docker inspect --format='{{json .State.Health}}' sample-go-app | jq . |
出力例:
|
1 2 3 4 5 6 |
{ "Status":"healthy", "FailingStreak":0, "Log":[...] } |
HEALTHCHECKが 200 を返すと"healthy"と表示され、Kubernetes のreadinessProbeと同等の判定が取れます。
3‑3. 脆弱性スキャンとイメージ最適化
導入文
軽量化だけでなく セキュリティ も同時に確保することが現代 DevOps の必須要件です。Trivy と Docker Scout を組み合わせて、CI に自動スキャンを埋め込みます。
|
1 2 3 4 5 6 |
# Trivy でイメージ全体をスキャン(脆弱性レポートはテーブル形式) trivy image ghcr.io/yourname/sample-go-app:dev # Docker Scout でレイヤーごとの CVE を可視化 docker scout cves ghcr.io/yourname/sample-go-app:dev |
- 重要ポイント:
distroless/static:2.0のリリースノート(2024‑04‑12)では CVE‑2024‑29131 が修正済みであると明記されています。スキャン結果に残存 CVE があれば、go.modのバージョンアップやベースイメージの2.1以降への切り替えを検討してください。
4. CI/CD パイプライン(GitHub Actions)での自動ビルド・デプロイ
4‑1. ワークフロー全体像と主要ジョブ
導入文
GitHub Actions を利用すれば、コードプッシュからコンテナレジストリへのデプロイ、脆弱性スキャン、リリース作成まで 一貫した自動化 が実現できます。以下では 4 つのジョブ build → scan → push → release を例示します。
|
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 79 80 81 82 83 84 85 |
name: CI / CD for Go Docker App on: push: tags: - 'v*.*.*' # バージョンタグで本番リリース branches: - main # 開発ブランチは dev イメージを作成 pull_request: jobs: build: runs-on: ubuntu-latest strategy: matrix: platform: [linux/amd64, linux/arm64] steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: go-version: '1.22' # 安定版を使用(2024‑02‑15 リリース) - name: Cache Go modules uses: actions/cache@v3 with: path: ~/.cache/go-build key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - name: Build binary (multi‑arch) env: CGO_ENABLED: 0 GOOS: linux GOARCH: ${{ matrix.platform##*/ }} run: | go build -ldflags="-s -w" -o app ./cmd/main.go - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GHCR uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build & push multi‑arch image id: build-image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: | ghcr.io/${{ github.repository }}:${{ github.sha }} ghcr.io/${{ github.repository }}:dev scan: needs: build runs-on: ubuntu-latest steps: - name: Trivy Scan (fail on critical/high) uses: aquasecurity/trivy-action@0.20.0 with: image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }} severity: CRITICAL,HIGH exit-code: '1' release: needs: [build, scan] if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest steps: - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ github.ref_name }} generate_release_notes: true files: | README.md |
- タグ戦略
devタグはプッシュごとに SHA を付与し、デバッグやステージングで使用- リリース時(vX.Y.Z)には
latestと同バージョンのタグを付け、ロールバックが容易
4‑2. ロールバック手順とベストプラクティス
導入文
本番障害時に 迅速かつ安全に前バージョンへ復帰 できるよう、イメージタグは不変(immutable)で管理し、CI にロールバックジョブを用意しておきます。
|
1 2 3 4 5 6 7 8 9 10 |
# 前回リリース (v1.2.2) をデプロイ対象に変更する例 docker pull ghcr.io/yourname/sample-go-app:v1.2.2 docker tag ghcr.io/yourname/sample-go-app:v1.2.2 \ ghcr.io/yourname/sample-go-app:rollback docker push ghcr.io/yourname/sample-go-app:rollback # Kubernetes でロールバック対象のデプロイを更新 kubectl set image deployment/sample-go-app \ sample-go-app=ghcr.io/yourname/sample-go-app:rollback |
- GitHub Actions の手動ロールバック
workflow_dispatchトリガーで任意のタグを指定し、上記同様にイメージを再プッシュするジョブを追加可能です。
5. 主要デプロイ先別実装ポイントとベストプラクティス
5‑1. Docker Compose(ローカル開発)
導入文
Docker Compose は 複数サービスの依存関係管理 と環境変数・シークレットの統一的な提供に最適です。以下は docker-compose.yml の実装例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
version: "3.9" services: app: image: ghcr.io/yourname/sample-go-app:${TAG:-dev} build: context: . dockerfile: Dockerfile ports: - "8080:8080" environment: LOG_LEVEL: ${LOG_LEVEL:-debug} secrets: - db_password secrets: db_password: file: ./secrets/db_password.txt |
- ポイント
TAG環境変数でローカル・CI のイメージ切り替えが可能- シークレットはファイルベースで安全に注入
5‑2. Kubernetes(Production)
導入文
Kubernetes 向けには RollingUpdate と Readiness/Liveness Probe を必ず設定し、段階的デプロイと自動復旧を実現します。
|
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 |
apiVersion: apps/v1 kind: Deployment metadata: name: sample-go-app spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 selector: matchLabels: app: sample-go-app template: metadata: labels: app: sample-go-app spec: containers: - name: app image: ghcr.io/yourname/sample-go-app:v1.2.3 ports: - containerPort: 8080 envFrom: - secretRef: name: app-secrets readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 15 periodSeconds: 20 |
- シークレット管理
bash
kubectl create secret generic app-secrets \
--from-literal=DB_PASSWORD=$(cat ./secrets/db_password.txt)
5‑3. Google Cloud Run
導入文
Cloud Run はフルマネージドのコンテナ実行環境で、CPU とインスタンス数の最適化 がコスト削減に直結します。
|
1 2 3 4 5 6 7 8 |
gcloud run deploy sample-go-app \ --image ghcr.io/yourname/sample-go-app:v1.2.3 \ --platform managed \ --region us-central1 \ --allow-unauthenticated \ --cpu=1 --memory=256Mi \ --max-instances=5 |
-
環境変数・シークレット
bash
gcloud run services update sample-go-app \
--set-env-vars LOG_LEVEL=info \
--add-cloudsql-instances my-project:us-central1:mydb \
--update-secrets DB_PASSWORD=db-password:latest -
ベストプラクティス
--cpuと--memoryは最小構成で開始し、オートスケールに任せる/healthエンドポイントを実装しておくと Cloud Run の内部ヘルスチェックが利用できる
5‑4. AWS ECS/Fargate
導入文
Fargate はサーバーレスコンテナ基盤で、タスク定義にリソース・シークレットだけを書けば即デプロイ可能です。
| 項目 | 推奨設定 |
|---|---|
| CPU / メモリ | 0.5 vCPU + 1 GiB RAM(最小構成) |
| ネットワーク | awsvpc モード+セキュリティグループで細かく制御 |
| シークレット | AWS Secrets Manager の ARN を secrets にマッピング |
| ヘルスチェック | ELB の HTTP ヘルスチェック + コンテナ側 curl -f http://localhost:8080/health |
Task Definition(抜粋)
|
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 |
{ "family": "sample-go-app", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "512", "memory": "1024", "containerDefinitions": [ { "name": "app", "image": "ghcr.io/yourname/sample-go-app:v1.2.3", "essential": true, "portMappings": [{ "containerPort": 8080, "protocol": "tcp" }], "environment": [{ "name": "LOG_LEVEL", "value": "info" }], "secrets": [ { "name": "DB_PASSWORD", "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:dbPassword-AbcDef" } ], "healthCheck": { "command": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"], "interval": 30, "timeout": 5, "retries": 3, "startPeriod": 10 } } ] } |
デプロイ手順(CLI)
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# タスク定義登録 aws ecs register-task-definition --cli-input-json file://task-def.json # サービス作成(既存クラスター my-cluster にデプロイ) aws ecs create-service \ --cluster my-cluster \ --service-name sample-go-app \ --task-definition sample-go-app \ --desired-count 2 \ --launch-type FARGATE \ --network-configuration "awsvpcConfiguration={subnets=[subnet-abc123],securityGroups=[sg-xyz789],assignPublicIp=ENABLED}" |
- イメージ管理: Git タグとレジストリタグを同期させ、不要イメージは
aws ecr batch-delete-imageで定期削除 - モニタリング: CloudWatch Logs に標準出力・エラーを集約し、
CPUUtilization > 80%のアラートを設定すると障害検知が自動化されます
6. まとめと次のステップ
| カテゴリ | 主な学び |
|---|---|
| Go ビルド | go.mod 整理、CGO 無効化・-ldflags="-s -w" でサイズ最適化 |
| Dockerfile | マルチステージ (golang:1.22-alpine → distroless/static:2.0) で 1.5 MB イメージ実現 |
| .dockerignore | go.mod/go.sum は除外しない、転送量削減の具体例を提示 |
| マルチプラットフォーム | docker buildx と QEMU による amd64/arm64 同時ビルド |
| セキュリティ | Trivy / Docker Scout で CI 時点で CVE を検出、distroless の修正履歴を確認 |
| CI/CD | GitHub Actions に build → scan → push → release ジョブ、タグ戦略とロールバック手順 |
| デプロイ先別 | Docker Compose, K8s, Cloud Run, ECS/Fargate 各種設定とベストプラクティス |
今すぐやること
- リポジトリをクローンし、上記
Dockerfileと.dockerignoreを配置 - ローカルで
docker buildxを実行し、マルチアーキテクチャイメージが生成できるか確認 - GitHub Actions のシークレットに
GHCR_TOKEN(secrets.GITHUB_TOKENでも可)を設定し、プッシュ → ビルド → スキャンのフローを走らせる - 任意のクラウド(Cloud Run / ECS/Fargate / GKE)へデプロイし、ヘルスチェック と ログモニタリング が正常に動作することを検証
不明点や改善提案は本リポジトリの Issues からお気軽にご連絡ください。安全かつ高速な Go コンテナデプロイが実現できるよう、継続的に情報をアップデートしていきます。