컨테이너가 여러 개가 되면 docker run 명령을 매번 순서대로 치기 어렵습니다. Docker Compose는 서비스, 네트워크, 볼륨을 YAML 파일 하나에 적고 한 번에 올리고 내리는 도구입니다. 이 글에서는 웹 앱과 Redis로 방문자 수를 세는 예제를 compose.yaml로 만들고, depends_on과 healthcheck, 환경별 설정 분리를 정리합니다.
컨테이너부터 Kubernetes까지 시리즈 5편이자 1부 Docker의 마지막 글입니다. 이전 글은 [Docker] 컨테이너 네트워크와 볼륨 정리이고, 전체 순서는 시리즈 목차에 있습니다.
1. Compose가 하는 일
4편에서는 네트워크를 만들고, 볼륨을 만들고, 컨테이너를 순서대로 띄우는 명령을 하나씩 실행했습니다. Compose는 이 내용을 compose.yaml에 적어 두고 docker compose up 한 번으로 같은 상태를 만듭니다. 요리 순서를 매번 말로 설명하는 대신 레시피 카드를 한 장 건네는 것과 같습니다.
| 항목 | docker run 옵션 | compose.yaml 키 |
| 이미지 | docker run redis:8-alpine | image |
| Dockerfile로 빌드 | docker build 후 실행 | build |
| 포트 매핑 | -p 8000:8000 | ports |
| 볼륨 | -v cache-data:/data | volumes |
| 환경 변수 | -e KEY=VALUE | environment, env_file |
| 네트워크 | docker network create, --network | 생략하면 프로젝트 네트워크 자동 생성 |
| 재시작 정책 | --restart unless-stopped | restart |
Compose는 폴더 이름을 프로젝트 이름으로 써서 리소스 이름 앞에 붙입니다. demo 폴더에서 실행하면 네트워크는 demo_default, 컨테이너는 demo-web-1, 볼륨은 demo_cache-data가 됩니다. 서비스 이름(web, cache)은 그대로 DNS 이름이 되어 서로 이름으로 접속합니다.

compose.yaml 하나로 네트워크, 컨테이너 두 개, 볼륨이 한 번에 만들어집니다
2. 시작 순서와 healthcheck
depends_on은 서비스를 시작하는 순서를 정합니다. 그런데 조건 없이 쓰면 Redis 컨테이너가 "시작"되기만 하면 웹 앱을 띄우므로, Redis가 아직 접속을 받을 준비가 안 됐을 때 웹 앱이 먼저 접속을 시도할 수 있습니다. condition: service_healthy를 주면 Redis의 healthcheck가 통과할 때까지 기다린 뒤 웹 앱을 시작합니다.

