Contents
1️⃣ プロジェクト構成と初期セットアップ
1‑1 ディレクトリ構成例
|
1 2 3 4 5 6 7 8 |
my-go-app/ ├── cmd/ # エントリポイント (main.go) │ └── main.go ├── go.mod # モジュール宣言(go 1.22 を明示) ├── Dockerfile # マルチステージビルド定義 ├── .dockerignore # ビルドに不要なファイル除外 └── docker-compose.yml # ローカル開発用 Compose 定義 |
go.modの作成
bash
go mod init github.com/yourname/my-go-app
go get ./... # 必要な依存を取得
go.mod の先頭行は必ず go 1.22 と書きましょう。
.dockerignoreに入れるべきパターン(不要ファイルがビルドコンテキストに混ざらないようにします)
gitignore
# ビルド成果物・テストデータは除外
/bin/
*.test
# module 管理ならベンダーは不要
/vendor/
# Git 関連ファイル
.git
.gitignore
# IDE の設定ファイル
.vscode/
ポイント
.dockerignoreが正しく機能すれば、Docker ビルドが高速化し CI/CD の安定性も向上します。
2️⃣ マルチステージ Dockerfile で「できるだけ」小さなイメージを作る
2‑1 ベースイメージの選択と書き方
| ビルドステージ | 推奨ベースイメージ |
|---|---|
| コンパイル | golang:1.22-alpine(Alpine の軽量パッケージマネージャが便利) |
| ランタイム | scratch(完全空のイメージ)or alpine:3.20-slim(CA 証明書等が必要な場合) |
2‑2 Dockerfile 全体例
|
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 |
# ---------- ビルドステージ ---------- FROM golang:1.22-alpine AS builder WORKDIR /app # go.mod と go.sum を先にコピーして依存解決だけをキャッシュ COPY go.mod go.sum ./ RUN go mod download # ソースコード全体をコピー COPY . . # 静的リンク+サイズ削減フラグでビルド # CGO_ENABLED=0 にすると純粋な静的バイナリになるが、 # 依存ライブラリがある場合はサイズが数十 MB になることもある点に注意。 RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ go build -ldflags="-s -w" -o /app/bin/app ./cmd # ---------- ランタイムステージ ---------- # 1) 完全に最小化したい場合は scratch を使用 FROM scratch AS final COPY --from=builder /app/bin/app /app ENTRYPOINT ["/app"] # 2) Alpine slim が必要な場合(TLS 等が必要なケース): # FROM alpine:3.20-slim AS runtime # RUN apk add --no-cache ca-certificates # COPY --from=builder /app/bin/app /usr/local/bin/app # ENTRYPOINT ["app"] |
静的リンクとサイズの実際
CGO_ENABLED=0で生成されるバイナリは 純粋に静的 ですが、Go の標準ライブラリだけでも数 MB 程度になります。- 外部 C ライブラリ(例: SQLite, libpq)を使用すると、リンク時にそれらのオブジェクトコードがバイナリに埋め込まれ、10 MB 以上になることもあります。
scratchベースは「空」なので実際の最終サイズは バイナリ本体 + 必要最低限のメタデータ に依存します。したがって「5 MB 未満」という表現はあくまで 典型的な純粋 Go バイナリ の目安であり、環境や依存関係により前後することをご留意ください。
3️⃣ ビルドキャッシュと不要ファイルの除去
3‑1 キャッシュを有効にするレイヤー設計
go.mod/go.sumのみを最初にコピー → 依存取得だけがキャッシュ対象- ソースコード全体は後段でコピー → コード変更時のみ再ビルド
3‑2 ビルダー内でテストバイナリや中間生成物を削除
|
1 2 3 4 5 |
# テスト実行後にテスト用バイナリが残らないようにする例 RUN go test ./... && \ rm -f $(go env GOBIN)/*_test && \ go build -ldflags="-s -w" -o /app/bin/app . |
4️⃣ ローカル開発環境の構築:Docker Compose と VS Code Dev Containers
4‑1 docker-compose.yml の基本設定
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
version: "3.9" services: app: build: context: . target: final # 本番イメージをそのまま使用 ports: - "8080:8080" environment: - ENV=development dev: image: golang:1.22-alpine working_dir: /workspace command: ["air"] volumes: - .:/workspace:cached # macOS/Windows のファイル共有速度向上 ports: - "8080:8080" environment: - AIR_WATCH_EXCLUDE=.git,.vscode |
devサービスは Air(コード変更を検知して自動ビルド)を使うので、ローカルでのフィードバックが高速です。cachedオプションは Docker Desktop のパフォーマンス向上に有効です。
4‑2 VS Code Dev Containers 設定 (.devcontainer/devcontainer.json)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "name": "Go 1.22 DevContainer", "dockerComposeFile": ["../docker-compose.yml"], "service": "dev", "workspaceFolder": "/workspace", "remoteUser": "root", "extensions": [ "golang.go", "ms-azuretools.vscode-docker" ], "postCreateCommand": "go mod tidy && air -v" } |
ポイント
dockerComposeFileに Compose ファイルをそのまま指定できるため、開発用と本番用の設定が二重管理になるリスクを回避できます。
5️⃣ トラブルシューティングとベストプラクティス
| エラー例 | 主な原因 | 推奨対処 |
|---|---|---|
go.mod: no such file or directory |
WORKDIR がプロジェクトルートとずれている |
ビルドステージ冒頭で WORKDIR /app を統一し、.dockerignore で除外したファイルが無いか確認 |
air: command not found |
Air が $GOPATH/bin に入っていて PATH が通っていない |
Dockerfile の dev ステージに ENV PATH=$PATH:/go/bin を追加、またはシンボリックリンクを作成 |
| ポート 8080 が競合している | ホスト側で同じポートが別プロセスに占有されている | Compose の ports を "0.0.0.0:8081:8080" に変更し、VS Code 側のデバッグ設定も合わせる |
5‑1 ビルドログを詳細に見るコマンド
|
1 2 |
docker build --progress=plain . |
レイヤーごとのキャッシュヒット/ミスが平文で出力され、原因特定が容易になります。
6️⃣ CI/CD パイプラインへの組み込み:GitHub Actions
6‑1 ワークフロー全体像
|
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 |
name: Docker Build & Push on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: write # GitHub Packages にプッシュする場合必要 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} # GitHub Packages へプッシュしたい場合は以下を有効化 # - name: Log in to GHCR # uses: docker/login-action@v3 # with: # registry: ghcr.io # username: ${{ github.actor }} # password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: | yourdockerhubuser/my-go-app:latest ghcr.io/${{ github.repository_owner }}/my-go-app:${{ github.sha }} cache-from: type=registry,ref=yourdockerhubuser/my-go-app:cache cache-to: type=registry,ref=yourdockerhubuser/my-go-app:cache,mode=max |
- マルチプラットフォーム対応:
linux/amd64,linux/arm64を指定すれば、Apple Silicon でも同一イメージを取得可能。 - ビルドキャッシュの再利用:自レジストリに
cacheタグで保存したレイヤーは次回以降のジョブでインクリメンタルに使用できます。
📌 最終まとめ
| 項目 | 重要ポイント |
|---|---|
| プロジェクト構成 | main.go, go.mod (go 1.22), Dockerfile, .dockerignore を明確に分離し、.dockerignore で不要ファイルを除外するだけでビルドが高速化 |
| マルチステージ Dockerfile | golang:1.22-alpine → scratch(または alpine:slim)の構成で、静的リンク と -ldflags="-s -w" によりバイナリサイズを最小化。ただし外部 C ライブラリや CGO の有無により実際のサイズは変動する点に注意 |
| キャッシュ設計 | go.mod/go.sum の先行コピーで依存取得だけをキャッシュ対象にし、ソース変更時のみ再ビルド。ビルダー内でテストバイナリや一時ファイルを削除してレイヤー肥大化を防止 |
| ローカル開発 | Docker Compose と VS Code Dev Containers の併用で、本番ビルドと同一のコードベースからホットリロード環境が構築でき、チーム全員が統一された開発体験を得られる |
| トラブルシューティング | WORKDIR・PATH・ポート設定の相違が多くのエラー原因。ビルドログ (--progress=plain) でレイヤーキャッシュ状態を確認すると問題解決が迅速になる |
| CI/CD 自動化 | GitHub Actions の docker/build-push-action@v5 と BuildKit キャッシュを活用すれば、プッシュごとにマルチアーキテクチャ対応イメージが自動生成・配布可能 |
これらのベストプラクティスに沿ってプロジェクトを構築すれば、Go 1.22 の最新機能を活かしつつ、軽量で安全な Docker イメージを本番環境へシームレスにデプロイできるようになります。ぜひ実際のリポジトリで試してみてください。