Docker 시리즈 ⑦ Compose — 여러 컨테이너 한꺼번에 다루기
여러 컨테이너를 단일 명령으로 관리하는 docker compose의 구조, 핵심 옵션, 그리고 실무 활용 패턴을 초보자 눈높이로 정리했다.
Docker 시리즈 ⑦ Compose — 여러 컨테이너 한꺼번에 다루기
시리즈 순서: Host → Image → Container → Network → Volume → Dockerfile → Compose → Security → Swarm
들어가며
웹 서버 + DB + 캐시 — 보통 앱은 한 컨테이너로 끝나지 않는다. 여러 컨테이너를 개별 `docker run`으로 띄우면 명령이 길어지고, 종속 관계 챙기기도 힘들다.
# 매번 이러기 싫다
docker network create my-net
docker volume create db-data
docker run -d --name db --network my-net -v db-data:/var/lib/... postgres
docker run -d --name redis --network my-net redis
docker run -d --name app --network my-net -p 3000:3000 -e DB_HOST=db ... my-app이 모든 걸 YAML 파일 하나로 정리해서 한 명령으로 띄우게 해주는 게 Docker Compose다.
가장 단순한 예시
docker-compose.yml:
services:
app:
image: my-app:latest
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://user:pass@db:5432/mydb
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:실행:
docker compose up -d이 한 줄로:
- 네트워크 자동 생성
- 볼륨 자동 생성
- DB 컨테이너 실행
- App 컨테이너 실행 (DB 시작 후)
- 포트 매핑 / 환경변수 설정 자동 처리
Compose가 자동으로 해주는 것들
1. 네트워크 자동 구성
Compose는 프로젝트마다 사용자 정의 네트워크를 자동으로 만든다. 모든 서비스가 그 네트워크에 들어가서, 서비스 이름으로 서로 부를 수 있다.
services:
app:
environment:
DATABASE_URL: postgres://user:pass@db:5432/mydb
# ↑
# "db" 서비스 이름이 곧 호스트명
db:
image: postgres이게 가능한 이유는 Network 글에서 다룬 사용자 정의 네트워크의 DNS 기능 덕이다.
2. 종속 관계 관리
services:
app:
depends_on:
- db
- redisapp은 db, redis가 시작된 후에 시작된다.
주의:depends_on은 "컨테이너가 떠 있는지"만 보장하지, "준비가 됐는지"는 보장 안 한다. DB가 부팅 중인데 앱이 연결을 시도해서 실패할 수 있다. 이건healthcheck로 해결한다 (아래 참고).
3. 볼륨 / 환경변수 일괄 관리
.env 파일 하나로 여러 서비스의 환경변수 관리 가능.
주요 설정 항목
image vs build
services:
# 1. 기존 이미지 사용
redis:
image: redis:7-alpine
# 2. 직접 빌드
app:
build:
context: .
dockerfile: Dockerfile
args:
NODE_VERSION: 20ports — 포트 매핑
ports:
- "3000:3000" # 호스트:컨테이너
- "127.0.0.1:8080:80" # 로컬 한정environment — 환경변수
environment:
NODE_ENV: production
DATABASE_URL: ${DATABASE_URL} # .env 파일에서 읽기
# 또는 파일로 한꺼번에
env_file:
- .envvolumes
services:
db:
volumes:
- db-data:/var/lib/postgresql/data # named volume
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro # bind mount
volumes:
db-data: # 명시적 선언restart — 재시작 정책
restart: unless-stopped # 가장 많이 씀
# no | always | on-failure | unless-stoppednetworks — 네트워크 분리
여러 네트워크를 만들어 서비스를 분리할 수 있다.
services:
app:
networks:
- frontend
- backend
db:
networks:
- backend # frontend엔 안 보임
networks:
frontend:
backend:healthcheck
services:
db:
image: postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
timeout: 3s
retries: 5
app:
depends_on:
db:
condition: service_healthy # db가 healthy일 때만 시작이게 depends_on만 쓸 때의 한계를 해결해준다.
자주 쓰는 명령어
# 시작
docker compose up # 포어그라운드
docker compose up -d # 백그라운드 (가장 많이 씀)
docker compose up --build # 이미지 새로 빌드 후 시작
# 정지
docker compose stop # 멈춤만 (제거 X)
docker compose down # 멈춤 + 컨테이너/네트워크 제거
docker compose down -v # 볼륨까지 삭제 (주의!)
# 상태 보기
docker compose ps # 서비스 상태
docker compose logs # 로그 (모든 서비스)
docker compose logs -f app # 특정 서비스만 실시간
# 명령 실행
docker compose exec app sh # 컨테이너 안 셸
docker compose run --rm app yarn test # 일회성 실행
# 빌드
docker compose build # 이미지만 빌드
docker compose pull # 외부 이미지만 받기환경별 설정 분리
개발/스테이징/운영에서 설정이 다른 경우.
방법 1: override 파일
docker-compose.yml (공통):
services:
app:
image: my-app
environment:
NODE_ENV: productiondocker-compose.override.yml (개발 자동 적용):
services:
app:
build: .
environment:
NODE_ENV: development
volumes:
- .:/app # 소스 마운트로 핫리로드docker compose up을 그냥 실행하면 두 파일이 자동으로 합쳐진다.
방법 2: 명시적 파일 지정
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d방법 3: profile
services:
app:
image: my-app
debug:
image: debug-tools
profiles: ["debug"] # 평소엔 안 뜸docker compose up # app만
docker compose --profile debug up # debug까지 같이실전 예시
예시 1: Next.js + PostgreSQL + Redis
services:
app:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://user:pass@db:5432/mydb
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
retries: 5
restart: unless-stopped
cache:
image: redis:7-alpine
restart: unless-stopped
volumes:
db-data:예시 2: 개발 환경 핫 리로드
services:
app:
build:
context: .
target: dev # Dockerfile의 dev stage
ports:
- "3000:3000"
volumes:
- .:/app # 소스 변경 즉시 반영
- /app/node_modules # node_modules는 컨테이너 것 사용
command: yarn dev헷갈리는 포인트
❓ "docker-compose와 docker compose 차이는?"
docker-compose(하이픈): 옛날 v1, Python으로 작성, 별도 설치 필요docker compose(공백): 새로운 v2, Go로 재작성, Docker에 내장
v2를 쓰자. Docker Desktop이나 최신 Docker Engine엔 기본 포함이다.
❓ "왜 depends_on이 있는데도 앱이 DB 연결에 실패해요?"
depends_on은 컨테이너가 시작됐는지만 본다. DB가 연결을 받을 준비가 됐는지는 다름. healthcheck + condition: service_healthy를 같이 써야 한다.
❓ "환경변수가 적용이 안 돼요"
.env 파일은 Compose 파일과 같은 위치에 있어야 한다. 이름도 정확히 .env.
# docker-compose.yml에서 변수 사용
environment:
DATABASE_URL: ${DATABASE_URL}# .env 파일
DATABASE_URL=postgres://...❓ "down 했더니 데이터가 사라졌어요"
docker compose down은 컨테이너만 지운다. 볼륨은 살아있다. 하지만 -v 옵션을 붙이면 볼륨도 같이 삭제된다.
docker compose down # 볼륨 유지 (안전)
docker compose down -v # 볼륨까지 삭제 (조심!)❓ "이미지를 새로 빌드했는데 적용이 안 돼요"
docker compose up -d # 기존 이미지 그대로 씀
docker compose up -d --build # 이미지 재빌드 ✅정리
- Compose = 여러 컨테이너를 YAML 하나로 관리
- 서비스 이름이 그대로 호스트명 (네트워크 자동 구성)
depends_on+healthcheck로 시작 순서 제어up -d,down,logs -f만 알아도 80% 활용- override 파일로 환경별 설정 분리
- Compose는 단일 호스트용이다 → 멀티 호스트가 필요하면 다음 단계: Swarm
다음 글에선 컨테이너 보안의 기본 — 비밀번호/시크릿 관리, non-root 실행, 이미지 스캔 등을 다룬다.
이전 글: Docker 시리즈 ⑥ Dockerfile 다음 글: Docker 시리즈 ⑧ Security — 컨테이너 보안의 기본
Related Posts
같이 읽으면 좋은 글
Docker 시리즈 ⑨ Swarm — 여러 호스트로 확장하기, 그리고 Kubernetes로
Docker Swarm의 핵심 개념(Service, Task, Stack)을 정리하고, 왜 결국 Kubernetes로 넘어가게 되는지 실제 프로젝트 경험을 바탕으로 정리했다.
Docker 시리즈 ⑧ Security — 컨테이너 보안의 기본
컨테이너 보안의 기초 — non-root 실행, 시크릿 관리, .dockerignore, 이미지 스캔, 리소스 제한까지 초보자도 챙겨야 할 핵심을 정리했다.
Docker 시리즈 ⑥ Dockerfile — 이미지를 만드는 레시피
Dockerfile 명령어 전체 정리 — CMD vs ENTRYPOINT, COPY vs ADD, ARG vs ENV의 차이와 레이어 캐시 최적화 패턴까지 초보자 눈높이로 다룬다.