Docker Compose로 멀티 컨테이너 환경 구축하기: 단계별 튜토리얼
주제 키워드: Docker Compose, 멀티 컨테이너, YAML 구성, 의존성 관리, CI/CD 통합
개인 프로젝트나 사내 인프라를 구축할 때 스프링 부트 웹 애플리케이션과 PostgreSQL DB를 각각 수동으로 docker run 명령어로 띄우다 보니 컨테이너 간 사설 네트워크가 안 묶여서 DB 연결 에러가 나거나, 서버 재부팅 시 DB 컨테이너가 먼저 안 떠서 웹이 뻗는 문제를 겪었습니다. 단일 YAML 파일 하나로 서비스 의존성(depends_on), 내부 네트워크, 영구 볼륨까지 한 번에 관리해 주는 Docker Compose를 도입하면서 이 삽질을 완전히 끝낼 수 있었습니다. 실무에서 바로 써먹을 수 있는 멀티 컨테이너 구성법과 운영 팁을 공유합니다.
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가 준비되기 전에 웹 애플리케이션이 연결을 시도
- 해결:
depends_on에condition추가- Dockerize 같은 툴로 헬스 체크
# 조건 추가 예제
depends_on:
db:
condition: service_healthy
5.2 볼륨 마운트 권한 문제
- 증상:
Permission denied오류 - 해결:
- 호스트 디렉터리 권한 변경
- 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의 확장성
- CI/CD 통합: GitHub Actions, GitLab CI에서
docker-compose명령어로 테스트 환경 구축 가능 - 확장성: Kubernetes로의 전환을 위해 Compose 파일을 Helm 차트로 변환 가능
- 모니터링: Prometheus + Grafana와 연동해 컨테이너 메트릭 수집
추가 학습 자료:
- Docker Compose 공식 문서
- Awesome Docker 커뮤니티 리소스
이 가이드를 통해 멀티 컨테이너 환경의 설계부터 운영까지의 핵심 역량을 습득할 수 있습니다. 실제 프로덕션 환경에서는 보안 강화(예: TLS 암호화)와 리소스 제한(예: --memory, --cpus)을 추가로 적용해야 합니다.