Docker: 総合実践トレーニング
最終更新:2026-08-26
これは本コースの最終プロジェクトです。全 23 レッスンの知識を結集し、フルスタックマイクロサービスをゼロから本番環境までデプロイします。
1. 学べること
- フルスタックマイクロサービスアーキテクチャ設計
- エンドツーエンドの多段階開発ハンズオン
- 複数サービスの Compose オーケストレーション
- CI/CD 自動デプロイ
- 監視・アラートシステムの構築
2. 3 日納品の物語
(1) 挑戦: 3 日以内にローカルから本番へ
Charlie はタスクを任されました。3 日以内に EC プラットフォームをローカル開発から本番環境へデプロイすることです。アーキテクチャは React フロントエンド、Go API、PostgreSQL、Redis、RabbitMQ の 5 サービス。手動の自動化プロセスは一切ありません。
(2) Docker フルスタックツールチェーンによるソリューション
Charlie はフルスタックの Docker ツールチェーンを使い、わずか 2 日でプロジェクトを完成させました。Dockerfile の作成 → Compose でのオーケストレーション → 自動化された CI/CD デプロイ → 監視とアラート。
(3) 効果: 2 日で完成・納品
手動デプロイから完全自動化へ。アーキテクチャ設計、コンテナ化、オーケストレーション、CI/CD、監視をわずか 2 日で完了——これがフルスタック Docker スキルをマスターする価値です。
3. フルスタックアーキテクチャ設計
(1) アーキテクチャ概要
graph TB
USER["ブラウザ"] --> ING["Nginx<br/>:80<br/>React SPA + API プロキシ"]
ING -->|"api/"| API1["Go API #1<br/>:8080"]
ING -->|"api/"| API2["Go API #2<br/>:8080"]
ING -->|"api/"| API3["Go API #3<br/>:8080"]
API1 --> PG["PostgreSQL<br/>:5432<br/>プライマリ DB"]
API2 --> PG
API3 --> PG
API1 --> REDIS["Redis<br/>:6379<br/>キャッシュ"]
API2 --> REDIS
API3 --> MQ["RabbitMQ<br/>:5672<br/>メッセージキュー"]
MQ --> WRK["Worker<br/>バックグラウンドジョブ"]
PROM["Prometheus<br/>:9090<br/>メトリクス"] --> GRAF["Grafana<br/>:3000<br/>ダッシュボード"]
PROM --> API1
PROM --> PG
PROM --> REDIS
(2) サービス一覧
| サービス | 技術スタック | イメージ | ポート |
|---|---|---|---|
| Nginx | リバースプロキシ + 静的ファイル | セルフホスト (マルチステージ) | 80 |
| Go API | REST API | セルフホスト (マルチステージ) | 8080 |
| PostgreSQL | リレーショナルデータベース | postgres:15-alpine | 5432 |
| Redis | キャッシュ + セッション | redis:7-alpine | 6379 |
| RabbitMQ | メッセージキュー | rabbitmq:3-management | 5672/15672 |
| Worker | バックグラウンドタスク処理 | セルフホスト (API イメージと共通) | - |
| Prometheus | メトリクス収集 | prom/prometheus | 9090 |
| Grafana | 可視化ダッシュボード | grafana/grafana | 3000 |
4. フロントエンド Dockerfile (React + Nginx)
▶ サンプル: フロントエンド Dockerfile のマルチステージビルド (難易度: ⭐⭐⭐)
DOCKERFILE
# ============================================
# ステージ 1: React アプリケーションをビルド
# ============================================
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ============================================
# ステージ 2: Nginx で配信
# ============================================
FROM nginx:1.25-alpine
# ビルド成果物をコピー
COPY --from=builder /app/dist /usr/share/nginx/html
# SPA ルーティング用の Nginx 設定をコピー
COPY nginx.conf /etc/nginx/conf.d/default.conf
# セキュリティ: root 以外のユーザー
RUN chown -R nginx:nginx /usr/share/nginx/html && \
chown -R nginx:nginx /var/cache/nginx && \
chown -R nginx:nginx /var/log/nginx
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s \
CMD wget -qO- http://localhost/ || exit 1
CMD ["nginx", "-g", "daemon off;"]
5. バックエンド Dockerfile (Go マルチステージ)
▶ サンプル: バックエンド Dockerfile のマルチステージビルド (難易度: ⭐⭐⭐)
DOCKERFILE
# ============================================
# ステージ 1: Go バイナリをビルド
# ============================================
FROM golang:1.22-alpine AS builder
RUN apk add --no-cache git
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /app/server
# ============================================
# ステージ 2: 最小限のランタイム
# ============================================
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata curl && \
adduser -D -u 1000 appuser
WORKDIR /app
COPY --from=builder /app/server .
RUN chown -R appuser:appuser /app
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
CMD ["/app/server"]
6. Docker Compose オーケストレーション
▶ サンプル: 完全な docker-compose.prod.yml (難易度: ⭐⭐⭐)
YAML
# ============================================
# docker-compose.prod.yml - フルスタック
# ============================================
services:
nginx:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "80:80"
depends_on:
api:
condition: service_healthy
restart: unless-stopped
networks:
- frontend
- backend
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
api:
build:
context: ./api
dockerfile: Dockerfile
environment:
DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/${DB_NAME}
REDIS_URL: redis://redis:6379
RABBITMQ_URL: amqp://guest:${MQ_PASSWORD}@rabbitmq:5672
deploy:
replicas: 3
resources:
limits:
cpus: "1.0"
memory: 512M
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
networks:
- backend
- db-net
- cache-net
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
worker:
build:
context: ./api
dockerfile: Dockerfile
command: ["/app/server", "worker"]
environment:
DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/${DB_NAME}
RABBITMQ_URL: amqp://guest:${MQ_PASSWORD}@rabbitmq:5672
depends_on:
postgres:
condition: service_healthy
rabbitmq:
condition: service_healthy
restart: unless-stopped
networks:
- backend
- db-net
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- pg-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- db-net
redis:
image: redis:7-alpine
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
restart: unless-stopped
networks:
- cache-net
rabbitmq:
image: rabbitmq:3-management-alpine
environment:
RABBITMQ_DEFAULT_PASS: ${MQ_PASSWORD}
ports:
- "15672:15672"
volumes:
- mq-data:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"]
interval: 15s
timeout: 10s
restart: unless-stopped
networks:
- backend
prometheus:
image: prom/prometheus:latest
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
ports:
- "9090:9090"
restart: unless-stopped
networks:
- backend
- db-net
- cache-net
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
volumes:
- grafana-data:/var/lib/grafana
depends_on:
- prometheus
restart: unless-stopped
networks:
- backend
volumes:
pg-data:
redis-data:
mq-data:
prometheus-data:
grafana-data:
networks:
frontend:
backend:
db-net:
internal: true
cache-net:
internal: true
7. CI/CD 設定
(1) GitHub Actions フルパイプライン
YAML
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ secrets.REGISTRY }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASS }}
- name: API をビルドしてプッシュ
uses: docker/build-push-action@v5
with:
context: ./api
push: true
tags: ${{ secrets.REGISTRY }}/api:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: フロントエンドをビルドしてプッシュ
uses: docker/build-push-action@v5
with:
context: ./frontend
push: true
tags: ${{ secrets.REGISTRY }}/frontend:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: 本番環境へデプロイ
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /opt/ecommerce
export API_TAG=${{ github.sha }}
export FRONTEND_TAG=${{ github.sha }}
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d --remove-orphans
docker image prune -f
8. 監視設定
(1) Prometheus 設定
YAML
# monitoring/prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'api'
static_configs:
- targets: ['api:8080']
metrics_path: /metrics
- job_name: 'postgres'
static_configs:
- targets: ['postgres-exporter:9187']
- job_name: 'redis'
static_configs:
- targets: ['redis-exporter:9121']
- job_name: 'rabbitmq'
static_configs:
- targets: ['rabbitmq:15692']
9. デプロイと検証
(1) デプロイチェックリスト
| ステップ | アクション | 検証 |
|---|---|---|
| 1 | docker compose -f docker-compose.prod.yml up -d --build |
docker compose ps ですべて Up |
| 2 | http://localhost にアクセス |
React ページが正常に表示される |
| 3 | curl http://localhost/api/health |
{"ステータス":"ok"} |
| 4 | http://localhost:3000 にアクセス |
Grafana ログインページ |
| 5 | http://localhost:15672 にアクセス |
RabbitMQ 管理ページ |
| 6 | docker compose logs api |
API ログにエラーがない |
(2) 比較表: ベアメタル vs Docker vs K8s
| ディメンション | ベアメタルデプロイ | Docker Compose | Kubernetes |
|---|---|---|---|
| デプロイ時間 | 1〜2 日 | 30 分 | 1〜2 日 (初期セットアップ) |
| 再現性 | ❌ | ✅ | ✅ |
| 自動スケーリング | ❌ | ❌ | ✅ HPA |
| セルフヒーリング | ❌ | 再起動ポリシー | ✅ 自動再構築 |
| ゼロダウンタイム更新 | 困難 | Compose 再起動 | ✅ ローリングアップデート |
| 監視 | 手動 | Prometheus + Grafana | 組み込み + Prometheus |
| 複雑性 | 低 | 中 | 高 |
10. 完全なサンプル: ワンクリックでフルスタックアプリをデプロイ
BASH
# ============================================
# 完全な手順: フルスタックデプロイ
# ============================================
# 1. .env ファイルを作成 (絶対にコミットしないこと)
cat > .env << 'EOF'
DB_PASSWORD=secure_db_pass_2024
DB_NAME=ecommerce
REDIS_PASSWORD=secure_redis_pass
MQ_PASSWORD=secure_mq_pass
GRAFANA_PASSWORD=admin123
EOF
# 2. すべてのサービスをビルドして起動
docker compose -f docker-compose.prod.yml up -d --build
# 3. サービスの初期化を待つ
sleep 30
# 4. すべてのサービスを検証
docker compose -f docker-compose.prod.yml ps
echo "=== ヘルスチェック ==="
docker compose -f docker-compose.prod.yml ps --format "table {{.Name}}\t{{.Status}}"
# 5. API をテスト
curl -s http://localhost/api/health
# 6. データベース接続を確認
docker compose -f docker-compose.prod.yml exec postgres pg_isready -U appuser
# 7. 監視にアクセス
echo "Grafana: http://localhost:3000 (admin/${GRAFANA_PASSWORD})"
echo "RabbitMQ: http://localhost:15672 (guest/${MQ_PASSWORD})"
echo "Prometheus: http://localhost:9090"
# 8. 集約ログを確認
docker compose -f docker-compose.prod.yml logs --tail 50 api
# 9. 必要に応じて API をスケール
docker compose -f docker-compose.prod.yml up -d --scale api=5
# 10. クリーンアップ
docker compose -f docker-compose.prod.yml down
❓ よくある質問
Q マイクロサービスはモノリシックアーキテクチャと比べてどれくらい複雑ですか?
A 運用面の複雑性は大幅に増します。ネットワーク構成、サービス検出、ログ集約、分散トレーシングなど新たな課題が生じます。ただしメリットには、独立したデプロイ、独立したスケーリング、障害の分離があります。推奨: 小規模チーム (5 人未満) はモジュール式モノリス、大規模チームはマイクロサービスを採用してください。マイクロサービスは手段のために採用しないでください。
Q CI/CD でデータベースマイグレーションはどのように実行しますか?
A デプロイステップの前にマイグレーションステップを追加します。① マイグレーションイメージをビルド。②
docker run --rm migrate:latest alembic upgrade head。③ 新バージョンをデプロイ。重要ポイント: マイグレーションは後方互換性を持たせる必要があります。新コードは新旧両方のテーブル構造をサポートしなければなりません。Q 複数サービスのログを統合するにはどうすればよいですか?
A 3 つのアプローチがあります。① ELK Stack (Elasticsearch + Logstash + Kibana) — 業界標準。② Loki + Grafana (軽量、クラウドネイティブ環境向け)。③ クラウドプロバイダーのロギングサービス (AWS CloudWatch / Alibaba Cloud SLS)。すべてのコンテナの stdout → コレクター → 一元ストレージ → 検索ダッシュボード。
Q 本番環境で HTTPS を設定するには?
A Nginx コンテナで TLS を設定します。① 無料の Let's Encrypt 証明書 + Certbot で自動更新。② リバースプロキシモード: Nginx が TLS を処理し、内部通信は HTTP を使用。③ クラウドプロバイダーのロードバランサーが TLS を処理 (ALB / SLB) する場合、コンテナに証明書は不要。
Q マイクロサービススタック全体の健全性を監視するには?
A Prometheus と Grafana のゴールデンコンボです。① Prometheus が各サービスの /メトリクス エンドポイントからデータを収集。② Grafana がダッシュボードとアラートルールを表示。③ 主要メトリクス: API レイテンシ (P50 / P95 / P99)、エラー率、CPU / メモリ使用率、データベース接続数。まず監視し、それから最適化しましょう。
📖 まとめ
- フルスタックマイクロサービス = フロントエンド + API + DB + キャッシュ + MQ + 監視 + CI/CD
- マルチステージフロントエンド: Node ビルド → Nginx ランタイム。イメージサイズを 1 GB から 25 MB へ縮小
- バックエンドマルチステージ: Go コンパイル → Alpine 実行。イメージサイズを 780 MB から 15 MB へ縮小
- Compose: ネットワーク分離 (db-net internal) + ヘルスチェック + リソース制限 + ログローテーション
- CI/CD: プッシュ → ビルド → プッシュ → SSH デプロイ — 完全自動化で人手による操作はゼロ
- Prometheus + Grafana: メトリクス収集 + 可視化 + アラート — 運用に必須
📝 練習問題
- 基礎問題 (難易度: ⭐): ブログシステム (Nginx + API + DB + キャッシュ) のマイクロサービスアーキテクチャ図を設計し、各サービスのイメージとポートを列挙してください。
- 応用問題 (難易度: ⭐⭐): フルスタック Docker 技術を使ってこのアーキテクチャをデプロイし、
docker-compose.prod.ymlファイルを作成、アプリケーションを正常に起動してください。 - 挑戦問題 (難易度: ⭐⭐⭐): Prometheus + Grafana 監視ダッシュボードをセットアップし、API レイテンシとエラー率用のダッシュボードを追加、メールアラートルールを設定してください。