Docker Compose 패턴
단일 호스트에서 여러 컨테이너 (웹 · DB · 캐시 · 큐) 를 함께 띄울 때 가장 가벼운 길이 Compose. YAML 한 파일로 의존 · 네트워크 · 볼륨 · 헬스체크를 묶음. 본격 오케스트레이터가 아니라는 점이 강점이자 한계. 이 글은 Compose 의 v1 vs v2 차이 · compose.yaml 핵심 키 ·…
목차
단일 호스트에서 여러 컨테이너 (웹 · 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"
YAML anchor 로 일괄 적용 — 같은 logging 블록을 모든 service 에 매번 적기 보다 anchor 한 곳에서 정의하고 참조:
x-logging: &default-logging
driver: json-file
options:
max-size: "10m"
max-file: "3"
services:
web:
logging: *default-logging
db:
logging: *default-logging
x- 접두사는 Compose 의 "확장 필드" — Compose 가 무시하지만 YAML 파서는 anchor 로 인식. 정책 변경의 단일 지점. 무설정 시 json-file 기본이 무제한 누적되므로, anchor 없이 시작해도 결국 모든 service 에 동일 블록을 붙이게 됨. 처음부터 anchor 가 깔끔.
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 을 참고합니다.
이 글에서 만나는 용어
infra 카테고리의 다른 글
카테고리 전체 보기 →관련 글
필수 readiness와 선택 capability를 분리하기
프로세스가 살아 있다는 사실, 핵심 검색 요청을 처리할 준비가 됐다는 사실, 선택 기능이 모두 정상이라는 사실은 서로 다릅니다. 하나의 ready 값에 전부 넣으면 선택 모델 장애가 전체 재시작으로 이어지고, 핵심 DB 장애를 liveness가 가립니다.
Compose readiness와 부분 롤백의 경계
컨테이너가 running이어도 DB 연결·필수 테이블·내부 의존성이 준비되지 않았을 수 있습니다. readiness는 사용자 요청을 받을 수 있는 상태를, liveness는 프로세스가 살아 있는지를 나타내야 합니다.
클라우드 에뮬레이터 스택 — 4번째 환경의 설계
> 아래 다중 에뮬레이터 경로와 명령은 독립 스택 설계를 설명하는 예시입니다. 실제 저장소에 적용할 때는 필요한 공급자와 비용을 먼저 선택하세요.
단일 서버 운영의 자리
서비스가 시작 단계라면 한 대의 VPS 에 docker compose 로 모든 것을 띄우는 구성이 종종 가장 합리적입니다. 운영 인지 부하가 작고 비용이 예측 가능. 이 모델이 어디까지 가능한지 · 언제 한계에 닿는지 · k8s 를 비롯한 다음 단계의 자리.