Docker Compose로 멀티 컨테이너 환경 구축하기: 단계별 튜토리얼

주제 키워드: Docker Compose, 멀티 컨테이너, YAML 구성, 의존성 관리, CI/CD 통합

현대 애플리케이션 개발에서 멀티 컨테이너 아키텍처는 마이크로서비스, 데이터 처리 파이프라인, 개발/운영 환경 통합에 필수적인 요소입니다. Docker Compose는 단일 YAML 파일로 여러 컨테이너의 생명주기, 네트워크, 볼륨, 환경 변수를 관리하는 도구로, 개발부터 프로덕션까지 효율성을 극대화합니다. 이 가이드에서는 DB-웹 애플리케이션 예제부터 고급 설정, 트러블슈팅까지 실제 동작 가능한 예제로 설명합니다.

1. Docker Compose의 핵심 개념과 장점

1.1 멀티 컨테이너 아키텍처의 필요성

단일 컨테이너로 모든 기능을 구현하면 관심사 분리가 어렵고, 확장성이 제한됩니다. 예를 들어, 웹 서버와 데이터베이스를 하나의 컨테이너에 묶으면:

  • DB 업데이트 시 전체 애플리케이션 재빌드 필요
  • 각 서비스의 독립적인 확장(Scale-out) 불가
  • 로깅/모니터링의 복잡성 증가

Docker Compose는 이런 문제를 해결하며, 서비스 간 의존성을 명확히 정의하고 환경별 구성을 쉽게 전환할 수 있습니다. 공식 문서(Docker Compose Overview)에서도 강조하는 핵심 이점은 다음과 같습니다:

  • 선언적 구성: YAML 파일로 인프라 상태를 코드로 관리
  • 의존성 자동 처리: depends_on으로 컨테이너 시작 순서 제어
  • 환경 변수 분리: .env 파일로 민감 정보 관리

1.2 단일 YAML 파일의 효율성

예를 들어, 웹 애플리케이션과 PostgreSQL DB를 연결하는 구성은 다음과 같이 단순화됩니다:

# docker-compose.yml
version: '3.8'
services:
  webapp:
    image: my-webapp:latest
    ports:
      - "8000:8000"
    environment:
      - DB_HOST=db
      - DB_USER=user
      - DB_PASSWORD=secret
    depends_on:
      - db
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: secret
    volumes:
      - postgres_data:/var/lib/postgresql/data
volumes:
  postgres_data:

2. 사전 준비: 개발 환경 설정

2.1 Docker 및 Docker Compose 설치

  • Linux (Ubuntu 기준):
sudo apt-get update
sudo apt-get install docker.io docker-compose
  • macOS: Docker Desktop 설치 후 자동 포함
  • Windows: Docker Desktop 설치 시 WSL2와 함께 Compose 활성화

확인 명령:

docker --version
# Docker version 24.0.2, Build cb71016

docker-compose version
# docker-compose version 1.29.2, build 5becea4c

2.2 네트워크 및 볼륨 개념

  • 네트워크: 컨테이너 간 통신을 위한 가상 네트워크. 기본적으로 Compose는 프로젝트명_default 네트워크를 생성하지만, 커스텀 네트워크도 정의 가능합니다.
  • 볼륨: 호스트와 컨테이너 간 데이터 영구 저장. 위 예제의 postgres_data는 DB 데이터를 호스트에 보관하여 컨테이너 재생성 시에도 데이터 유지

3. 단계별 본문: 기본 구성 실습

3.1 간단한 웹 애플리케이션 예제

Python Flask 애플리케이션과 Redis 캐시를 연동하는 예제를 구현해 보겠습니다.

3.1.1 애플리케이션 코드 작성

# app.py
from flask import Flask
import redis
import os

app = Flask(__name__)
redis_host = os.getenv('REDIS_HOST', 'localhost')
redis_client = redis.StrictRedis(host=redis_host, port=6379, db=0)

