Docker Compose 는 컨테이너 여러 개를 한 번에 정의·실행하는 도구. 로컬 개발 환경의 사실상 표준이고, 작은 production 배포에도 종종 쓰인다. 이 가이드는 docker-compose.yml 의 구조, top-level 키 5 종의 역할, depends_on 과 healthcheck 의 차이, named volume vs bind mount, 그리고 개발 환경을 깨뜨리는 함정을 정리한다.
docker-compose.yml 의 5 가지 최상위 키
# v3+ 형식 — version 키는 더 이상 권장 X
services: # 1. 컨테이너 정의 (필수)
networks: # 2. 네트워크 정의
volumes: # 3. named volume 정의
configs: # 4. Swarm 설정 (선택)
secrets: # 5. Swarm 시크릿 (선택)모든 매니페스트는 services 가 핵심. 다른 4 개는 services 가 참조할 때만 의미. 한 파일에 같이 박힘.
services — 컨테이너 정의
services:
web:
image: nginx:1.27 # 이미지 또는 build
container_name: my-web # 컨테이너 이름 (선택)
ports:
- "80:80" # host:container
- "443:443"
environment: # env 변수
LOG_LEVEL: info
DATABASE_URL: postgres://db/app
env_file: # .env 파일에서 가져옴
- .env
depends_on: # 시작 순서
- db
networks: # 어떤 네트워크에 연결
- frontend
volumes: # 어떤 디스크 마운트
- ./static:/usr/share/nginx/html
- logs:/var/log/nginx
restart: unless-stopped # 재시작 정책
healthcheck: # 헬스 체크
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 3s
retries: 3서비스 이름 (위 예의 web) 이 컨테이너의 DNS 이름이 됨 — 같은 Compose 안 다른 서비스가 http://web 로 호출 가능. 별도 DNS 설정 0.
image vs build
- image: Docker Hub 또는 private registry 의 이미지를 pull.
image: postgres:16. - build: 로컬 Dockerfile 에서 빌드.
build: ./api또는build: {context: ., dockerfile: Dockerfile.dev}.
둘 다 박으면 build 가 우선 — 빌드된 이미지를 image 이름으로 tag. 일반적 으로 둘 중 하나만.
networks — 서비스 간 격리
networks:
frontend:
driver: bridge # 기본값
backend:
driver: bridge
internal: true # 외부 차단 (내부 통신만)
monitoring:
external: true # 이미 존재하는 외부 네트워크같은 네트워크에 속한 서비스만 서로 보임. 위 예: backend 에 박힌 db 는 frontend 의 web 에서 직접 호출 불가. DMZ 패턴 자연 구현.
Compose 가 자동으로 default network (이름: <project>_default) 생성. networks: 안 쓰면 모든 서비스가 자동으로 같은 네트워크에 묶임.
volumes — 영속 데이터
Named volume (권장)
services:
db:
image: postgres:16
volumes:
- db-data:/var/lib/postgresql/data # named: 마운트
volumes:
db-data: # top-level 선언- Docker 가 관리. 위치는
/var/lib/docker/volumes/<project>_db-data/. docker compose down으로 컨테이너 삭제해도 볼륨은 남음.docker compose down -v로 명시적으로 삭제.- 백업·migration 친화. 환경 이식성 ↑.
Bind mount (host 파일시스템 직접 마운트)
volumes:
- ./src:/app/src # host ./src → container /app/src
- /etc/timezone:/etc/timezone:ro # read-only- 호스트 OS 의 특정 경로를 마운트.
- 코드 hot reload (dev 환경) 에 필수.
- OS 의존성·권한 충돌 위험. production 비추.
tmpfs (메모리)
services:
api:
tmpfs:
- /tmp # RAM 마운트 (재시작 시 사라짐)구조 확인 — Docker Compose 시각화 에 매니페스트 붙여넣으면 named volume 이 어떤 서비스에 연결됐는지 한눈에. bind mount 는 그래프에 안 나타남 (host 의존이라 Compose 가 관리하는 리소스 아님).
depends_on — 시작 순서 ≠ 준비 상태
services:
api:
depends_on:
- db
db:
image: postgres:16Compose 가 db 먼저 시작 → 그 다음 api. 하지만 db 가 받을 준비 가 됐는지는 보장 안 됨. 컨테이너 시작 ≠ 프로세스 ready.
해결: long form + condition 으로 healthcheck 대기.
services:
api:
depends_on:
db:
condition: service_healthy # db 의 healthcheck 통과까지 대기
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5condition 값:
service_started(기본) — 컨테이너 시작만service_healthy— healthcheck 통과까지service_completed_successfully— 종료까지 (Job 패턴)
environment vs env_file vs secrets
environment
environment:
- LOG_LEVEL=info # array 형식
- DATABASE_URL=postgres://...
# 또는 map 형식
environment:
LOG_LEVEL: info
DATABASE_URL: postgres://...매니페스트에 평문. 시크릿 절대 박지 말 것 — git 에 commit 되면 노출.
env_file
env_file:
- .env
- .env.local.env 파일에서 변수 로드. .env 는 gitignore 대상. dev 환경 표준.
secrets (Swarm)
services:
db:
secrets:
- db-password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db-password
secrets:
db-password:
file: ./secrets/db-password.txtsecret 파일이 /run/secrets/<name> 으로 마운트. 환경 변수보다 안전 — 프로세스 환경에 노출 X, docker inspect 결과에도 안 보임.
ports — host : container
ports:
- "8080:80" # host 8080 → container 80
- "127.0.0.1:8080:80" # localhost 바인딩 (외부 차단)
- "8080:80/udp" # UDP
- "8000-8010:8000-8010" # 범위ports = 외부 노출. 같은 Compose 안 서비스끼리는 ports 없 어도 서로 통신 가능 (default network 의 DNS 로). ports 는 호스트와 외부 클라이언트용.
실전 — 풀스택 dev 환경
services:
web:
build: ./web
ports: ["3000:3000"]
volumes:
- ./web/src:/app/src # hot reload
depends_on:
api:
condition: service_started
environment:
NEXT_PUBLIC_API_URL: http://localhost:8000
api:
build: ./api
ports: ["8000:8000"]
volumes:
- ./api:/app
depends_on:
db:
condition: service_healthy
environment:
DATABASE_URL: postgres://app:secret@db:5432/app
db:
image: postgres:16
ports: ["5432:5432"] # 로컬 DBeaver 접속용
volumes:
- db-data:/var/lib/postgresql/data
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
retries: 5
cache:
image: redis:7-alpine
ports: ["6379:6379"]
volumes:
db-data:실제로 매니페스트가 어떻게 연결되는지는 Docker Compose 시각화 에 위 YAML 을 넣으면 그래프로 즉시 보임.
주요 명령
docker compose up -d # 백그라운드 시작
docker compose up --build # 이미지 다시 빌드 후 시작
docker compose down # 컨테이너 + 네트워크 삭제
docker compose down -v # + 볼륨도 삭제 (주의)
docker compose ps # 상태 확인
docker compose logs -f api # 특정 서비스 로그
docker compose exec api bash # 컨테이너 안 셸
docker compose restart api # 단일 서비스 재시작
docker compose pull # 이미지 갱신Compose vs Kubernetes
| 축 | Docker Compose | Kubernetes |
|---|---|---|
| 대상 | 단일 호스트, 로컬 개발 | 클러스터, production |
| 학습 곡선 | 가파름 X (수 시간) | 가파름 (수 주) |
| HA / 스케일링 | 제한적 (Swarm 모드) | 1급 (Deployment / HPA) |
| 매니페스트 | 1 파일 (compose.yml) | 여러 파일 (Helm / Kustomize) |
Compose → K8s 마이그레이션 시 kompose convert 같은 도구가 매핑 자동화. Kubernetes YAML 시각화 와 Docker Compose 시각화 둘 다 같은 시각화 패턴이라 구조 비교 직관적.
흔히 빠지는 함정
1. depends_on 만으로 ready 가정
API 가 시작 즉시 DB connect 시도 → DB 가 listener 띄우는 중 → connection refused. healthcheck + condition: service_healthy 필수.
2. environment 에 시크릿
매니페스트 commit 시 시크릿 노출. env_file + .env (gitignore) 또는 secrets 사용.
3. bind mount 의 권한
호스트의 디렉토리 권한이 컨테이너 안 user 와 안 맞아 write 실패. macOS/ Windows 의 Docker Desktop 은 자동 해결하지만 Linux 는 직접 처리 필요 (user: "${UID}:{GID}").
4. version: "3" 박기
Compose v2+ (CLI 도구 자체) 부터 version: 키 무시. 경고 나옴. 새 매니페스트에서는 빼는 것이 표준.
5. 같은 host port 두 서비스
ports: ["8080:80"] 가 두 서비스에 있으면 두 번째 가 실패. host port 충돌. 한 서비스만 외부 노출 또는 다른 port.
6. networks 안 쓰면 모든 서비스가 같은 망
보안 격리 0. production 또는 보안 민감 환경에서는 명시적 frontend / backend 분리.
7. docker compose down -v 실수
named volume 까지 삭제. DB 데이터 날아감. dev 에서도 가끔 사고 발생. backup 정책 권장.
8. v1 / v2 명령 혼동
옛 docker-compose (하이픈, Python) vs 새 docker compose (공백, Go 플러그인). 새 환경은 무조건 후자. 옛 명령은 2024 년 deprecated.
요약
- services / networks / volumes / configs / secrets — 5 가지 top-level 키. services 가 핵심.
- 서비스 이름 = 컨테이너 DNS 이름. 같은 네트워크 안 자동 통신.
- depends_on 은 시작 순서만. ready 보장 = healthcheck + condition: service_healthy.
- named volume = Docker 관리, bind mount = host 의존. production 은 named.
- 시크릿은 environment X, env_file (.env gitignore) 또는 secrets.
- 구조 확인은 Docker Compose 시각화 — 5 분 안에 그래프 + 의존성 파악.