基礎LESSON 02

この教材で学ぶこと コンテナの仕組みとDockerfileの基本を学びます。

Docker 実践 — コンテナの本質から運用まで


1. コンテナの本質(VM との違い)

VM:
┌─────────────────────────────────┐
│  App A  │  App B  │  App C      │
│ (OS込み)│ (OS込み)│ (OS込み)    │  ← 各VMがフルOS
├─────────────────────────────────┤
│        Hypervisor               │
├─────────────────────────────────┤
│        Host OS                  │
└─────────────────────────────────┘

コンテナ:
┌─────────────────────────────────┐
│  App A  │  App B  │  App C      │
│(依存のみ)│(依存のみ)│(依存のみ)  │  ← アプリと依存のみ
├─────────────────────────────────┤
│        Docker Engine            │
├─────────────────────────────────┤
│        Host OS(カーネル共有)   │
└─────────────────────────────────┘

コンテナの実体: Linux の namespace(プロセス隔離)+ cgroup(リソース制限)

# コンテナで見えるプロセスは namespace で隔離されている
docker run --rm ubuntu ps aux  # PID 1 だけ見える

# コンテナのリソース制限(cgroup)
docker run --cpus="0.5" --memory="512m" nginx

2. Dockerfile ベストプラクティス

# ❌ アンチパターン
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y python3
RUN pip3 install flask
COPY . /app
CMD python3 /app/main.py

# ✅ ベストプラクティス
# 1. 軽量ベースイメージ
FROM python:3.12-slim

# 2. 非rootユーザー(セキュリティ)
RUN groupadd -r appuser && useradd -r -g appuser appuser

WORKDIR /app

# 3. 依存ファイルを先にコピー(レイヤーキャッシュ最適化)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 4. アプリコードは最後(変更頻度が高い)
COPY --chown=appuser:appuser . .

USER appuser

# 5. EXPOSE は文書化目的(実際の公開は -p オプション)
EXPOSE 8080

# 6. シグナルを受け取れるように exec 形式
CMD ["python3", "main.py"]

マルチステージビルド(本番イメージを軽くする)

# ビルドステージ
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o server .

# 実行ステージ(scratch = 空のイメージ)
FROM scratch
COPY --from=builder /app/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
ENTRYPOINT ["/server"]
# → イメージサイズが数MB になる(golang:1.22 は約800MB)

3. Docker Compose — ローカル開発・検証環境

# docker-compose.yml
version: '3.9'

services:
  app:
    build:
      context: .
      target: development  # マルチステージの特定ステージを指定
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    volumes:
      - .:/app  # ホットリロード用(開発時のみ)
    depends_on:
      db:
        condition: service_healthy  # ヘルスチェック後に起動
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru

volumes:
  postgres_data:

networks:
  default:
    driver: bridge
docker compose up -d          # バックグラウンド起動
docker compose logs -f app    # アプリのログ追跡
docker compose exec app bash  # コンテナ内に入る
docker compose down -v        # 停止+ボリューム削除

4. イメージサイズの調査と最適化

# レイヤー別サイズ確認
docker history myapp:latest

# 詳細な内容確認(dive ツール)
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock wagoodman/dive myapp:latest

# 不要なイメージ・コンテナの一括削除
docker system prune -af --volumes
docker image prune -f  # タグのないイメージのみ

5. コンテナのネットワーク

# Dockerネットワーク一覧
docker network ls

# コンテナ間通信(同一ネットワーク内はコンテナ名で解決)
docker run -d --name db --network mynet postgres
docker run -d --name app --network mynet myapp
# → app から "db:5432" で接続可能

# ホストネットワーク(パフォーマンス重視の場合)
docker run --network host nginx
# → localhost:80 でアクセス可能(ポートマッピング不要)

# ネットワークデバッグ
docker network inspect mynet
docker exec app ping db
docker exec app nslookup db

6. セキュリティのベストプラクティス

# 読み取り専用ファイルシステム
docker run --read-only --tmpfs /tmp myapp

# ケイパビリティを最小限に
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp

# セキュリティスキャン
docker scout cves myapp:latest      # Docker Scout
trivy image myapp:latest            # Trivy(OSS)

# 実行中コンテナの確認
docker inspect myapp | jq '.[0].HostConfig | {Privileged, CapAdd, CapDrop}'

7. よく使うデバッグコマンド

# コンテナのリソース使用量
docker stats --no-stream

# コンテナ内でコマンド実行
docker exec -it myapp sh  # sh/bash が入っていない場合
docker exec -it myapp /bin/sh

# コンテナが起動しない場合(終了後も確認)
docker run --entrypoint sh myapp -c "ls /app && cat /app/config.json"

# ログの確認
docker logs myapp --tail=100 --since="1h"
docker logs myapp 2>&1 | grep ERROR

# コンテナの差分確認(どのファイルが変更されたか)
docker diff myapp

🔗 次のステップ