Contents
1️⃣ 前提条件とインストール確認
Docker の導入が正しく行われているか
|
1 2 |
docker --version # → Docker version 24.0.5, build ... |
上記コマンドでバージョン情報が表示されれば、ローカル環境はすぐにコンテナ開発へ移行できます。Docker Desktop/Docker Engine のインストール手順は公式ドキュメントを参照してください(Windows・macOS・Linux 共通)。
2️⃣ 公式 Golang イメージの選定と .dockerignore の活用
2-1. ベースイメージは何を選ぶべきか
| イメージ | 主な特徴 | 推奨理由 |
|---|---|---|
golang:1.22-alpine |
Alpine Linux 上の公式 Go 1.22 ビルダー。サイズ ≈ 210 MB(2024‑12 時点、Docker Hub の manifest list に基づく) |
アーカイブが小さく、Alpine のパッケージは最小限に抑えられるため攻撃対象が限定される。公式サポートなので脆弱性情報も迅速に提供される |
golang:1.22 (Debian) |
Debian 系統のフルイメージ。サイズ ≈ 800 MB | ビルドツールやローカルパッケージが豊富だが、不要なものまで含むため本番ランタイムには不向き |
| 非公式・古いバージョン | メンテナンスが止まっているケースが多い | セキュリティパッチが提供されず、脆弱性リスクが高まる |
ポイント:実際のサイズは
docker pull golang:1.22-alpine後にdocker imagesで確認できます。30 MB と表記されている情報は古い Alpine のイメージ(Go ツールチェーン未含)です。
2-2. ビルドコンテキストを削減する .dockerignore
|
1 2 3 4 5 6 7 8 9 10 11 |
# .dockerignore (プロジェクトのルートに配置) .git .gitignore node_modules/ vendor/ *.log Dockerfile* docker-compose.yml .idea/ *.md |
上記設定で Docker が転送するデータは数十 KB にまで削減でき、ビルド時間とネットワーク負荷が大幅に改善します。
3️⃣ マルチステージ Dockerfile の構成
3-1. ビルドステージ(依存取得とキャッシュ活用)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# ---------- Build Stage ---------- FROM golang:1.22-alpine AS builder # 必要なツールだけをインストール (git は go.mod の取得に必要) RUN apk add --no-cache git WORKDIR /src # 依存関係だけ先にコピーし、キャッシュ層を確保 COPY go.mod go.sum ./ RUN go mod download # ← ここがキャッシュ対象になる # ソースコード全体をコピーしてビルド COPY . . RUN CGO_ENABLED=0 GOOS=linux \ go build -ldflags="-s -w" -o /app/main . |
go.modとgo.sumのみを先にコピーすることで、ソース変更があっても依存解決レイヤーは再利用されます。- ビルド時の一時パッケージ(git)は ビルダーだけ にインストールし、次のステージでは削除します。
3-2. ランタイムステージ(最小権限・サイズ最適化)
|
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 |
# ---------- Runtime Stage ---------- FROM alpine:3.19 AS runtime # 必要最低限の証明書だけインストール (HTTPS 通信がある場合) RUN apk add --no-cache ca-certificates && update-ca-certificates # 非 root ユーザーを作成し、権限を最小化 ARG USER=appuser ARG UID=10001 RUN addgroup -g ${UID} ${USER} \ && adduser -D -u ${UID} -G ${USER} ${USER} WORKDIR /app # ビルダーからバイナリだけコピー COPY --from=builder /app/main . # ファイル所有者を変更して実行権限を付与 RUN chown ${UID}:${UID} ./main && chmod 755 ./main # 以降は非 root ユーザーで実行 USER ${UID} EXPOSE 8080 VOLUME ["/app/config"] # 必要に応じて設定ファイルだけをマウント # ルートファイルシステムを read‑only にする (セキュリティ強化) ENTRYPOINT ["./main"] |
セキュリティ上のポイント
| 項目 | 内容 |
|---|---|
| 非 root ユーザー | USER ディレクティブで UID 10001 の専用ユーザーを使用。コンテナ内の権限昇格が不可能になる |
| 最小パッケージ | ランタイムは alpine:3.19 + ca-certificates のみ。不要なシェルやビルドツールは含まれない |
| 読み取り専用ファイルシステム | docker run --read-only … オプションと併せて使用すると、コンテナ内部への書き込みは明示的に許可したボリュームだけになる |
| Capabilities の削減 | --cap-drop ALL で不要な Linux ケーパビリティを除外(Delve デバッグ時は例外) |
3-3. 完成イメージのサイズ目安
- ビルダー段階:≈ 210 MB
- ランタイム段階:約 30 MB(バイナリと ca‑certificates のみ)
実際に docker images my-go-app とすれば、最終イメージのサイズが確認できます。
4️⃣ ビルド・デプロイのベストプラクティス
4-1. Docker Build の推奨コマンド
|
1 2 3 4 5 |
docker build \ --pull \ # 常に最新のベースイメージを取得 --rm \ # 中間コンテナを自動削除 -t my-go-app:1.0.0 . # バージョンタグは必ず付与 |
--no-cacheは頻繁に変更しない限り使用しない方がキャッシュ効果でビルド時間を短縮できます。- CI/CD パイプラインでは
docker buildx bake --pushでマルチプラットフォームイメージも同時に生成可能です。
4-2. Docker Run の安全な起動例
|
1 2 3 4 5 6 7 8 9 |
docker run -d \ --name go-app \ -p 8080:8080 \ -e LOG_LEVEL=info \ --read-only \ # ルートFS を読み取り専用に --cap-drop ALL \ # 不要な権限をすべて除去 --user 10001:10001 \ # Dockerfile と同じ UID/GID を明示 my-go-app:1.0.0 |
--read-onlyと--cap-drop ALLにより、攻撃者がコンテナ内で権限昇格を試みても実行できる操作は極めて限定的です。- 必要な書き込み領域(ログや一時ファイル)は
-v $(pwd)/data:/app/data:rwのようにボリュームで明示的にマウントします。
4-3. イメージサイズ削減テクニック
| 手法 | 効果 |
|---|---|
ビルダーから不要パッケージを削除 (apk del) |
約 10‑15 MB の削減 |
--squash(Docker 23.0+)でレイヤー統合 |
最大 30 % のサイズ低減 |
strip コマンドでバイナリのシンボル情報除去 (go build -ldflags="-s -w") |
バイナリ自体が約 20 % 小さくなる |
実践例
dockerfile
RUN apk add --no-cache git && \
go mod download && \
apk del git # ビルダーから git を除去
5️⃣ ローカルデバッグ環境の構築(Docker‑Compose & VS Code Remote Containers)
5-1. docker-compose.yml の最小構成
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
version: "3.9" services: app: build: . ports: - "8080:8080" # アプリ公開ポート - "2345:2345" # Delve デバッグ用ポート volumes: - .:/src # ソースコードをリアルタイムで反映 environment: LOG_LEVEL: debug command: ["dlv", "exec", "/app/main", "--headless", "--listen=:2345", "--api-version=2"] security_opt: - no-new-privileges:true # コンテナ内で権限昇格を防止 |
commandに Delve の起動オプションを直接記述することで、docker compose upだけでデバッグ可能です。security_opt: no-new-privileges:trueは Linux カーネル機能で、プロセスが新しい特権を取得できなくします。
5-2. VS Code Remote‑Containers 設定(devcontainer.json)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
{ "name": "Go (Docker) Development", "dockerComposeFile": ["../docker-compose.yml"], "service": "app", "workspaceFolder": "/src", "remoteUser": "root", // コンテナ内部での初期権限は root → 後段階で non‑root に切り替える "features": { "ghcr.io/devcontainers/features/go:1": {} }, "runArgs": [ "--cap-add=SYS_PTRACE", // Delve 用に ptrace を許可 "-e", "GOFLAGS=-mod=readonly" ], "forwardPorts": [8080, 2345], "postCreateCommand": "go mod tidy", "customizations": { "vscode": { "extensions": ["golang.go"] } }, "shutdownAction": "none" } |
forwardPortsにより VS Code のデバッグ UI が自動的にホスト側ポートへ接続。runArgsの--cap-add=SYS_PTRACEは Delve がプロセスをトレースするために必須ですが、デバッグ以外の本番環境では除去すべきです。
外部参照について:上記設定は Qiita 記事「Go アプリを Docker でデバッグ」等で紹介されている手順と同等ですが、本稿内に全コードと解説を掲載しているため、リンク先の内容確認が不要です。
6️⃣ まとめ & 次のアクション
| 項目 | 実施すべきこと |
|---|---|
| Docker の導入 | docker --version が表示されたら次へ |
| ベースイメージ選定 | golang:1.22-alpine(≈210 MB) を使用し、.dockerignore でコンテキストを最小化 |
| マルチステージ Dockerfile | ビルダーとランタイムを分離し、非 root ユーザー・read‑only FS・capability 削減を実装 |
| ビルドコマンド | docker build --pull --rm -t my-go-app:1.0.0 . を CI に組み込む |
| デプロイ | docker run 時に --read-only、--cap-drop ALL、--user オプションを必ず付与 |
| サイズ最適化 | ビルダーの一時パッケージ削除と --squash の活用 |
| ローカルデバッグ | docker‑compose + VS Code Remote Containers で即座にブレークポイント設定可能な環境を構築 |
| 運用フェーズ | イメージの定期スキャン(Trivy 等)と、ベースイメージ更新時の CI 再ビルドを自動化 |
これらを順に実装すれば、軽量・高速・最小権限 の Go コンテナが完成し、開発から本番まで一貫した安全性を確保できます。ぜひ本稿の手順をローカル環境で試し、CI/CD パイプラインへ組み込んでみてください。