service_healthy 조건은 "켜짐"이 아니라 "준비 완료"를 기다립니다
식당에 비유하면 조건 없는 depends_on은 주방 불이 켜지자마자 손님을 받는 것이고, service_healthy는 불판이 달궈졌는지 확인한 뒤 손님을 받는 것입니다. 다만 이 조건은 docker compose up으로 띄울 때의 순서를 정하는 것이라, 운영 중에 Redis가 잠깐 끊겼을 때 다시 접속하는 처리는 앱에 따로 있어야 합니다.
3. 직접 해 보기
demo 폴더에 파일 4개(app.py, requirements.txt, Dockerfile, compose.yaml)를 만듭니다. 먼저 접속할 때마다 Redis의 visits 값을 1씩 올려 보여 주는 웹 앱입니다.
# app.py
from flask import Flask
import redis
app = Flask(__name__)
r = redis.Redis(host="cache", port=6379)
@app.get("/")
def index():
return f"visits: {r.incr('visits')}\n"
requirements.txt에는 flask와 redis 두 줄을 적습니다. Dockerfile은 3편과 같은 방식으로, 의존성 목록을 먼저 복사해 설치한 뒤 소스를 복사합니다.
FROM python:3.14-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 8000
CMD ["flask", "run", "--host=0.0.0.0", "--port=8000"]
compose.yaml에는 두 서비스와 볼륨을 적습니다. cache 서비스에 healthcheck를 두고, web은 cache가 healthy가 된 뒤에 시작합니다.
services:
web:
build: .
ports:
- "8000:8000"
depends_on:
cache:
condition: service_healthy
cache:
image: redis:8-alpine
command: ["redis-server", "--appendonly", "yes"]
volumes:
- cache-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
volumes:
cache-data:
예전 글에서 보던 맨 위의 version: 키는 지금의 Compose에서 쓰지 않으므로 적지 않습니다. 적으면 무시된다는 경고만 나옵니다. 이제 올리고 확인합니다.
docker compose up -d --build
docker compose ps
curl localhost:8000
docker compose logs web
up 로그에서 cache가 Healthy가 된 다음 web이 Started 되는 순서를 볼 수 있습니다. docker compose ps에서 cache의 STATUS에 (healthy)가 붙고, curl을 실행할 때마다 visits 숫자가 1씩 올라갑니다.
docker compose down은 컨테이너와 네트워크를 지우지만 볼륨은 남깁니다. 그래서 다시 up 하면 visits 숫자가 이어서 올라갑니다. 볼륨까지 지우려면 docker compose down -v를 씁니다.
4. 환경별 설정 나누기
개발과 운영에서 포트, 비밀번호, 리소스 제한이 다를 때 compose.yaml을 복사해서 두 벌 관리하면 금방 어긋납니다. Compose는 공통 파일 하나에 환경별 차이만 덧붙이는 방법을 제공합니다.
| 방식 | 동작 | 용도 |
| .env 파일 | 같은 폴더의 .env 값을 compose.yaml의 ${변수}에 채움 | 포트, 이미지 태그 같은 값 |
| compose.override.yaml | 있으면 자동으로 compose.yaml 위에 덮어씀 | 개발용 설정(소스 바인드 마운트 등) |
| -f 여러 개 | docker compose -f compose.yaml -f compose.prod.yaml up 뒤 파일이 앞 파일을 덮어씀 |
운영용 설정 |
| profiles | 서비스에 profile을 달고 --profile로 켤 때만 실행 | 디버그 도구, 관리 화면 |
docker compose config를 실행하면 파일들을 합치고 변수를 채운 최종 결과를 보여 줍니다. 실제로 무엇이 적용되는지 확인할 때 가장 먼저 쓰는 명령입니다.
5. 운영 시 주의점
| 항목 | 조치 |
| 명령 이름 | docker-compose(V1)는 2023년에 업데이트가 끝났으므로 docker compose(V2) 사용 |
| version: 키 | 지금의 Compose는 무시하므로 적지 않음 |
| 시작 순서 | depends_on은 처음 시작 순서만 보장 운영 중 재접속은 앱에서 처리 |
| 비밀번호 | .env에 넣은 값은 Git에 올리지 않음(.gitignore) docker compose config 출력에도 값이 그대로 보임 |
| down -v | 볼륨까지 지워 DB 데이터가 사라짐 운영 서버에서는 쓰지 않음 |
| 프로젝트 이름 | 같은 폴더 이름이면 리소스가 겹침 -p 옵션이나 name 키로 구분 |
| 여러 서버 | Compose는 한 호스트 안에서만 동작 여러 서버에 나눠 띄우고 자동 복구하려면 Kubernetes 필요 |
6. 정리
- Compose는 docker run으로 하나씩 만들던 네트워크, 볼륨, 컨테이너를 compose.yaml 하나로 묶어 한 번에 올리고 내립니다.
- depends_on에 condition: service_healthy를 주면 의존 서비스가 준비된 뒤에 시작하지만, 운영 중 재접속은 앱이 처리해야 합니다.
- 환경별 차이는 .env와 override 파일로 나누고, docker compose config로 최종 결과를 확인합니다.
여기까지가 1부 Docker입니다. 2부에서는 Compose가 한 서버 안에서 하던 일을 여러 서버에 걸쳐 해 주는 Kubernetes의 구조부터 다룹니다.
참고
'DevOps > Docker' 카테고리의 다른 글
| [Docker] 컨테이너부터 Kubernetes까지 학습 순서 정리 (0) | 2026.09.28 |
|---|---|
| [Docker] 컨테이너 네트워크와 볼륨 정리 (0) | 2026.09.28 |
| [Docker] Dockerfile 작성과 멀티 스테이지 빌드 정리 (0) | 2026.09.28 |
| [Docker] 이미지와 레이어 정리 (0) | 2026.09.28 |
| [Docker] 컨테이너 개념 정리 (0) | 2026.09.28 |