복잡한 다중 컨테이너 환경을 Docker Compose로 간단하게 관리하는 방법을 배우고, 개발부터 프로덕션까지 활용하는 실전 노하우를 알아봅니다
이전 포스트에서 다중 컨테이너 애플리케이션을 구축했지만, 각 컨테이너를 개별적으로 관리하는 것은 매우 번거로웠습니다. 긴 명령어, 복잡한 옵션, 실행 순서 관리... 이 모든 것을 해결해주는 도구가 바로 Docker Compose입니다.
Docker Compose는 YAML 파일을 사용해 다중 컨테이너 Docker 애플리케이션을 정의하고 실행하는 도구입니다.
docker-compose up으로 모든 서비스 시작version: '3.8' # Compose 파일 버전
services: # 컨테이너 정의
service1:
# 서비스 1 설정
service2:
# 서비스 2 설정
volumes: # 볼륨 정의
volume1:
volume2:
networks: # 네트워크 정의
network1:
이전 포스트의 복잡한 명령어들을 Docker Compose로 변환해보겠습니다.
# 네트워크 생성
docker network create goals-net
# MongoDB 실행
docker run -d --name mongodb \
-v goals-data:/data/db \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=secret \
--network goals-net \
mongo
# 백엔드 빌드 및 실행
docker build -t goals-backend ./backend
docker run -d --name goals-backend \
-v $(pwd)/backend:/app \
-v /app/node_modules \
-e MONGODB_USERNAME=admin \
-e MONGODB_PASSWORD=secret \
--network goals-net \
-p 80:80 \
goals-backend
# 프론트엔드 빌드 및 실행
docker build -t goals-frontend ./frontend
docker run -d --name goals-frontend \
-v $(pwd)/frontend/src:/app/src \
-e REACT_APP_API_URL=http://localhost \
-p 3000:3000 \
goals-frontend
# docker-compose.yml
version: '3.8'
services:
mongodb:
image: mongo
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: secret
volumes:
- goals-data:/data/db
backend:
build: ./backend
ports:
- '80:80'
volumes:
- ./backend:/app
- /app/node_modules
environment:
MONGODB_USERNAME: admin
MONGODB_PASSWORD: secret
depends_on:
- mongodb
frontend:
build: ./frontend
ports:
- '3000:3000'
volumes:
- ./frontend/src:/app/src
environment:
REACT_APP_API_URL: http://localhost
depends_on:
- backend
volumes:
goals-data:
이제 단 하나의 명령으로 전체 스택을 실행할 수 있습니다:
docker-compose up -d
services:
# 공식 이미지 사용
database:
image: postgres:13-alpine
# Dockerfile로 빌드
app:
build: ./app
# 고급 빌드 설정
backend:
build:
context: ./backend
dockerfile: Dockerfile.prod
args:
NODE_VERSION: 16
Docker Compose는 다양한 방법으로 환경 변수를 설정할 수 있습니다:
services:
backend:
# 방법 1: 직접 정의
environment:
NODE_ENV: production
PORT: 3000
# 방법 2: 배열 형식
environment:
- NODE_ENV=production
- PORT=3000
# 방법 3: 외부 파일 참조
env_file:
- ./env/backend.env
- ./env/common.env
.env 파일 예시
# ./env/backend.env
NODE_ENV=development
DB_HOST=mongodb
DB_PORT=27017
DB_NAME=goals
JWT_SECRET=your-secret-key
services:
app:
volumes:
# 바인드 마운트 (상대 경로)
- ./src:/app/src
# 바인드 마운트 (절대 경로)
- /home/user/data:/app/data
# 명명된 볼륨
- app-data:/app/data
# 익명 볼륨
- /app/node_modules
# 읽기 전용 마운트
- ./config:/app/config:ro
volumes:
app-data:
# 볼륨 드라이버 설정 (선택사항)
driver: local
driver_opts:
type: none
device: /path/to/data
o: bind
services:
frontend:
networks:
- frontend-net
backend:
networks:
- frontend-net
- backend-net
database:
networks:
- backend-net
networks:
frontend-net:
driver: bridge
backend-net:
driver: bridge
# 커스텀 설정
ipam:
config:
- subnet: 172.28.0.0/16
services:
database:
image: postgres:13
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
backend:
build: ./backend
depends_on:
database:
condition: service_healthy # 헬스체크 통과 후 시작
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
개발과 프로덕션 환경을 분리할 수 있습니다:
docker-compose.yml (기본 설정)
version: '3.8'
services:
backend:
build: ./backend
environment:
NODE_ENV: production
docker-compose.dev.yml (개발 환경 오버라이드)
version: '3.8'
services:
backend:
volumes:
- ./backend:/app
- /app/node_modules
environment:
NODE_ENV: development
command: npm run dev
사용 방법
# 개발 환경
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
# 프로덕션 환경
docker-compose up
.env 파일을 활용한 환경 분리
# .env.development
COMPOSE_PROJECT_NAME=goals-dev
DB_PASSWORD=dev-password
API_PORT=3000
# .env.production
COMPOSE_PROJECT_NAME=goals-prod
DB_PASSWORD=secure-prod-password
API_PORT=80
docker-compose.yml에서 변수 사용
services:
backend:
ports:
- "${API_PORT}:${API_PORT}"
environment:
DB_PASSWORD: ${DB_PASSWORD}
# backend 서비스를 3개로 스케일링
docker-compose up -d --scale backend=3
# 로드 밸런서 추가
services:
nginx:
image: nginx
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "80:80"
depends_on:
- backend
# 서비스 시작
docker-compose up -d
# 로그 보기
docker-compose logs -f [service-name]
# 서비스 중지
docker-compose stop
# 서비스 중지 및 제거
docker-compose down
# 볼륨까지 모두 제거
docker-compose down -v
# 특정 서비스만 재시작
docker-compose restart backend
# 이미지 재빌드 후 시작
docker-compose up -d --build
# 특정 서비스만 빌드
docker-compose build backend
# 서비스 상태 확인
docker-compose ps
# 서비스에서 명령 실행
docker-compose exec backend npm test
# 새 컨테이너에서 명령 실행
docker-compose run --rm backend npm install
완전한 프로덕션급 설정 예제를 살펴보겠습니다.
myapp/
├── docker-compose.yml
├── docker-compose.prod.yml
├── .env.example
├── frontend/
│ ├── Dockerfile
│ ├── Dockerfile.prod
│ └── src/
├── backend/
│ ├── Dockerfile
│ ├── Dockerfile.prod
│ └── src/
├── nginx/
│ ├── nginx.conf
│ └── nginx.prod.conf
└── scripts/
├── backup.sh
└── deploy.sh
version: '3.8'
services:
# PostgreSQL 데이터베이스
postgres:
image: postgres:13-alpine
environment:
POSTGRES_DB: ${DB_NAME:-myapp}
POSTGRES_USER: ${DB_USER:-admin}
POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}
volumes:
- postgres-data:/var/lib/postgresql/data
- ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-admin}"]
interval: 5s
timeout: 5s
retries: 5
# Redis 캐시
redis:
image: redis:6-alpine
command: redis-server --appendonly yes
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
# Node.js 백엔드
backend:
build:
context: ./backend
dockerfile: Dockerfile
volumes:
- ./backend:/app
- /app/node_modules
environment:
NODE_ENV: development
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: ${DB_NAME:-myapp}
DB_USER: ${DB_USER:-admin}
DB_PASSWORD: ${DB_PASSWORD:-secret}
REDIS_HOST: redis
REDIS_PORT: 6379
JWT_SECRET: ${JWT_SECRET:-dev-secret}
ports:
- "3001:3001"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
command: npm run dev
# React 프론트엔드
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
volumes:
- ./frontend:/app
- /app/node_modules
environment:
REACT_APP_API_URL: http://localhost:3001
ports:
- "3000:3000"
depends_on:
- backend
command: npm start
# 개발용 관리 도구
adminer:
image: adminer
ports:
- "8080:8080"
depends_on:
- postgres
volumes:
postgres-data:
redis-data:
networks:
default:
name: myapp-network
version: '3.8'
services:
# Nginx 리버스 프록시
nginx:
image: nginx:alpine
volumes:
- ./nginx/nginx.prod.conf:/etc/nginx/nginx.conf:ro
- static-data:/usr/share/nginx/html
ports:
- "80:80"
- "443:443"
depends_on:
- backend
- frontend
postgres:
restart: always
ports: [] # 외부 포트 노출 제거
redis:
restart: always
backend:
build:
context: ./backend
dockerfile: Dockerfile.prod
restart: always
volumes: [] # 개발용 볼륨 제거
environment:
NODE_ENV: production
ports: [] # 외부 포트 노출 제거
command: node dist/server.js
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.prod
args:
REACT_APP_API_URL: /api
volumes:
- static-data:/app/build
ports: []
command: echo "Frontend built successfully"
# 프로덕션에서는 adminer 제거
adminer:
deploy:
replicas: 0
volumes:
static-data:
#!/bin/bash
# scripts/deploy.sh
echo "🚀 프로덕션 배포 시작..."
# 환경 변수 로드
export $(cat .env.production | xargs)
# 이미지 빌드
echo "📦 이미지 빌드 중..."
docker-compose -f docker-compose.yml -f docker-compose.prod.yml build
# 기존 서비스 중지
echo "🛑 기존 서비스 중지..."
docker-compose -f docker-compose.yml -f docker-compose.prod.yml down
# 데이터베이스 백업
echo "💾 데이터베이스 백업..."
./scripts/backup.sh
# 새 서비스 시작
echo "🚀 새 서비스 시작..."
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 헬스 체크
echo "🏥 헬스 체크..."
sleep 10
curl -f http://localhost/health || exit 1
echo "✅ 배포 완료!"
# 상세 로그 확인
docker-compose logs backend
# 설정 검증
docker-compose config
# 환경 변수 확인
docker-compose config | grep -A 5 environment
# 네트워크 목록 확인
docker network ls
# 서비스 간 통신 테스트
docker-compose exec backend ping postgres
services:
app:
user: "${UID}:${GID}" # 호스트 사용자와 매칭
volumes:
- ./data:/app/data
.env 파일로 관리.env 파일은 .gitignore에 추가Docker Compose는 복잡한 다중 컨테이너 환경을 간단하게 만들어줍니다. 하나의 YAML 파일로 전체 인프라를 정의하고, 버전 관리하며, 팀원들과 공유할 수 있습니다.
다음 포스트에서는 개발 도구를 컨테이너화하는 유틸리티 컨테이너 패턴을 알아보겠습니다. npm, composer 같은 도구를 로컬에 설치하지 않고도 사용하는 방법을 배워보세요!
시리즈 네비게이션
로컬에 개발 도구를 설치하지 않고 Docker 컨테이너로 npm, composer, artisan 등을 실행하는 유틸리티 컨테이너 패턴을 알아봅니다
Docker 컨테이너 간 통신, 외부 네트워크 연결, 그리고 다양한 네트워크 드라이버를 활용한 효과적인 네트워킹 구성 방법을 알아봅니다
실제 프로덕션 환경과 같은 다중 컨테이너 애플리케이션을 Docker로 구축하는 방법을 React, Node.js, MongoDB를 활용한 실전 예제로 알아봅니다
Nginx, PHP-FPM, MySQL, Redis를 조합한 프로덕션급 Laravel 개발 환경을 Docker로 구축하는 방법을 상세히 알아봅니다