Skip to content

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 ls

Dockerfile 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: bridge

Development 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: true

Container 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_app

Kubernetes 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: LoadBalancer

Security 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-security

Runtime 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: 65535

Performance 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: false

Monitoring 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: manual

Troubleshooting

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_name

Performance Issues

bash
# Monitor container resources
docker stats

# Check container processes
docker exec container_name ps aux

# Analyze image layers
docker history image_name

Network Issues

bash
# List networks
docker network ls

# Inspect network
docker network inspect network_name

# Test connectivity
docker exec container_name ping other_container

Storage 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_name

Debugging 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-tools

Best Practices Summary

Development

  1. Use Multi-stage Builds: Reduce image size and improve security
  2. Leverage Build Cache: Speed up builds with proper layer ordering
  3. Use .dockerignore: Exclude unnecessary files from build context
  4. Pin Base Images: Use specific versions for reproducible builds
  5. Run as Non-root: Improve security by using non-privileged users

Production

  1. Health Checks: Implement proper health checks for containers
  2. Resource Limits: Set appropriate CPU and memory limits
  3. Security Scanning: Regularly scan images for vulnerabilities
  4. Monitoring: Implement comprehensive monitoring and logging
  5. Backup Strategy: Ensure data persistence and backup procedures

Operations

  1. Container Orchestration: Use orchestration tools for production
  2. Rolling Updates: Implement zero-downtime deployment strategies
  3. Secret Management: Use proper secret management solutions
  4. Network Security: Implement network segmentation and policies
  5. Disaster Recovery: Plan for disaster recovery scenarios

究極の AI 駆動 IDE 学習ガイド