Docker Compose 심화 운영

Docker Compose 운영 심화

Docker Compose는 로컬 개발을 넘어 스테이징·CI/CD 환경에서도 활발히 사용됩니다. Profiles로 환경별 서비스를 분리하고, healthcheck 기반 의존성으로 안정적인 기동 순서를 보장하며, Secrets로 민감 정보를 안전하게 관리하는 심화 기법을 다룹니다.

Profiles: 환경별 서비스 분리

개발 환경에서만 필요한 서비스(디버거, 모니터링 UI)를 profiles로 분리합니다.

# docker-compose.yml
services:
  api:
    build: .
    ports: ["3000:3000"]
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: myapp
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5

  # 개발 환경에서만 실행
  adminer:
    image: adminer
    ports: ["8080:8080"]
    profiles: ["dev"]

  mailhog:
    image: mailhog/mailhog
    ports: ["1025:1025", "8025:8025"]
    profiles: ["dev"]

  # 모니터링 (운영 환경)
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    profiles: ["monitoring"]

  grafana:
    image: grafana/grafana
    ports: ["3001:3000"]
    profiles: ["monitoring"]
# 기본 서비스만 실행 (api + db)
docker compose up -d

# 개발 도구 포함
docker compose --profile dev up -d

# 모니터링 포함
docker compose --profile monitoring up -d

# 전부 실행
docker compose --profile dev --profile monitoring up -d

depends_on + healthcheck 의존성

단순 depends_on은 컨테이너 시작만 보장하고, 서비스 준비 완료를 보장하지 않습니다. condition: service_healthy를 사용해야 합니다.

services:
  api:
    build: .
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
      kafka:
        condition: service_healthy

  db:
    image: mysql:8
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 30s    # 초기 부팅 대기

  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 3s
      timeout: 2s
      retries: 5

  kafka:
    image: confluentinc/cp-kafka:7.5.0
    healthcheck:
      test: ["CMD-SHELL", "kafka-broker-api-versions --bootstrap-server localhost:9092"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 40s
condition 의미 사용 시점
service_started 컨테이너 시작됨 (기본) 순서만 중요할 때
service_healthy healthcheck 통과 DB, 메시지 큐 등 준비 필요
service_completed_successfully 종료 코드 0 마이그레이션, 초기화 작업

Secrets 관리

환경 변수로 비밀번호를 전달하면 docker inspect에 노출됩니다. Secrets를 사용하면 파일로 안전하게 주입됩니다.

services:
  api:
    build: .
    secrets:
      - db_password
      - jwt_secret
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password
      JWT_SECRET_FILE: /run/secrets/jwt_secret

  db:
    image: postgres:16
    secrets:
      - db_password
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt     # 로컬 파일
  jwt_secret:
    environment: JWT_SECRET_VALUE       # 환경 변수에서 (Compose v2.23+)
// Node.js에서 secret 파일 읽기
import { readFileSync } from 'fs';

const dbPassword = process.env.DB_PASSWORD_FILE
  ? readFileSync(process.env.DB_PASSWORD_FILE, 'utf8').trim()
  : process.env.DB_PASSWORD;

Multi-Stage Compose: extends와 include

공통 설정을 분리하고 환경별로 오버라이드합니다.

# docker-compose.yml (기본)
services:
  api:
    build:
      context: .
      target: production
    restart: always
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'

  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
# docker-compose.override.yml (개발 환경, 자동 로드)
services:
  api:
    build:
      target: development
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      NODE_ENV: development
      DEBUG: "app:*"
    ports:
      - "3000:3000"
      - "9229:9229"     # 디버거 포트
    command: npm run start:debug

  db:
    ports:
      - "5432:5432"     # 로컬 접근용
# docker-compose.prod.yml (운영)
services:
  api:
    image: registry.example.com/myapp:${TAG:-latest}
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 1G

# 실행: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

include (Compose v2.20+)

# docker-compose.yml
include:
  - path: ./infra/docker-compose.db.yml
  - path: ./infra/docker-compose.redis.yml
  - path: ./infra/docker-compose.monitoring.yml
    env_file: ./infra/.env

services:
  api:
    build: .
    depends_on:
      db:
        condition: service_healthy

리소스 제한과 로깅

Docker Health Check 운영 심화와 함께 리소스 제한을 설정하면 안정적인 운영이 가능합니다.

services:
  api:
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'
        reservations:
          memory: 256M
          cpus: '0.5'
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
        tag: "{{.Name}}/{{.ID}}"
    
    # OOM 방지: 메모리 초과 시 재시작
    restart: unless-stopped

CI/CD에서 Compose 활용

# GitHub Actions 예시
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Start services
        run: docker compose --profile test up -d --wait

      - name: Run tests
        run: docker compose exec api npm run test:e2e

      - name: Collect logs on failure
        if: failure()
        run: docker compose logs --tail=100 > compose-logs.txt

      - name: Cleanup
        if: always()
        run: docker compose down -v

--wait 플래그는 모든 서비스의 healthcheck가 통과할 때까지 대기합니다. Docker BuildKit 캐시 최적화와 함께 사용하면 CI 빌드 시간도 단축할 수 있습니다.

정리

Docker Compose 심화 운영의 핵심은 Profiles로 환경별 서비스를 분리하고, healthcheck 기반 depends_on으로 안정적 기동 순서를 보장하며, Secrets로 민감 정보를 보호하는 것입니다. override 파일로 개발/운영 설정을 분리하고, CI에서는 –wait 플래그로 서비스 준비를 확인한 후 테스트를 실행하세요.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux