🎄 科学と神々株式会社 アドベントカレンダー 2025
Hybrid License System Day 21: Docker Composeでの統合
統合・デプロイ編 (1/5)
📖 はじめに
Day 21では、Docker Composeを使ったマイクロサービスの統合を学びます。3つのサービスをコンテナ化し、docker-compose.ymlで一括管理する方法を理解しましょう。
🐳 Docker Composeとは?
定義
Docker Composeは、複数のDockerコンテナを定義・実行するツールです。YAMLファイル1つで、複数のサービスの設定、ネットワーク、ボリュームを管理できます。
version: '3.8'
services:
api-gateway: # サービス1
auth-service: # サービス2
admin-service: # サービス3
volumes: # 共有ボリューム
database-data:
networks: # ネットワーク設定
license-network:
🏗️ Hybrid License Systemの構成
システム全体図
┌────────────────────────────────────────────┐
│ Docker Compose Environment │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ API Gateway │ │ Auth Service │ │
│ │ Port: 3000 │ │ Port: 3001 │ │
│ │ Node.js │ │ Node.js │ │
│ │ Alpine │ │ Alpine │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ │ ┌──────────────┘ │
│ │ │ ┌──────────────┐ │
│ │ │ │ Admin Service│ │
│ │ │ │ Port: 3002 │ │
│ │ │ │ Node.js │ │
│ │ │ │ Alpine │ │
│ │ │ └──────┬───────┘ │
│ └──┴─────────┘ │
│ │ │
│ ┌────▼────────────┐ │
│ │ Shared Database │ │
│ │ (Volume) │ │
│ │ SQLite File │ │
│ └─────────────────┘ │
│ │
│ Network: license-network (Bridge) │
└────────────────────────────────────────────┘
📄 docker-compose.yml設計
完全版設定ファイル
version: '3.8'
services:
# API Gateway (JavaScript/Express.js)
api-gateway:
build:
context: ./api-gateway
dockerfile: Dockerfile
container_name: license-api-gateway
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- GATEWAY_PORT=3000
- GATEWAY_HOST=0.0.0.0
- AUTH_SERVICE_URL=http://auth-service:3001
- ADMIN_SERVICE_URL=http://admin-service:3002
- SERVICE_SECRET=${SERVICE_SECRET:-shared-secret-between-services}
- CORS_ORIGINS=${CORS_ORIGINS:-*}
- JWT_SECRET=${JWT_SECRET}
depends_on:
auth-service:
condition: service_healthy
admin-service:
condition: service_healthy
networks:
- license-network
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# Auth Service (JavaScript/Node.js)
auth-service:
build:
context: ./auth-service
dockerfile: Dockerfile
container_name: license-auth-service
ports:
- "3001:3001"
environment:
- NODE_ENV=production
- AUTH_SERVICE_PORT=3001
- JWT_SECRET=${JWT_SECRET}
- DATABASE_PATH=/app/data/licenses.db
- SERVICE_SECRET=${SERVICE_SECRET:-shared-secret-between-services}
volumes:
- database-data:/app/data
networks:
- license-network
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3001/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"]
interval: 30s
timeout: 10s
retries: 3
start_period: 5s
# Admin Service (JavaScript/Express.js)
admin-service:
build:
context: ./admin-service
dockerfile: Dockerfile
container_name: license-admin-service
ports:
- "3002:3002"
environment:
- NODE_ENV=production
- ADMIN_SERVICE_PORT=3002
- DATABASE_PATH=/app/data/licenses.db
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin123}
volumes:
- database-data:/app/data
networks:
- license-network
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3002/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"]
interval: 30s
timeout: 10s
retries: 3
start_period: 5s
volumes:
database-data:
driver: local
networks:
license-network:
driver: bridge
🔧 Dockerfile最適化
Auth Service Dockerfile
# Auth Service Dockerfile
# JavaScript/Node.js implementation
FROM node:20-alpine
WORKDIR /app
# Install dependencies for better-sqlite3
RUN apk add --no-cache \
python3 \
make \
g++ \
sqlite-dev
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY src/ ./src/
# Create directories
RUN mkdir -p /app/data
# Set non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs /app
USER nodejs
# Expose port
EXPOSE 3001
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3001/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
# Run the service
CMD ["node", "src/standalone.js"]
API Gateway Dockerfile
# API Gateway Dockerfile
# JavaScript/Express.js implementation
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY src/ ./src/
# Set non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs /app
USER nodejs
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
# Run the service
CMD ["node", "src/server.js"]
Admin Service Dockerfile
# Admin Service Dockerfile
# JavaScript/Express.js + React implementation
# Stage 1: Build stage
FROM node:20-alpine AS builder
WORKDIR /app
# Install dependencies for better-sqlite3
RUN apk add --no-cache \
python3 \
make \
g++ \
sqlite-dev
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY src/ ./src/
# Stage 2: Production stage
FROM node:20-alpine
WORKDIR /app
# Copy from builder
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/src ./src
# Create directories
RUN mkdir -p /app/data
# Set non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs /app
USER nodejs
# Expose port
EXPOSE 3002
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3002/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
# Run the service
CMD ["node", "src/server.js"]
🔒 環境変数管理
.env.example
# License System - Hybrid Implementation
# Environment Variables Configuration
# ============================================================
# API Gateway Configuration
# ============================================================
GATEWAY_PORT=3000
GATEWAY_HOST=0.0.0.0
# ============================================================
# Auth Service Configuration (JavaScript/Node.js)
# ============================================================
AUTH_SERVICE_URL=http://localhost:3001
AUTH_SERVICE_PORT=3001
# JWT Configuration
# IMPORTANT: Change this to a secure random string in production
# Generate with: openssl rand -base64 48
JWT_SECRET=your-super-secret-jwt-key-minimum-32-characters-change-this-in-production
# ============================================================
# Admin Service Configuration (JavaScript)
# ============================================================
ADMIN_SERVICE_URL=http://localhost:3002
ADMIN_SERVICE_PORT=3002
# Admin Credentials
# IMPORTANT: Change these in production
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123
# ============================================================
# Database Configuration
# ============================================================
# For local development
DATABASE_PATH=./shared/database/licenses.db
# ============================================================
# Inter-Service Authentication
# ============================================================
# Shared secret for service-to-service communication
# Generate with: openssl rand -hex 32
SERVICE_SECRET=shared-secret-between-services-change-this-in-production
# ============================================================
# CORS Configuration
# ============================================================
# Comma-separated list of allowed origins
CORS_ORIGINS=http://localhost:3000,http://localhost:3002,http://localhost:8080
🚀 使用方法
1. 環境変数設定
# .envファイルを作成
cp .env.example .env
# セキュアな値を生成
openssl rand -base64 48 # JWT_SECRET用
openssl rand -hex 32 # SERVICE_SECRET用
# .envファイルを編集
vim .env
2. サービスビルド
# すべてのサービスをビルド
docker-compose build
# 特定のサービスのみビルド
docker-compose build auth-service
# キャッシュなしでビルド
docker-compose build --no-cache
3. サービス起動
# バックグラウンドで起動
docker-compose up -d
# フォアグラウンドで起動(ログ表示)
docker-compose up
# 特定のサービスのみ起動
docker-compose up -d api-gateway auth-service
4. ログ確認
# すべてのサービスのログ
docker-compose logs -f
# 特定のサービスのログ
docker-compose logs -f auth-service
# 最新100行のログ
docker-compose logs --tail=100 api-gateway
5. ヘルスチェック
# API Gateway
curl http://localhost:3000/health
# Auth Service
curl http://localhost:3001/health
# Admin Service
curl http://localhost:3002/health
6. サービス停止
# サービスを停止(コンテナは保持)
docker-compose stop
# サービスを停止してコンテナを削除
docker-compose down
# ボリュームも削除(データベースも削除される)
docker-compose down -v
📊 ヘルスチェックの詳細
ヘルスチェックの仕組み
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3001/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"]
interval: 30s # 30秒ごとにチェック
timeout: 10s # 10秒でタイムアウト
retries: 3 # 3回失敗したらunhealthy
start_period: 10s # 起動後10秒は失敗してもカウントしない
ヘルスチェックのライフサイクル
Starting → Healthy → Unhealthy → Healthy
↑ ↓
└──────────┘
(自動再試行)
depends_onとヘルスチェック
api-gateway:
depends_on:
auth-service:
condition: service_healthy # Auth ServiceがHealthyになってから起動
admin-service:
condition: service_healthy # Admin ServiceがHealthyになってから起動
🗃️ データ永続化
Named Volumeの使用
volumes:
database-data: # Named Volume
driver: local
services:
auth-service:
volumes:
- database-data:/app/data # Named Volumeをマウント
admin-service:
volumes:
- database-data:/app/data # 同じVolumeを共有
ボリュームの管理
# ボリューム一覧
docker volume ls
# ボリュームの詳細情報
docker volume inspect hybrid_database-data
# ボリュームのバックアップ
docker run --rm \
-v hybrid_database-data:/data \
-v $(pwd)/backup:/backup \
alpine tar czf /backup/database-backup-$(date +%Y%m%d).tar.gz /data
# ボリュームのリストア
docker run --rm \
-v hybrid_database-data:/data \
-v $(pwd)/backup:/backup \
alpine tar xzf /backup/database-backup-20251121.tar.gz -C /
🌐 ネットワーク設定
Bridge Networkの使用
networks:
license-network:
driver: bridge # Dockerのデフォルトネットワークドライバー
サービス間通信
// API Gatewayから Auth Serviceへの通信
const AUTH_SERVICE_URL = 'http://auth-service:3001'; // サービス名で解決
// Docker Composeが自動的にDNS解決してくれる
// auth-service → 172.18.0.2 (内部IP)
🚀 次のステップ
Day 22では、テスト戦略について学びます。統合テスト、E2Eテスト、パフォーマンステスト、セキュリティテストの実装方法を理解しましょう。
🔗 関連リンク
次回予告: Day 22では、Jestを使った統合テストとE2Eテストの実装を詳しく解説します!
Copyright © 2025 Gods & Golem, Inc. All rights reserved.