Dockerガイド
この包括的なガイドでは、Traeで構築されたアプリケーションのDocker使用法、コンテナ化ベストプラクティス、デプロイメント戦略について説明します。
概要
Dockerは、コンテナ化を通じて一貫性があり、ポータブルで、スケーラブルなアプリケーションデプロイメントを可能にします。このガイドでは以下について説明します:
- Dockerの基礎
- Dockerfileベストプラクティス
- マルチステージビルド
- Docker Compose
- コンテナオーケストレーション
- セキュリティ考慮事項
- パフォーマンス最適化
Dockerの基礎
核心概念
イメージとコンテナ
- イメージ: コンテナを作成するための読み取り専用テンプレート
- コンテナ: イメージの実行中インスタンス
- レイヤー: Dockerfileの各命令がレイヤーを作成
- レジストリ: イメージの保存と配布システム
Dockerアーキテクチャ
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Docker CLI │ │ Docker Daemon │ │ Docker Hub │
│ │───▶│ │───▶│ │
│ docker build │ │ Image Mgmt │ │ Image Store │
│ docker run │ │ Container │ │ Public/Private│
│ docker push │ │ Management │ │ Repositories │
└─────────────────┘ └─────────────────┘ └─────────────────┘基本コマンド
bash
# イメージ管理
docker build -t myapp:latest .
docker images
docker rmi image_id
docker pull nginx:alpine
docker push myregistry/myapp:latest
# コンテナ管理
docker run -d -p 3000:3000 --name myapp myapp:latest
docker ps
docker ps -a
docker stop container_id
docker rm container_id
docker logs container_id
docker exec -it container_id /bin/bash
# System management
docker system prune
docker volume ls
docker network lsDockerfile Best Practices
Basic Dockerfile Structure
dockerfile
# 公式ベースイメージを使用
FROM node:18-alpine AS base
# 作業ディレクトリを設定
WORKDIR /app
# パッケージファイルをコピー
COPY package*.json ./
# 依存関係をインストール
RUN npm ci --only=production && npm cache clean --force
# アプリケーションコードをコピー
COPY . .
# 非rootユーザーを作成
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# 所有権を変更
RUN chown -R nextjs:nodejs /app
USER nextjs
# ポートを公開
EXPOSE 3000
# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# アプリケーションを開始
CMD ["npm", "start"]Multi-Stage Builds
dockerfile
# Node.jsアプリケーション用マルチステージビルド
# ステージ1: 依存関係のビルド
FROM node:18-alpine AS dependencies
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# ステージ2: アプリケーションのビルド
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ステージ3: 本番イメージ
FROM node:18-alpine AS production
WORKDIR /app
# 適切なシグナル処理のためdumb-initをインストール
RUN apk add --no-cache dumb-init
# 非rootユーザーを作成
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# ビルドされたアプリケーションをコピー
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist
COPY --from=dependencies --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --chown=nextjs:nodejs package*.json ./
# 非rootユーザーに切り替え
USER nextjs
# ポートを公開
EXPOSE 3000
# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# 適切なシグナル処理のためdumb-initを使用
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]Language-Specific Examples
Python Application
dockerfile
FROM python:3.11-slim AS base
# 環境変数を設定
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONHASHSEED=random \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# システム依存関係をインストール
RUN apt-get update && apt-get install -y \
build-essential \
curl \
&& rm -rf /var/lib/apt/lists/*
# 非rootユーザーを作成
RUN groupadd -r appuser && useradd -r -g appuser appuser
# 作業ディレクトリを設定
WORKDIR /app
# Python依存関係をインストール
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# アプリケーションコードをコピー
COPY --chown=appuser:appuser . .
# 非rootユーザーに切り替え
USER appuser
# ポートを公開
EXPOSE 8000
# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# アプリケーションを開始
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]Go Application
dockerfile
# ビルドステージ
FROM golang:1.21-alpine AS builder
WORKDIR /app
# go modファイルをコピー
COPY go.mod go.sum ./
RUN go mod download
# ソースコードをコピー
COPY . .
# アプリケーションをビルド
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# 本番ステージ
FROM alpine:latest AS production
# HTTPS用のca-certificatesをインストール
RUN apk --no-cache add ca-certificates
# 非rootユーザーを作成
RUN adduser -D -s /bin/sh appuser
WORKDIR /root/
# ビルダーステージからバイナリをコピー
COPY --from=builder /app/main .
# 所有権を変更
RUN chown appuser:appuser main
# 非rootユーザーに切り替え
USER appuser
# ポートを公開
EXPOSE 8080
# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
# アプリケーションを開始
CMD ["./main"]Docker Compose
Basic Docker Compose Setup
yaml
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
target: production
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
volumes:
- app_logs:/app/logs
networks:
- app_network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app_network
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- app_network
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- app
networks:
- app_network
restart: unless-stopped
volumes:
postgres_data:
redis_data:
app_logs:
networks:
app_network:
driver: bridgeDevelopment Docker Compose
yaml
# docker-compose.dev.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
- "9229:9229" # Node.jsデバッガー
environment:
- NODE_ENV=development
- DEBUG=app:*
volumes:
- .:/app
- /app/node_modules
command: npm run dev
depends_on:
- db
- redis
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=myapp_dev
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
ports:
- "5432:5432"
volumes:
- postgres_dev_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
mailhog:
image: mailhog/mailhog
ports:
- "1025:1025"
- "8025:8025"
volumes:
postgres_dev_data:Production Docker Compose
yaml
# docker-compose.prod.yml
version: '3.8'
services:
app:
image: myregistry/myapp:${APP_VERSION:-latest}
deploy:
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
environment:
- NODE_ENV=production
secrets:
- db_password
- jwt_secret
networks:
- app_network
- traefik_network
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`myapp.com`)"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
volumes:
- postgres_data:/var/lib/postgresql/data
secrets:
- db_password
networks:
- app_network
deploy:
placement:
constraints:
- node.role == manager
secrets:
db_password:
external: true
jwt_secret:
external: true
volumes:
postgres_data:
external: true
networks:
app_network:
driver: overlay
attachable: true
traefik_network:
external: trueContainer Orchestration
Docker Swarm
bash
# Swarmを初期化
docker swarm init
# ワーカーノードを参加
docker swarm join --token SWMTKN-1-... manager-ip:2377
# スタックをデプロイ
docker stack deploy -c docker-compose.prod.yml myapp
# サービスをスケール
docker service scale myapp_app=5
# サービスを更新
docker service update --image myregistry/myapp:v2.0.0 myapp_app
# サービスを監視
docker service ls
docker service ps myapp_appKubernetes Deployment
yaml
# k8s/deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry/myapp:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-secrets
key: database-url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancerSecurity Best Practices
Dockerfile Security
dockerfile
# セキュリティ重視のDockerfile
FROM node:18-alpine AS base
# パッケージを更新してセキュリティアップデートをインストール
RUN apk update && apk upgrade && apk add --no-cache dumb-init
# 非rootユーザーを作成
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001 -G nodejs
# 作業ディレクトリを設定
WORKDIR /app
# パッケージファイルをコピー
COPY package*.json ./
# 依存関係をインストール
RUN npm ci --only=production && \
npm cache clean --force && \
rm -rf /tmp/*
# アプリケーションコードをコピー
COPY --chown=nextjs:nodejs . .
# 不要なファイルを削除
RUN rm -rf .git .gitignore README.md docs/ tests/
# 適切な権限を設定
RUN chmod -R 755 /app && \
chmod -R 644 /app/package*.json
# 非rootユーザーに切り替え
USER nextjs
# ポートを公開(非特権)
EXPOSE 3000
# 適切なシグナル処理のためにdumb-initを使用
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "server.js"]Security Scanning
bash
# Scan images for vulnerabilities
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image myapp:latest
# Scan Dockerfile
docker run --rm -i hadolint/hadolint < Dockerfile
# Use Docker Bench Security
docker run -it --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /var/lib:/var/lib \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/lib/systemd:/usr/lib/systemd \
-v /etc:/etc --label docker_bench_security \
docker/docker-bench-securityRuntime Security
yaml
# docker-compose.yml with security constraints
version: '3.8'
services:
app:
image: myapp:latest
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
read_only: true
tmpfs:
- /tmp
- /var/cache
user: "1001:1001"
ulimits:
nproc: 65535
nofile:
soft: 65535
hard: 65535Performance Optimization
Image Optimization
dockerfile
# Optimized Dockerfile
FROM node:18-alpine AS dependencies
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Distroless image for minimal attack surface
FROM gcr.io/distroless/nodejs18-debian11 AS production
WORKDIR /app
COPY --from=dependencies /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package*.json ./
EXPOSE 3000
CMD ["dist/server.js"]Build Optimization
bash
# Use BuildKit for faster builds
export DOCKER_BUILDKIT=1
docker build --target production -t myapp:latest .
# Use build cache
docker build --cache-from myapp:latest -t myapp:latest .
# Multi-platform builds
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .Resource Management
yaml
# docker-compose.yml with resource limits
version: '3.8'
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
mem_limit: 512m
mem_reservation: 256m
cpus: 0.5
oom_kill_disable: falseMonitoring and Logging
Container Monitoring
yaml
# docker-compose.monitoring.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
grafana:
image: grafana/grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
cadvisor:
image: gcr.io/cadvisor/cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
volumes:
prometheus_data:
grafana_data:Centralized Logging
yaml
# docker-compose.logging.yml
version: '3.8'
services:
app:
image: myapp:latest
logging:
driver: "fluentd"
options:
fluentd-address: localhost:24224
tag: myapp
fluentd:
image: fluent/fluentd:v1.14-debian-1
ports:
- "24224:24224"
volumes:
- ./fluentd.conf:/fluentd/etc/fluent.conf
- fluentd_data:/var/log/fluentd
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.5.0
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
kibana:
image: docker.elastic.co/kibana/kibana:8.5.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
volumes:
fluentd_data:
elasticsearch_data:CI/CD Integration
GitHub Actions
yaml
# .github/workflows/docker.yml
name: Docker Build and Deploy
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- 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=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Run security scan
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'GitLab CI/CD
yaml
# .gitlab-ci.yml
stages:
- build
- test
- security
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
build:
stage: build
image: docker:20.10.16
services:
- docker:20.10.16-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
only:
- main
- develop
security_scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --exit-code 0 --format template --template "@contrib/gitlab.tpl" -o gl-container-scanning-report.json $IMAGE_TAG
artifacts:
reports:
container_scanning: gl-container-scanning-report.json
only:
- main
- develop
deploy_staging:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache curl
script:
- curl -X POST "$WEBHOOK_URL" -H "Content-Type: application/json" -d '{"image":"'$IMAGE_TAG'"}'
only:
- develop
deploy_production:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache curl
script:
- curl -X POST "$PROD_WEBHOOK_URL" -H "Content-Type: application/json" -d '{"image":"'$IMAGE_TAG'"}'
only:
- main
when: manualTroubleshooting
Common Issues
Container Won't Start
bash
# Check container logs
docker logs container_name
# Check container configuration
docker inspect container_name
# Run container interactively
docker run -it --entrypoint /bin/sh image_namePerformance Issues
bash
# Monitor container resources
docker stats
# Check container processes
docker exec container_name ps aux
# Analyze image layers
docker history image_nameNetwork Issues
bash
# List networks
docker network ls
# Inspect network
docker network inspect network_name
# Test connectivity
docker exec container_name ping other_containerStorage Issues
bash
# Check disk usage
docker system df
# Clean up unused resources
docker system prune -a
# Check volume usage
docker volume ls
docker volume inspect volume_nameDebugging Tools
bash
# Debug container with additional tools
docker run -it --rm \
--pid container:target_container \
--net container:target_container \
--cap-add SYS_PTRACE \
nicolaka/netshoot
# Profile container performance
docker run -it --rm \
--pid container:target_container \
--cap-add SYS_PTRACE \
brendangregg/perf-toolsBest Practices Summary
Development
- Use Multi-stage Builds: Reduce image size and improve security
- Leverage Build Cache: Speed up builds with proper layer ordering
- Use .dockerignore: Exclude unnecessary files from build context
- Pin Base Images: Use specific versions for reproducible builds
- Run as Non-root: Improve security by using non-privileged users
Production
- Health Checks: Implement proper health checks for containers
- Resource Limits: Set appropriate CPU and memory limits
- Security Scanning: Regularly scan images for vulnerabilities
- Monitoring: Implement comprehensive monitoring and logging
- Backup Strategy: Ensure data persistence and backup procedures
Operations
- Container Orchestration: Use orchestration tools for production
- Rolling Updates: Implement zero-downtime deployment strategies
- Secret Management: Use proper secret management solutions
- Network Security: Implement network segmentation and policies
- Disaster Recovery: Plan for disaster recovery scenarios