@app.route('/')
def hello():
    redis_client.incr('hits')
    return f"Total hits: {redis_client.get('hits').decode('utf-8')"\n
if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8000)

3.1.2 Docker 이미지 빌드

# Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . ./
CMD ["python", "app.py"]

# requirements.txt
flask
redis

3.1.3 Docker Compose 파일 작성

# docker-compose.yml
version: '3.8'
services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      REDIS_HOST: redis
    depends_on:
      - redis
  redis:
    image: redis:7.0
    ports:
      - "6379:6379"

3.1.4 실행 및 테스트

# 빌드 및 실행
$ docker-compose up --build

# 별도 터미널에서 테스트
$ curl http://localhost:8000
# 출력: Total hits: 1

3.2 의존성 관리의 중요성

depends_on은 컨테이너 시작 순서만 제어할 뿐, DB 준비 완료 상태를 보장하지 않습니다. 이를 해결하려면 wait-for-it.sh 같은 툴을 사용하거나, 애플리케이션 수준에서 재시도 로직을 구현해야 합니다.

4. 고급 설정: 프로덕션 환경 최적화

4.1 커스텀 네트워크와 환경 변수 분리

4.1.1 네트워크 정의 예제

# docker-compose.yml
version: '3.8'
networks:
  app-network:
    driver: bridge
services:
  web:
    networks:
      - app-network
  db:
    networks:
      - app-network

4.1.2 .env 파일 활용

# .env
DB_USER=admin
DB_PASSWORD=strong_password
# docker-compose.yml
env_file:
  - .env
services:
  web:
    environment:
      DB_USER: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}

4.2 프로필 기반 구성 (dev/prod)

Compose 1.27+부터 profiles를 사용해 환경별 서비스를 활성화할 수 있습니다.

# docker-compose.yml
version: '3.8'
services:
  web:
    image: my-webapp:latest
  db:
    image: postgres:15
  devtools:
    image: node:18
    command: npm run dev
    profiles:
      - dev
  monitoring:
    image: prom/prometheus
    profiles:
      - prod

실행 방법:

# 개발 환경
$ docker-compose --profile dev up

# 프로덕션 환경
$ docker-compose --profile prod up

5. 트러블슈팅: 흔한 문제와 해결법

5.1 컨테이너 간 연결 실패

  • 증상: Connection refused 오류 발생
  • 원인: DB가 준비되기 전에 웹 애플리케이션이 연결을 시도
  • 해결:
    1. depends_oncondition 추가
    2. Dockerize 같은 툴로 헬스 체크
# 조건 추가 예제
depends_on:
  db:
    condition: service_healthy

5.2 볼륨 마운트 권한 문제

  • 증상: Permission denied 오류
  • 해결:
    1. 호스트 디렉터리 권한 변경
    2. Docker Compose에서 user 지정
# 권한 문제 해결 예제
volumes:
  - type: volume
    source: myvol
    target: /app/data
    volume:
      nocopy: true

5.3 로그 분석

  • 컨테이너 로그 확인:
$ docker-compose logs [서비스명]
# 예: docker-compose logs web
  • 실시간 모니터링:
$ docker-compose logs -f web

6. 마치며: Docker Compose의 확장성

  1. CI/CD 통합: GitHub Actions, GitLab CI에서 docker-compose 명령어로 테스트 환경 구축 가능
  2. 확장성: Kubernetes로의 전환을 위해 Compose 파일을 Helm 차트로 변환 가능
  3. 모니터링: Prometheus + Grafana와 연동해 컨테이너 메트릭 수집

추가 학습 자료:

이 가이드를 통해 멀티 컨테이너 환경의 설계부터 운영까지의 핵심 역량을 습득할 수 있습니다. 실제 프로덕션 환경에서는 보안 강화(예: TLS 암호화)와 리소스 제한(예: --memory, --cpus)을 추가로 적용해야 합니다.