Docker Compose 패턴
Docker Compose 패턴
단일 호스트에서 여러 컨테이너 (웹 · DB · 캐시 · 큐) 를 함께 띄울 때 가장 가벼운 길이 Compose. YAML 한 파일로 의존 · 네트워크 · 볼륨 · 헬스체크를 묶음. 본격 오케스트레이터가 아니라는 점이 강점이자 한계. 이 글은 Compose 의 v1 vs v2 차이 · compose.yaml 핵심 키 · healthcheck · env_file · profiles · override 패턴.
1. Compose 에 대한 이야기
Compose 는 원래 Fig 라는 외부 도구 (2014) 였다가 Docker 가 인수해 흡수. v1 은 Python 으로 작성된 별도 CLI (docker-compose), v2 (2021) 부터 Go 로 재작성되어 docker compose 서브커맨드로 통합. v2 는 BuildKit · 프로필 · 더 빠른 실행이 차이.
권장 파일명은 compose.yaml 또는 compose.yml. 이전 관례인 docker-compose.yml 도 인식.
2. 기본 구조
services:
web:
build: ./web
ports:
- "127.0.0.1:8080:8080"
environment:
DATABASE_URL: postgres://app:${DB_PASS}@db:5432/app
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: ${DB_PASS}
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
volumes:
db-data:
3. 핵심 키
| 키 | 역할 |
|---|---|
services |
컨테이너 단위 정의. |
volumes |
명명된 볼륨. |
networks |
사용자 정의 네트워크 (미정의 시 default 자동 생성). |
secrets |
비밀값 마운트 (파일 또는 외부 secret). |
configs |
설정 파일 마운트. |
profiles |
선택적 서비스 묶음. |
4. 의존과 헬스체크
depends_on 만으로는 "다른 서비스가 살아 있다" 보장이 약함. 네트워크가 떴다 해도 DB 가 실제 쿼리 받을 준비 안 됐을 수 있음. v2 에서는 condition: service_healthy 와 healthcheck 결합으로 진짜 준비 상태 확인.
5. env_file 과 ${VAR} 치환
services:
web:
env_file: .env
environment:
LOG_LEVEL: ${LOG_LEVEL:-info}
env_file 은 컨테이너 안의 환경변수로 주입. ${VAR} 치환은 Compose 가 .env 파일과 셸 환경에서 읽어 YAML 자체를 채움. 두 자리는 의미가 다름 — 하나는 컨테이너 환경, 하나는 Compose 파일의 텍스트 치환.
6. profiles
특정 환경에서만 띄우고 싶은 서비스에 프로필을 붙임:
services:
app: { ... }
pgadmin:
image: dpage/pgadmin4
profiles: ["dev"]
docker compose --profile dev up 으로 dev 프로필 서비스까지 함께. 운영에서 불필요한 도구를 자연스럽게 빼는 데 유용.
7. override 파일
compose.yaml 옆의 compose.override.yaml 은 자동 병합. 환경별 분기에는 명시적 파일을 쓰는 편이 명확:
docker compose -f compose.yaml -f compose.prod.yaml up -d
뒤에 오는 파일이 앞을 덮어씀. 개발 / 스테이징 / 운영 차이를 작은 패치 파일로 분리하는 패턴이 흔합니다.
8. extends
다른 파일의 서비스 정의를 상속 · 합성. 큰 모노레포에서 공통 베이스를 정의하는 자리:
services:
app:
extends:
file: common.yaml
service: app-base
9. 다른 길
Compose 와 비교 대상은 단일 호스트 한정.
- systemd unit + Docker — 서비스 정의를 systemd 가 관리. 의존 그래프는 systemd 의
After=·Requires=. - Podman + quadlet — Podman 의 systemd 통합.
*.container파일에서 컨테이너 정의. - Nomad (HashiCorp) — 단일 바이너리의 가벼운 오케스트레이터. 단일 호스트 ~ 수백 노드.
- Kubernetes — 다른 무게 클래스. 단일 호스트에 쓰면 운영 비용이 과하다는 평.
여러 호스트로 나가는 순간 Compose 는 부족. Swarm · Nomad · k8s 로 옮김.
10. 단일 호스트 적합성
장점 — 학습 비용 작음, 단일 파일에 모든 정의, 빠른 시작 / 종료.
한계 — 단일 호스트 (수평 확장 없음). 무중단 배포는 외부 도구나 수동 절차 필요. 시크릿 관리 · 롤링 업그레이드 약함.
대다수 사이드 프로젝트 · 중소 서비스는 단일 호스트 + Compose 로 충분.
11. 자주 쓰는 모양
빌드와 이미지 분리:
services:
app:
image: ghcr.io/my/app:1.4.2
build: ./app
CI 에서 docker compose build 후 push. 운영 호스트에서는 pull. image 와 build 를 함께 두면 두 흐름이 자연스럽게 합쳐짐.
로깅 · 재시작 — 기본 로그 드라이버의 디스크 무한 증가 방지:
services:
app:
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
12. 자주 걸리는 자리
포트 바인딩 주소 누락 — "8080:8080" 은 0.0.0.0 에 바인딩되어 외부 접근 가능. 운영 보안 모델에서는 "127.0.0.1:8080:8080" 이 적절할 때가 많음.
depends_on 만으로 충분하다는 오해 — condition 없이는 단순 시작 순서만 보장.
볼륨 vs 바인드 마운트의 권한 — 호스트 사용자 (uid) 와 컨테이너 사용자가 다르면 쓰기 실패가 자주.
.env 파일의 보안 — Compose 의 ${VAR} 치환을 위해 평문으로 저장. 시크릿은 별도 도구 (Docker secret · age · sops) 로 분리.
이름 충돌 — 같은 호스트에서 여러 Compose 프로젝트를 돌릴 때 프로젝트 이름이 같으면 컨테이너 · 네트워크가 섞임. --project-name 또는 디렉터리 분리.
하고픈 말
Compose 는 단일 호스트의 모든 자리를 단일 YAML 로 묶는 가장 직관적인 도구. healthcheck + condition: service_healthy + 127.0.0.1 바인딩 + override 파일 분기 네 자리가 함께 있을 때 운영의 안정성이 크게 좋아집니다.
Next
- caddy-not-nginx
- loopback-ssh-tunnel
Compose 공식 문서 · Compose Specification · Compose 파일 레퍼런스 · Healthcheck 문서 · Profiles 문서 · Podman + quadlet 을 참고합니다.