docker について
コンテナ環境の作成、レビュー、トラブルシューティングを支援する Docker 向けスキルです。Dockerfile、Compose、イメージ最適化、セキュリティ、一般的なアプリケーション構成の実行診断を扱います。
主な機能
- 01Dockerfile の設計とレビュー
- 02Docker Compose の設定
- 03イメージサイズとビルドの最適化
- 04コンテナのセキュリティ強化
- 05ネットワークと実行時の診断
活用シーン
- 01アプリケーションのコンテナ化
- 02Compose のネットワークやボリュームの問題調査
- 03イメージサイズとビルド時間の削減
- 04コンテナのセキュリティ設定の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ marcioaltoe/ skills --skill dockerインストール後、Claude Code で/dockerを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yaml
詳細
Docker エキスパート
使用する場面
- Dockerfile の作成やレビュー
- イメージサイズやビルドパフォーマンスの最適化
- コンテナーのセキュリティ強化
- マルチサービスアプリ向けの Docker Compose のセットアップ
- コンテナーのネットワーク、ボリューム、ランタイムの問題のデバッグ
- アプリケーション(Node.js、Python、Go、Java、Rust)のコンテナー化
対象外(専用スキルを推奨):
- Kubernetes のオーケストレーション → kubernetes expert
- CI/CD パイプライン → github-actions expert
- クラウド固有のコンテナーサービス(ECS、Cloud Run) → devops expert
基本原則
- レイヤーキャッシュ — 変更頻度が低いものから高いものの順にレイヤーを配置します
- セキュリティを最優先 — 非 root ユーザー、攻撃対象領域の最小化、レイヤーにシークレットを含めない
- 最小限のイメージ — ランタイムに必要なものだけを含めます
- 再現性 — バージョンを固定し、
latestタグを避け、ロックファイルを使用します
ベースイメージの選択
推奨順(最も推奨されるものから順):
| ベースイメージ | サイズ | シェル | 用途 |
|---|---|---|---|
cgr.dev/chainguard/*(Wolfi) | 約10~30MB | あり | CVE ゼロを目指す場合、SBOM を含む |
alpine:3.21 | 約7MB | あり | 汎用の最小構成 |
gcr.io/distroless/* | 約2~5MB | なし | 強化された本番環境、デバッグ不要 |
*-slim バリアント | 約70~100MB | あり | Alpine との互換性に問題がある場合 |
scratch | 0MB | なし | 静的バイナリ(Go、Rust) |
ルール:
- 常に正確なバージョンを指定します:
node:alpineではなくnode:22.14.0-alpine3.21 latestは再現性を損なうため、使用しないでください- 実際のランタイム要件に合ったベースイメージを選びます
Dockerfile のベストプラクティス
レイヤーの順序
# 1. Base image + system deps (rarely change)
FROM node:22-alpine
RUN apk add --no-cache dumb-init
# 2. Dependencies (change occasionally)
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
# 3. Application code (changes frequently)
COPY . .
# 4. Metadata + runtime config
USER node
EXPOSE 3000
CMD ["dumb-init", "node", "server.js"]
.dockerignore
ビルドコンテキストを小さくするために、必ず作成します:
.git
node_modules
__pycache__
*.pyc
.vscode
.idea
.DS_Store
*.log
coverage/
.env
.env.local
dist/
build/
README.md
docs/
BuildKit のキャッシュマウント
依存関係のインストールを高速化します:
# Node.js
RUN --mount=type=cache,target=/root/.npm \
npm ci
# Python
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
# Go
RUN --mount=type=cache,target=/go/pkg/mod \
go build -o /app .
DOCKER_BUILDKIT=1 または Docker Desktop で有効にします(デフォルトで有効です)。
マルチステージビルドのパターン
Node.js
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM node:22-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:22-alpine AS runtime
RUN addgroup -g 1001 -S app && adduser -S app -u 1001
WORKDIR /app
COPY --from=build --chown=app:app /app/dist ./dist
COPY --from=deps --chown=app:app /app/node_modules ./node_modules
COPY --chown=app:app package.json ./
USER app
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]
Python
FROM python:3.13-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
FROM python:3.13-slim AS runtime
RUN useradd -r -u 1001 app
WORKDIR /app
COPY --from=build /install /usr/local
COPY --chown=app:app . .
USER app
EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Go
FROM golang:1.24-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app .
FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /app /app
USER 65534:65534
EXPOSE 8080
ENTRYPOINT ["/app"]
Java
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY . .
RUN ./gradlew build --no-daemon
FROM eclipse-temurin:21-jre-alpine AS runtime
RUN addgroup -g 1001 -S app && adduser -S app -u 1001
WORKDIR /app
COPY --from=build --chown=app:app /app/build/libs/*.jar app.jar
USER app
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD wget -qO- http://localhost:8080/actuator/health || exit 1
CMD ["java", "-jar", "app.jar"]
Rust
FROM rust:1.84-alpine AS build
RUN apk add --no-cache musl-dev
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src
COPY src ./src
RUN cargo build --release
FROM scratch
COPY --from=build /app/target/release/app /app
USER 65534:65534
EXPOSE 8080
ENTRYPOINT ["/app"]
セキュリティ強化
非 root ユーザー
# Alpine
RUN addgroup -g 1001 -S app && adduser -S app -u 1001 -G app
USER app
# Debian/Ubuntu
RUN groupadd -g 1001 app && useradd -r -u 1001 -g app app
USER app
# Distroless/scratch (numeric only)
USER 65534:65534
シークレットの管理
# BuildKit secrets (never stored in layers)
RUN --mount=type=secret,id=api_key \
API_KEY=$(cat /run/secrets/api_key) && \
./configure --api-key="$API_KEY"
# Build with secret
docker build --secret id=api_key,src=./api_key.txt .
絶対に行わないでください:
ENV API_KEY=secret123 # Visible in image history
COPY .env /app/.env # Baked into layer
ARG PASSWORD=hunter2 # Visible in build history
ランタイムの強化
docker run \
--user 1001:1001 \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--read-only \
--tmpfs /tmp:noexec,nosuid \
--security-opt="no-new-privileges:true" \
--memory="512m" \
--cpus="1.0" \
my-image
脆弱性スキャン
# Docker Scout
docker scout quickview my-image
docker scout cves my-image
# Trivy
trivy image my-image
# Grype
grype my-image
Docker Compose
本番環境向けパターン
services:
app:
build:
context: .
target: runtime
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- frontend
- backend
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.5"
memory: 256M
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
db:
image: postgres:17-alpine
environment:
POSTGRES_DB_FILE: /run/secrets/db_name
POSTGRES_USER_FILE: /run/secrets/db_user
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_name
- db_user
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
command: redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # No external access
volumes:
postgres_data:
redis_data:
secrets:
db_name:
file: ./secrets/db_name.txt
db_user:
file: ./secrets/db_user.txt
db_password:
file: ./secrets/db_password.txt
ネットワークの分離
networks:
frontend: # Web-facing services
backend:
internal: true # Database, cache — no external access
services:
proxy:
networks: [frontend]
api:
networks: [frontend, backend]
db:
networks: [backend] # Only reachable from api
開発環境と本番環境のオーバーライド
# compose.yml (base)
services:
app:
build: .
# compose.override.yml (dev, loaded automatically)
services:
app:
build:
target: development
volumes:
- .:/app
- /app/node_modules
ports:
- "9229:9229" # Debug
environment:
- NODE_ENV=development
command: npm run dev
# compose.prod.yml
services:
app:
build:
target: runtime
environment:
- NODE_ENV=production
restart: unless-stopped
# Dev (auto-loads compose.override.yml)
docker compose up
# Prod
docker compose -f compose.yml -f compose.prod.yml up -d
イメージの最適化
サイズ削減の手法
- マルチステージビルド — ビルドツールをイメージに含めません
- 最小限のベースイメージ — Alpine または distroless を使用します
- RUN コマンドをまとめる — 同じレイヤー内でクリーンアップします
- 必要なものだけをコピーする — 必要な成果物だけをコピーします
# Bad — 3 layers, cleanup doesn't reduce size
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# Good — 1 layer, cleanup is effective
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
CMD:exec 形式と shell 形式
CMD ["node", "server.js"] # Exec form — PID 1, receives signals directly
CMD node server.js # Shell form — spawns /bin/sh, signal issues
常に exec 形式を優先してください。
開発ワークフロー
ホットリロードのセットアップ
services:
app:
build:
target: development
volumes:
- .:/app # Source code
- /app/node_modules # Prevent overwrite
ports:
- "3000:3000"
- "9229:9229" # Debug port
environment:
- NODE_ENV=development
- DEBUG=app:*
command: npm run dev
デバッグ
services:
app:
# Node.js inspect
command: node --inspect=0.0.0.0:9229 server.js
ports:
- "9229:9229"
# Python debugpy
# command: python -m debugpy --listen 0.0.0.0:5678 main.py
# ports:
# - "5678:5678"
プラットフォーム固有の設定
Linux
// /etc/docker/daemon.json
{
"userns-remap": "default",
"storage-driver": "overlay2",
"live-restore": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
- セキュリティを強化するには、ユーザー名前空間の再マッピングを使用します
- 本番環境では SELinux/AppArmor プロファイルを設定します
overlay2ストレージドライバーを使用します
macOS
volumes:
- ./src:/app/src:delegated # Better write performance
- ./build:/app/build:cached # Container writes cached
- Docker Desktop に十分なリソースを割り当てます
- バインドマウントのパフォーマンス向上には
:delegated/:cachedを使用します - ファイル同期には
mutagenまたはdocker compose watchを検討します - ARM(Apple Silicon)向けにマルチプラットフォームビルドを行います
Windows
- 最適なパフォーマンスを得るため、WSL2 バックエンドを優先します
- 改行コードに対処するため、
.gitattributesに* text=auto eol=lfを追加します - Dockerfile と compose ファイルではスラッシュを使用します
- Docker Desktop の設定でドライブが共有されていることを確認します
- ファイル権限の違いに注意します
# .gitattributes — prevent CRLF issues in containers
* text=auto eol=lf
*.sh text eol=lf
Dockerfile text eol=lf
本番環境のチェックリスト
- ベースイメージのバージョンを固定している(
latestを使用していない) - マルチステージビルドでビルド環境とランタイム環境を分離している
- 非 root ユーザーとして実行している
- レイヤーや ENV にシークレットが含まれていない
-
.dockerignoreを設定している - ヘルスチェックを実装している
- リソース制限(CPU、メモリ)を設定している
- ログローテーションを設定している
- 脆弱性スキャンに合格している
- シグナルを適切に処理している(exec 形式の CMD、または
dumb-init/tini) - 可能な場合は読み取り専用ファイルシステムを使用している
- ケーパビリティを削除している(
--cap-drop=ALL)
アンチパターン
| しないこと | 代わりにすること |
|---|---|
| root として実行する | USER 1001 または名前付きユーザーを使用する |
latest タグを使用する | バージョンを正確に固定する |
--privileged フラグを使用する | 必要なものだけ --cap-add を指定する |
| Docker ソケットをマウントする | Docker-in-Docker または代替手段を使用する |
| ENV/ARG にシークレットをハードコードする | BuildKit シークレットまたは実行時マウントを使用する |
| ヘルスチェックを省略する | Dockerfile または Compose に HEALTHCHECK を設定する |
| リソース制限を無視する | memory と cpus の制限を設定する |
| コンテキスト全体をコピーする | .dockerignore を使用する |
| 不要なパッケージをインストールする | --no-install-recommends を使用する |
| CMD にシェル形式を使用する | exec 形式 ["cmd", "arg"] を使用する |
診断
ビルドが遅い
- レイヤーの順序を確認する(ソースより先に依存関係)
- BuildKit を有効にする(
DOCKER_BUILDKIT=1) - パッケージマネージャーにキャッシュマウントを使用する
.dockerignoreを確認する(ビルドコンテキストが大きすぎませんか?)
イメージが大きい
- マルチステージビルドを使用する
- Alpine または distroless に切り替える
- RUN コマンドをクリーンアップ処理とまとめる
docker history <image>を確認して大きなレイヤーを特定する
ネットワークの問題
- DNS 解決のためのサービス名を確認する
- Compose のネットワーク割り当てを確認する
docker network inspectを使用してデバッグする- 依存サービスの起動前にヘルスチェックが成功することを確認する
コンテナーのクラッシュ
- ログを確認する:
docker logs <container> - シグナル処理を確認する(PID 1 が SIGTERM を処理する必要があります)
- リソース制限を確認する(OOM による強制終了)
- ヘルスチェックのエンドポイントが存在することを確認する
コメント
Markdown 対応