docker について
ローカル開発、CI/CD、デプロイに携わる開発者向けの Docker コンテナ化ガイドです。例を交えて、イメージ構築、複数コンテナ構成、セキュリティ、デバッグ、本番運用を説明します。
主な機能
- 01Dockerfile とイメージ構築の基本
- 02Compose による複数コンテナ構成
- 03マルチステージイメージ構築
- 04コンテナのセキュリティとリソース制限
- 05コンテナのデバッグ手法
活用シーン
- 01一貫性のあるローカル開発環境の構築
- 02再現可能な CI/CD ワークフローの作成
- 03デプロイ用サービスのパッケージ化
- 04統合テスト用の一時データベース実行
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bobmatnyc/ claude-mpm-skills --skill dockerインストール後、Claude Code で/dockerを入力して読み込まれたか確認します。
同梱ファイルmetadata.json
詳細
Docker コンテナ化スキル
概要
Docker は、アプリケーションとその依存関係を分離されたポータブルな単位にパッケージ化するコンテナ化を提供します。コンテナによって開発、テスト、本番環境間の一貫性が保たれ、「自分のマシンでは動く」問題を解消できます。
使用する場面
- ローカル開発:チームメンバー間で一貫した開発環境を実現します
- CI/CD パイプライン:再現可能なビルドおよびテスト環境を実現します
- マイクロサービス:個別にスケーリングできる分離されたサービスを実現します
- 本番環境へのデプロイ:クラウドプロバイダーを問わず、アプリケーションをポータブルにします
- データベース/サービスのテスト:統合テスト用の一時的なデータベースを利用できます
- レガシーアプリケーションの分離:互換性のない依存関係を並行して実行できます
クイックスタート
1. Dockerfile を作成する
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
2. イメージをビルドする
docker build -t myapp:1.0 .
3. コンテナを実行する
docker run -p 3000:3000 myapp:1.0
基本概念
イメージとコンテナ
- イメージ:アプリケーションコード、ランタイム、依存関係を含む読み取り専用のテンプレートです
- コンテナ:書き込み可能なレイヤーを持つ、イメージの実行中のインスタンスです
- レジストリ:イメージを保存する場所です(Docker Hub、GitHub Container Registry)
レイヤーとキャッシュ
Dockerfile の各命令はレイヤーを作成します。Docker は変更されていないレイヤーをキャッシュし、ビルドを高速化します。
# GOOD: Dependencies change less frequently than code
FROM python:3.11-slim
COPY requirements.txt .
RUN pip install -r requirements.txt # Cached unless requirements.txt changes
COPY . . # Rebuild only when code changes
# BAD: Invalidates cache on every code change
FROM python:3.11-slim
COPY . . # Changes frequently
RUN pip install -r requirements.txt # Reinstalls on every build
ボリューム
コンテナを再起動しても保持される永続データストレージです。
# Named volume (managed by Docker)
docker run -v mydata:/app/data myapp
# Bind mount (host directory)
docker run -v $(pwd)/data:/app/data myapp
# Anonymous volume (temporary)
docker run -v /app/data myapp
ネットワーク
コンテナは Docker ネットワークを介して通信します。
# Create network
docker network create mynetwork
# Run containers on network
docker run --network mynetwork --name db postgres
docker run --network mynetwork --name app myapp
# App can connect to db using hostname "db"
Dockerfile の基本
基本的な命令
# Base image
FROM node:18-alpine
# Metadata
LABEL maintainer="[email protected]"
LABEL version="1.0"
# Set working directory
WORKDIR /app
# Copy files
COPY package*.json ./
COPY src/ ./src/
# Run commands (creates layer)
RUN npm ci --only=production
# Set environment variables
ENV NODE_ENV=production
ENV PORT=3000
# Expose ports (documentation only)
EXPOSE 3000
# Default command
CMD ["node", "src/server.js"]
# Alternative: ENTRYPOINT (not overridden by docker run args)
ENTRYPOINT ["node"]
CMD ["src/server.js"] # Default args for ENTRYPOINT
キャッシュ効率を高める命令の順序
# 1. Base image (rarely changes)
FROM python:3.11-slim
# 2. System dependencies (rarely change)
RUN apt-get update && apt-get install -y \
gcc \
&& rm -rf /var/lib/apt/lists/*
# 3. Application dependencies (change occasionally)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 4. Application code (changes frequently)
COPY . .
# 5. Runtime configuration
ENV PYTHONUNBUFFERED=1
EXPOSE 8000
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
.dockerignore
ビルドコンテキストからファイルを除外します(ビルドを高速化し、イメージを小さくします)。
# .dockerignore
node_modules/
npm-debug.log
.git/
.gitignore
*.md
.env
.vscode/
__pycache__/
*.pyc
.pytest_cache/
coverage/
dist/
build/
マルチステージビルド
ビルドステージとランタイムステージを分けて、イメージサイズを最適化します。
Node.js TypeScript の例
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]
メリット:
- ビルド依存関係(TypeScript、webpack)を最終イメージから除外できます
- 最終イメージは約 50MB です(ビルドツールを含めると約 500MB)
- デプロイが高速になり、攻撃対象領域を縮小できます
Python の例
# Build stage
FROM python:3.11 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# Runtime stage
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "app.py"]
Go の例(最小サイズのイメージ)
# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server
# Runtime stage (scratch = empty base image)
FROM scratch
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
結果:約 10MB の最終イメージに、コンパイル済みバイナリのみが含まれます。
Docker Compose
YAML で複数コンテナのアプリケーションを定義します。
基本構造
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://db:5432/myapp
depends_on:
- db
volumes:
- ./src:/app/src # Hot reload in development
db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
- db_data:/var/lib/postgresql/data
ports:
- "5432:5432"
volumes:
db_data:
コマンド
# Start all services
docker-compose up
# Start in background
docker-compose up -d
# Rebuild images
docker-compose up --build
# Stop services
docker-compose down
# Stop and remove volumes
docker-compose down -v
# View logs
docker-compose logs -f app
# Run one-off command
docker-compose run app npm test
フルスタックの例
version: '3.8'
services:
# Frontend
web:
build:
context: ./frontend
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./frontend/src:/app/src
environment:
- REACT_APP_API_URL=http://localhost:8000
# Backend API
api:
build: ./backend
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
volumes:
- ./backend:/app
command: uvicorn main:app --host 0.0.0.0 --reload
# Database
db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
- db_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# Cache
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
# Worker (background jobs)
worker:
build: ./backend
command: celery -A tasks worker --loglevel=info
environment:
- REDIS_URL=redis://redis:6379
depends_on:
- redis
- db
volumes:
db_data:
redis_data:
networks:
default:
name: myapp_network
開発ワークフロー
ボリュームを使ったホットリロード
Node.js
services:
app:
build: .
volumes:
- ./src:/app/src # Sync source code
- /app/node_modules # Prevent overwriting container's node_modules
command: npm run dev
# Dockerfile.dev
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install # Include dev dependencies
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
Python(Django/FastAPI)
services:
web:
build: .
volumes:
- .:/app
command: python manage.py runserver 0.0.0.0:8000
# or for FastAPI:
# command: uvicorn main:app --host 0.0.0.0 --reload
VS Code Dev Containers
.devcontainer/devcontainer.json:
{
"name": "Python Dev Container",
"dockerComposeFile": "../docker-compose.yml",
"service": "app",
"workspaceFolder": "/app",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance"
],
"settings": {
"python.defaultInterpreterPath": "/usr/local/bin/python"
}
}
},
"postCreateCommand": "pip install -r requirements-dev.txt",
"remoteUser": "vscode"
}
ローカルデータベースコンテナ
# PostgreSQL
docker run -d \
--name dev-postgres \
-e POSTGRES_PASSWORD=localdev \
-e POSTGRES_DB=myapp_dev \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/data \
postgres:15-alpine
# MySQL
docker run -d \
--name dev-mysql \
-e MYSQL_ROOT_PASSWORD=localdev \
-e MYSQL_DATABASE=myapp_dev \
-p 3306:3306 \
-v mysqldata:/var/lib/mysql \
mysql:8
# MongoDB
docker run -d \
--name dev-mongo \
-p 27017:27017 \
-v mongodata:/data/db \
mongo:7
# Redis
docker run -d \
--name dev-redis \
-p 6379:6379 \
redis:7-alpine
本番環境でのパターン
ヘルスチェック
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# Health check endpoint
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD node healthcheck.js
EXPOSE 3000
CMD ["node", "server.js"]
// healthcheck.js
const http = require('http');
const options = {
host: 'localhost',
port: 3000,
path: '/health',
timeout: 2000
};
const request = http.request(options, (res) => {
if (res.statusCode === 200) {
process.exit(0);
} else {
process.exit(1);
}
});
request.on('error', () => process.exit(1));
request.end();
セキュリティのベストプラクティス
FROM python:3.11-slim
# 1. Use non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
# 2. Install dependencies as root
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 3. Copy application files
COPY --chown=appuser:appuser . .
# 4. Switch to non-root user
USER appuser
# 5. Drop unnecessary privileges
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
追加のセキュリティ対策:
- 最小限のベースイメージ(alpine、distroless)を使用します
- イメージの脆弱性をスキャンします:
docker scan myapp:latest - イメージにシークレットを含めないでください(環境変数またはシークレットマネージャーを使用します)
- ベースイメージを最新の状態に保ちます
- 可能な場合は読み取り専用のルートファイルシステムを使用します
シークレット管理
# Docker Swarm secrets (production)
echo "db_password_here" | docker secret create db_password -
version: '3.8'
services:
app:
image: myapp
secrets:
- db_password
environment:
- DB_PASSWORD_FILE=/run/secrets/db_password
secrets:
db_password:
external: true
代替方法:環境ファイル
# docker-compose.yml
services:
app:
env_file:
- .env.production # Never commit this file
# .env.production (gitignored)
DATABASE_URL=postgresql://user:pass@db:5432/prod
SECRET_KEY=your-secret-key
リソース制限
services:
app:
image: myapp
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
# Command-line resource limits
docker run -d \
--memory="512m" \
--cpus="1.0" \
--restart=unless-stopped \
myapp
フレームワーク別の例
Python:Django
FROM python:3.11-slim
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Collect static files
RUN python manage.py collectstatic --noinput
# Create non-root user
RUN useradd -m -u 1000 django && chown -R django:django /app
USER django
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "myproject.wsgi:application"]
docker-compose.yml:
version: '3.8'
services:
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/app
ports:
- "8000:8000"
environment:
- DEBUG=1
- DATABASE_URL=postgres://postgres:postgres@db:5432/django_dev
depends_on:
- db
db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: django_dev
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Python:FastAPI
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
Node.js:Next.js
# Multi-stage build for Next.js
FROM node:18-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]
next.config.js(standalone 出力に必要):
module.exports = {
output: 'standalone',
}
Node.js:Express
FROM node:18-alpine
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci --only=production
# Copy application
COPY . .
# Create non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]
TypeScript のビルド
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json tsconfig.json ./
RUN npm ci
COPY src/ ./src/
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]
コンテナオーケストレーションの基本
Docker Swarm(組み込み)
# Initialize swarm
docker swarm init
# Deploy stack
docker stack deploy -c docker-compose.yml myapp
# Scale service
docker service scale myapp_web=5
# Update service (zero-downtime)
docker service update --image myapp:2.0 myapp_web
# Remove stack
docker stack rm myapp
Kubernetes の比較
| 特徴 | Docker Compose | Docker Swarm | Kubernetes |
|---|---|---|---|
| 複雑さ | 低 | 中 | 高 |
| 用途 | ローカル開発 | 小規模クラスター | 大規模な本番環境 |
| セットアップ | 単一ファイル | 組み込み | 個別にインストール |
| スケーリング | 手動 | 自動 | 自動+高度な機能 |
| 高可用性 | なし | あり | あり |
| エコシステム | 限定的 | Docker | 膨大 |
それぞれの使用場面:
- Docker Compose:ローカル開発、シンプルなデプロイ
- Docker Swarm:小規模な本番クラスター、K8s よりシンプル
- Kubernetes:大規模な本番環境、マルチクラウド、高度なオーケストレーション
CI/CD 統合
GitHub Actions
# .github/workflows/docker.yml
name: Build and Push Docker Image
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Run tests
run: |
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} npm test
GitLab CI
# .gitlab-ci.yml
stages:
- build
- test
- deploy
variables:
DOCKER_DRIVER: overlay2
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
test:
stage: test
script:
- docker run --rm $IMAGE_TAG npm test
deploy:
stage: deploy
script:
- docker pull $IMAGE_TAG
- docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
only:
- main
コンテナーのデバッグ
ログの表示
# Stream logs
docker logs -f container_name
# Last 100 lines
docker logs --tail 100 container_name
# Logs since timestamp
docker logs --since 2024-01-01T10:00:00 container_name
# With timestamps
docker logs -t container_name
# Docker Compose logs
docker-compose logs -f service_name
実行中のコンテナーでコマンドを実行する
# Interactive shell
docker exec -it container_name /bin/sh
# or
docker exec -it container_name /bin/bash
# Run single command
docker exec container_name ls -la /app
# Run as different user
docker exec -u root container_name apt-get update
コンテナーの調査
# Full container details
docker inspect container_name
# Specific field (IP address)
docker inspect -f '{{.NetworkSettings.IPAddress}}' container_name
# Environment variables
docker inspect -f '{{.Config.Env}}' container_name
# Mounted volumes
docker inspect -f '{{.Mounts}}' container_name
リソース使用状況
# Real-time stats
docker stats
# Single container
docker stats container_name
# No streaming (single snapshot)
docker stats --no-stream
ネットワークのデバッグ
# List networks
docker network ls
# Inspect network
docker network inspect bridge
# Test connectivity between containers
docker exec container1 ping container2
# Check DNS resolution
docker exec container_name nslookup other_container
ビルド問題のデバッグ
# Build with no cache
docker build --no-cache -t myapp .
# Show build progress
docker build --progress=plain -t myapp .
# Build specific stage
docker build --target builder -t myapp-builder .
# Inspect intermediate layers
docker history myapp:latest
ローカル Docker のパターン(mcp-browser、mcp-memory)
mcp-browser の開発用 compose
docker-compose.ymlは、ポート範囲8875-8895とオプションのchromeプロファイルを指定してmcp-serverを実行します。- コードは読み取り専用でマウントされ(
./src:/app/src:ro)、ログと一時ボリュームは永続化されます。 - 環境変数のデフォルト値:
MCP_DEBUG=true、MCP_LOG_LEVEL=DEBUG、MCP_HOST=0.0.0.0、MCP_PORT=8875。 - オプションのプロファイル:
chrome(ブラウザー)とtools(開発ツール用コンテナー)。
mcp-browser の Dockerfile.dev
ARG PYTHON_VERSION=3.11を指定し、watchdogと Playwright Chromium をインストールします。- パッケージを編集可能モードでインストールし、
python -m src.dev_runnerを実行します。 - 非 root ユーザーを使用し、
/healthをヘルスチェックします。
mcp-memory の本番用 Dockerfile
/opt/venvに仮想環境を作成し、python:3.11-slimを使用するマルチステージビルドです。- 実行環境ではヘルスチェック用に
curlをインストールし、PYTHONPATH=/appを設定します。 - 非 root ユーザーを使用し、
/healthのチェックを行いながらCMD ["python", "run_api_server.py"]を実行します。
トラブルシューティング
よくある問題
「ポートはすでに使用されています」
# Find process using port
lsof -i :3000
# or
netstat -tulpn | grep 3000
# Kill process
kill -9 <PID>
# Or use different host port
docker run -p 3001:3000 myapp
「Docker デーモンに接続できません」
# Check Docker is running
docker info
# Restart Docker Desktop (Mac/Windows)
# or
sudo systemctl restart docker # Linux
# Check permissions (Linux)
sudo usermod -aG docker $USER
# Log out and back in
「デバイスに空き領域がありません」
# Remove unused containers, images, volumes
docker system prune -a --volumes
# Remove only dangling images
docker image prune
# Remove stopped containers
docker container prune
# Remove unused volumes
docker volume prune
# Check disk usage
docker system df
「ビルドコンテキストが大きすぎます」
# Create .dockerignore
cat > .dockerignore << EOF
node_modules/
.git/
*.log
dist/
coverage/
EOF
# Build with specific context
docker build -f Dockerfile -t myapp ./src
コンテナーがすぐに終了する
# Check logs
docker logs container_name
# Run with interactive shell to debug
docker run -it myapp /bin/sh
# Override entrypoint
docker run -it --entrypoint /bin/sh myapp
# Check exit code
docker inspect -f '{{.State.ExitCode}}' container_name
コンテナー内で権限が拒否される
# Run as root to debug
docker exec -u root -it container_name /bin/sh
# Fix ownership
docker exec -u root container_name chown -R appuser:appuser /app
# Or rebuild with correct permissions in Dockerfile
パフォーマンスの問題
ビルドが遅い
# Use BuildKit (faster, better caching)
DOCKER_BUILDKIT=1 docker build -t myapp .
# Multi-stage builds to reduce layers
# Order instructions by change frequency
# Use .dockerignore to exclude unnecessary files
メモリ使用量が多い
# Set memory limits
docker run -m 512m myapp
# Monitor memory
docker stats container_name
# Check for memory leaks in application
ボリュームのマウントが遅い(Mac/Windows)
# Use delegated consistency (Mac)
volumes:
- ./src:/app/src:delegated
# Or use named volumes instead of bind mounts
volumes:
- node_modules:/app/node_modules
ベストプラクティス
レイヤーの最適化
- 変更頻度に応じて並べる:変更頻度が最も低いものを先にする
- RUN コマンドをまとめる:レイヤー数を減らす
- 同じレイヤー内でクリーンアップする:一時ファイルをすぐに削除する
# GOOD
RUN apt-get update && apt-get install -y \
package1 \
package2 \
&& rm -rf /var/lib/apt/lists/*
# BAD (creates 3 layers, apt cache remains in layer 2)
RUN apt-get update
RUN apt-get install -y package1 package2
RUN rm -rf /var/lib/apt/lists/*
イメージサイズの削減
- Alpine イメージを使用する:フル Linux の 100MB 以上に対して 5MB
- マルチステージビルド:最終イメージからビルドツールを除外する
- 不要なファイルを削除する:ドキュメント、サンプル、テスト
- .dockerignore を使用する:開発用ファイルを除外する
# Before: 800MB
FROM node:18
COPY . .
RUN npm install
CMD ["node", "server.js"]
# After: 120MB
FROM node:18-alpine
COPY package*.json ./
RUN npm ci --only=production
COPY server.js .
CMD ["node", "server.js"]
セキュリティチェックリスト
- 信頼できる提供元の公式ベースイメージを使用する
- イメージのバージョンを正確に指定する(
latestは使用しない) - 非 root ユーザーとして実行する
- イメージの脆弱性をスキャンする
- ベースイメージを最新の状態に保つ
- イメージにシークレットを埋め込まない
- 可能な場合は読み取り専用のルートファイルシステムを使用する
- 攻撃対象領域を最小限に抑える(Alpine、distroless)
- Docker Content Trust(イメージ署名)を有効にする
開発環境と本番環境
開発環境:
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install # Include dev dependencies
COPY . .
CMD ["npm", "run", "dev"]
本番環境:
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]
ロギングのベストプラクティス
# Log to stdout/stderr (Docker captures these)
CMD ["node", "server.js"] # Good
# Don't log to files (lost when container stops)
CMD ["node", "server.js", ">", "app.log"] # Bad
// Application logging
console.log('Info message'); // stdout
console.error('Error message'); // stderr
// Use structured logging
console.log(JSON.stringify({
level: 'info',
timestamp: new Date().toISOString(),
message: 'Request processed',
requestId: '123'
}));
環境設定
# Use ARG for build-time variables
ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV
# Use ENV for runtime variables
ENV PORT=3000
ENV LOG_LEVEL=info
# Override at runtime
# docker run -e PORT=8080 -e LOG_LEVEL=debug myapp
ヘルスチェック
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# docker-compose.yml
services:
app:
image: myapp
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 40s
グレースフルシャットダウン
// Node.js example
process.on('SIGTERM', () => {
console.log('SIGTERM received, closing server...');
server.close(() => {
console.log('Server closed');
process.exit(0);
});
});
# Use exec form to properly handle signals
CMD ["node", "server.js"] # Good
CMD node server.js # Bad (wrapped in /bin/sh, signals not forwarded)
クイックリファレンス
基本コマンド
# Images
docker build -t name:tag .
docker pull image:tag
docker push image:tag
docker images
docker rmi image:tag
# Containers
docker run -d --name container image
docker ps # Running containers
docker ps -a # All containers
docker stop container
docker start container
docker restart container
docker rm container
docker logs -f container
docker exec -it container /bin/sh
# Cleanup
docker system prune -a # Remove all unused resources
docker container prune # Remove stopped containers
docker image prune # Remove dangling images
docker volume prune # Remove unused volumes
# Compose
docker-compose up -d
docker-compose down
docker-compose logs -f
docker-compose exec service /bin/sh
docker-compose build
よく使うフラグ
# docker run flags
-d # Detached (background)
-it # Interactive with TTY
-p 8080:80 # Port mapping (host:container)
--name myapp # Container name
-e VAR=value # Environment variable
-v /host:/container # Volume mount
--network name # Connect to network
--rm # Remove container on exit
-m 512m # Memory limit
--cpus 1.0 # CPU limit
Dockerfile の命令
FROM image:tag # Base image
WORKDIR /path # Set working directory
COPY src dst # Copy files
ADD src dst # Copy (with URL/tar support)
RUN command # Execute command
ENV KEY=value # Environment variable
EXPOSE port # Document port
CMD ["executable"] # Default command
ENTRYPOINT ["exec"] # Command prefix
VOLUME /path # Create mount point
USER username # Set user
ARG name=default # Build argument
LABEL key=value # Metadata
HEALTHCHECK CMD command # Health check
概要
Docker によるコンテナ化には、次のメリットがあります。
- 一貫性:開発から本番まで同一の環境を実現します
- 分離:アプリケーション間で依存関係が競合しません
- 移植性:Docker が動作するあらゆる場所(クラウド、ローカル、CI)で実行できます
- 効率性:VM と比べて軽量で、起動も高速です
- スケーラビリティ:オーケストレーションによる水平スケーリングが簡単です
主なワークフロー:
- 開発:ホットリロード用のボリュームを設定した docker-compose
- CI/CD:イメージをビルド、テストし、レジストリにプッシュします
- 本番:イメージをプルし、リソース制限とヘルスチェックを設定して実行します
次のステップ:
- イメージサイズを最適化するマルチステージビルドを習得します
- ヘルスチェックと正常なシャットダウンを実装します
- ローカル開発用に docker-compose を設定します
- Docker を CI/CD パイプラインに統合します
- 本番環境での大規模運用に向けて、オーケストレーション(Swarm または Kubernetes)を検討します
コメント
Markdown 対応