docker-pro について
docker-pro は、Docker のビルド・実行ファイルを作成、レビュー、更新する開発者向けのスキルです。BuildKit を前提に、安全性と保守性を考慮した設定を案内します。
主な機能
- 01編集前にリポジトリを確認
- 02BuildKit のキャッシュ・シークレットマウントを利用
- 03堅牢なイメージとマルチステージビルドを適用
- 04外部イメージをダイジェストで固定
- 05.dockerignore と Bake ファイルを作成
- 06ローカル開発向け Compose 規約に準拠
活用シーン
- 01アプリ用の Dockerfile を作成
- 02既存のビルドファイルをレビュー・更新
- 03ビルドコンテキストから機密ファイルを除外
- 04ローカルと CI の再現可能なビルドを整理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ bretfisher/ skills --skill docker-proインストール後、Claude Code で/docker-proを入力して読み込まれたか確認します。
詳細
最新の Docker と BuildKit のルール
Docker のビルドまたは実行に関するファイル(Dockerfile、.dockerignore、docker-bake.hcl、compose.yaml)を作成、編集、レビュー、または最新化する際は、このスキルを使用してください。リポジトリに明確に異なる慣例がすでにある場合や、ユーザーが別のトレードオフを明示的に求めている場合を除き、強い推奨方針に基づいて、これらのデフォルトを先回りして適用してください。
これらのルールでは、ビルダーとして BuildKit(最新の Docker Engine と docker buildx のデフォルト)を前提とします。この前提により、以下に示す最新のフロントエンド機能をすべて、レガシービルダー向けの条件分岐なしで使用できます。
作業スタイル
- 書き始める前にリポジトリを調査します。言語/ランタイム、パッケージマネージャー、ロックファイル、ビルド出力ディレクトリ、既存の Docker ファイルを確認し、汎用テンプレートではなく実際の構成に合った内容にします。
- 正しい変更のうち最小のものを優先します。ただし、すでにそのファイルを編集している場合は、レガシーなパターン(compose の
version:、イメージレイヤーへのシークレットの埋め込み、キャッシュマウントなしのapt-getなど)を維持しないでください。 - ベースイメージのタグ、パッケージ名、ポート、ビルドコマンドを、事実であるかのように作り出さないでください。リポジトリから値を確認できない場合は、明確なプレースホルダーを使い、その旨を伝えてください。特にイメージタグは変わるため、存在するか確信がないタグを断定せず、レジストリのカタログで確認するようユーザーに伝えてください。
- 以下の引き継ぎチェックリストを完了し、ユーザーが各変更を保守できるようにしてください。
Dockerfile の構成
よく変更される要素(バージョンや設定)を予測しやすい場所である冒頭にまとめ、ビルドロジックを上から順に読めるようにすると、Dockerfile の保守が容易になります。次の順序に従ってください。
# syntax=docker/dockerfile:1 # 1. frontend directive — MUST be the very first line
ARG NODE_VERSION=22 # 2. global version ARGs — one place to bump bases
ARG ALPINE_VERSION=3.22
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS deps # 3. all FROM stages, each named AS
FROM deps AS build
FROM cgr.dev/chainguard/node:latest AS runtime
# ... stage bodies follow, in build order ...
1. フロントエンドディレクティブを1行目に置く
すべての Dockerfile の冒頭は、次のようにします。
# syntax=docker/dockerfile:1
これにより、ビルド時に Docker Hub から最新の安定版 1.x Dockerfile フロントエンドを取得でき、Dockerfile の機能をエンジンのバージョンから切り離せます。そのため、古い Docker Engine でも最新の構文を認識できます。:1(:1.0 や完全なバージョンではなく)に固定するのは意図的です。v1 系列では破壊的変更がないことが保証されているため、新しい機能や修正を追加作業なしで利用できます。ユーザーがフロントエンド自体のバイト単位での再現性を明示的に必要とする場合に限り、マイナーバージョン(例::1.7)に厳密に固定してください。
必ず1行目に置いてください。コメントより前、ARG より前です。1行目でない場合、BuildKit はこれを無視し、組み込みフロントエンドに警告なくフォールバックします。
2. グローバル ARG でバージョンを一元管理する
ベースイメージのバージョンは、グローバルスコープ(最初の FROM より前)の ARG として宣言し、すべてのバージョンを冒頭の1つのブロックにまとめます。
ARG NODE_VERSION=22
ARG ALPINE_VERSION=3.22
グローバルな ARG は FROM 行で使用できます。ステージ内(たとえば RUN 内)でも使用するには、そのステージ内でデフォルト値を指定せずに ARG NODE_VERSION と再宣言します。グローバル引数はステージ本体には自動的に引き継がれません。この方法なら、バージョンの更新は冒頭の1行を編集するだけで済み、ファイルに手を加えずに --build-arg NODE_VERSION=20 で任意の値をビルド時に上書きできます。
3. すべての FROM ステージを冒頭にまとめ、それぞれに AS を指定する
各ビルドステージの FROM をファイルの冒頭近くにまとめ、明示的な AS <name> を付けて、各ステージの長い本体より前に記述します。FROM 行をまとめることでイメージグラフを一目で把握でき、ベースイメージへの参照も1か所に集約されます。これは上記のバージョン用 ARG とも相性が良い構成です。ステージに名前を付けると、安定した COPY --from=<name> の参照先を使え、ユーザーは --target <name> で単一のステージをビルドできます。
4. 各ステージの冒頭近くに ENV を置く
各ステージの ENV 宣言は、そのステージの冒頭近くにまとめ、RUN の間に散在させないでください。環境変数は、最初に参照されることが多い設定です。まとめておけば見つけやすくなり、キャッシュの無効化も予測しやすくなります(ENV を変更すると、その下のすべてが無効になります)。
5. BuildKit のフロントエンド機能を使用する
BuildKit を前提としているため、従来の代替手段よりも高速、または安全、あるいはその両方である次の機能を優先してください。
# Cache mounts: persist package-manager caches across builds (don't bloat layers)
RUN --mount=type=cache,target=/root/.npm npm ci
# Secret mounts: secrets are available during RUN but never written to a layer
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
# Bind mounts: read context/another stage during RUN without COPYing it in
RUN --mount=type=bind,source=package.json,target=package.json npm ci
# COPY --link: layer is independent of its parent chain → better cache reuse
COPY --link ./dist /app/dist
# Heredocs: multi-line RUN without trailing backslashes
RUN <<EOF
set -eux
apt-get update
apt-get install -y --no-install-recommends curl
rm -rf /var/lib/apt/lists/*
EOF
# ADD --checksum: verify a remote artifact's integrity
ADD --checksum=sha256:24454f8...d68d https://example.com/app.tar.gz /opt/
デフォルトで強化されたベースイメージを使用する
最小限で強化されたベースイメージをデフォルトで使用してください。これらは含まれるパッケージが大幅に少ないため、攻撃対象領域が縮小し、パッチを適用する CVE が減り、イメージも小さくなります。完全な汎用ベースイメージ(Debian/Ubuntu/公式のalpine)を選ぶのは、アプリが実行時に追加のツールを本当に必要とし、強化版では対応できない場合に限り、その場合は理由を説明してください。
無料で利用できる、おすすめのソースは 2 つあります。
- Chainguard Images —
cgr.dev/chainguard/<name>。公開カタログは認証なしで取得できるため、最も手軽なデフォルトの選択肢です。ほとんどのイメージには 2 つの種類があります。:latest-dev— シェルとapkパッケージマネージャーが含まれます。インストール作業が必要なビルドステージで使用します。:latest(およびstatic) — ディストロレスです。シェルもパッケージマネージャーもありません。最終的な実行ステージで使用します。
- Docker Hardened Images(DHI) —
dhi.io/<name>。無料ですが、イメージの取得には Docker アカウントを使ったdocker login dhi.ioが初回のみ必要です。タグはdhi.io/python:3.13やdhi.io/python:3.12-alpine3.22のような形式で、ビルドステージ用にシェルとパッケージマネージャーを追加した-dev版もあります。ユーザーがすでに Docker のエコシステムを利用している場合や、Docker の署名付き SBOM/VEX サプライチェーンメタデータを求めている場合は、DHI を優先してください。
強化イメージを実用的にするマルチステージパターンは、コンパイラーやパッケージのインストールなどの複雑な作業を-devビルドステージで行い、ビルドした成果物だけをCOPY --fromでシェルやパッケージマネージャーがなく、攻撃に利用される余地のないディストロレスの最終ステージにコピーする方法です。
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.24
FROM cgr.dev/chainguard/go:latest-dev AS build
FROM cgr.dev/chainguard/static:latest AS runtime
FROM build AS compile
WORKDIR /src
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
CGO_ENABLED=0 go build -o /app ./cmd
FROM runtime
COPY --from=compile /app /app
ENTRYPOINT ["/app"]
ディストロレスの最終ステージにはシェルがないため、ENTRYPOINT/CMDには exec 形式(["/app"]。/appではありません)を使う必要があり、RUN/apt/apkは機能しません。それが狙いです。イメージによって、また時間の経過によって正確なタグは変わるため、上記のタグは形式の例として扱い、絶対的なものとは見なさないでください。重要な場合は、カタログで確認するようユーザーに伝えてください。
外部イメージはすべてダイジェストで固定する
レジストリから取得するイメージを参照する外部イメージ参照には、FROM行とCOPY --from=<image>の両方で、タグとダイジェストの両方を指定してください。node:24や:latestのようなタグは変更可能です。レジストリはいつでもタグの参照先を新しいコンテンツに変更できるため、同じ Dockerfile のビルドでも異なるイメージが生成される可能性があります。@sha256:...ダイジェストは変更不可能なコンテンツアドレス指定なので、ビルドの再現性を確保し、サプライチェーンの弱点を塞げます(後からプッシュされたものではなく、確認したものとまったく同じバイト列を取得できます)。読み手がイメージの内容を確認できるよう、人が読めるタグもダイジェストと併記してください。
FROM node:24.11.1@sha256:9a2ed90cd91b1f3412affe080b62e69b057ba8661d9844e143a6bbd76a23260f
ダイジェストを取得するには、インストールされていればregctlを優先してください。イメージ全体をプルせずにレジストリへ問い合わせられます。
regctl manifest head node:24.11.1 # prints the digest for the current tag
regctlを利用できない場合は、docker buildx imagetools inspect <image>:<tag>(こちらもイメージ全体のプルは不要)を使うか、イメージをプルしてdocker inspect --format '{{index .RepoDigests 0}}' <image>:<tag>で確認してください。ダイジェストをでっち上げてはいけません。実際のものを解決できない場合は、@sha256:<PIN-ME>という、明確に印を付けたプレースホルダーを残し、置き換えるための正確なコマンドをユーザーに伝えてください。
この方法は、バージョン ARG の一元管理と組み合わせられます。読みやすさのためタグを ARG に入れ、ダイジェストは対応する別の ARG に入れて、参照全体をファイル冒頭の 1 か所にまとめてください。
ARG NODE_VERSION=24.11.1
ARG NODE_DIGEST=sha256:9a2ed90cd91b1f3412affe080b62e69b057ba8661d9844e143a6bbd76a23260f
FROM node:${NODE_VERSION}@${NODE_DIGEST} AS build
意図しない問題を避けるため、次の 2 点を補足します。
- これは外部イメージ URIのみに適用されます。_名前付きの内部ステージ_を参照する
COPY --from=buildにはダイジェストは不要です。ビルド内で解決され、レジストリからプルされるものではありません。 - ダイジェストで固定するとイメージは変更されなくなります。それが目的ですが、固定を更新するまでは上流のセキュリティパッチも取り込まれなくなります。ハッシュを手作業で編集したり、さらに悪いことに「最新の状態を保つ」ためにタグだけの指定へ戻したりするのではなく、他の依存関係と同様に、Dependabot/Renovate の Docker サポートなどの自動化で定期的にダイジェストを更新するよう、ユーザーに伝えてください。
.dockerignoreを必ず作成する
すべてのビルドコンテキストに.dockerignoreを用意してください。ビルダーに送信する内容を減らせるため、ビルドが速くなり、キャッシュの効率も上がります。さらに重要なのは、実際のセキュリティ対策になることです。コンテキスト全体がデーモンに送信されるため、.git、.env、キーを除外すれば、シークレットや履歴がイメージに漏れるのを防げます。
リポジトリに.gitignoreがあれば、その内容から作成を始めます。すでに無視されているnode_modules、ビルド出力、distなども除外されるよう、エントリーをコピーしてから、.gitignoreには含まれないDocker固有のエントリーを追加してください。
# --- copied from .gitignore ---
# (paste the project's .gitignore contents here)
# --- Docker additions ---
.git
.gitignore
.dockerignore
Dockerfile*
compose*.yaml
docker-compose*.yml
docker-bake.hcl
# secrets & local env
.env
.env.*
*.pem
secrets/
# CI / docs / editor / OS cruft
.github/
README.md
.vscode/
.idea/
.DS_Store
.gitは特に重要な追加項目です。サイズが非常に大きいことが多く、完全な履歴(過去にコミットされたシークレットも含む)が含まれています。また、イメージ内で必要になることはほとんどありません。
Bakeファイルを必ず追加する
docker-bake.hclを追加し、長くて内容が変わりやすいdocker buildコマンドラインではなく、再現可能でバージョン管理された設定としてビルドを定義します。Bake(docker buildx bake)はこのファイルを読み込み、一貫したタグ、プラットフォーム、引数を使って、1つまたは複数のターゲットをビルドします。CIからもこのファイルを呼び出すことで、ローカルとCIのビルドを同一に保てます。
variable "TAG" {
default = "latest"
}
group "default" {
targets = ["app"]
}
target "app" {
context = "."
dockerfile = "Dockerfile"
tags = ["myapp:${TAG}"]
# platforms = ["linux/amd64", "linux/arm64"] # uncomment for multi-arch
}
docker buildx bake(defaultグループ)またはdocker buildx bake appでビルドします。実行時に変数を上書きする(TAG=v2 docker buildx bake)か、ターゲットごとに上書きします(--set app.platforms=linux/arm64)。inheritsを使うとベースターゲットの設定を共有でき、matrixを使うと1つのターゲットから複数のバリアントを展開できます。-fを指定せずにdocker buildx bakeを実行すると、compose.yamlがdocker-bake.hclより先に自動検出され、サービスのbuild:ブロックがターゲットに変換される点に注意してください。そのため、リポジトリに両方のファイルがある場合は、CIから意図したファイルを-f docker-bake.hclで指定してください。
ローカル開発にはComposeを使う
Composeファイルがない状態で初回セットアップを行う場合は、依頼されたビルドファイルを完成させ、最終回答でcompose.yamlの追加を提案してください。ローカル開発の範囲をユーザーが管理できるよう、Composeの作成は承認待ちにしてください。ユーザーが応答できない場合は、次回の応答に向けた提案として記載してください。すでにComposeファイルがある場合は、そのファイルを直接最新化してください。
規約:
- ファイル名は
compose.yamlにしてください(Compose Specの標準ファイル名)。docker-compose.ymlは使わないでください。 - トップレベルの
version:キーは使わないでください。 Compose V2では廃止されており、現在は警告が出ます。変更するファイルからは削除してください。 - ソースコードにはバインドマウントよりファイル同期を優先してください。 従来のバインドマウント(
./src:/app/src)は、コンテナ内のパスをホストの構成に結び付け、処理が遅くなったり、権限の問題が発生したりすることがあります。最新のdevelop.watchの仕組みでは、変更されたファイルをコンテナに「同期」できます。これにより処理が速く、クロスプラットフォームで動作し、各パスの変更に対する処理を選択できます。
services:
web:
build: .
ports:
- "3000:3000"
develop:
watch:
# sync: push live source changes into the running container (hot reload)
- action: sync
path: ./src
target: /app/src
ignore:
- node_modules/
# rebuild: dependency manifests / compiled languages need a fresh image
- action: rebuild
path: package.json
# sync+restart: config the process only reads at startup
- action: sync+restart
path: ./config.yml
target: /app/config.yml
docker compose watchで起動します。変更に必要な処理を選んでください。ホットリロード可能なソースにはsync、ロックファイルやコンパイル済みコードにはrebuild、起動時にのみ読み込まれる設定にはsync+restartを使います。
完了時
ユーザーが別のドキュメントを開かずにファイルを保守できるよう、最終的なユーザー向け回答に次の確認事項をすべて含めてください。
- 適用した各デフォルト設定や最新化について、それぞれ「変更 — 理由」の形式で1行ずつ記載してください。フロントエンド、ベースイメージ、ダイジェストの固定、キャッシュマウント、実行時ユーザー、
.dockerignore、Bake、および使用したComposeの各変更を含めてください。理由ではメリットを説明してください。設定の一覧だけでは不十分です。 - 正確なビルドコマンドと実行コマンドに加え、検証結果と未解決のプレースホルダーを含め、ユーザーが作業を再現できるようにしてください。
- 初回セットアップの場合は、上記のCompose追加提案を含め、ユーザーが次のステップを承認できるようにしてください。