Docker Compose

StrayCat·2026년 3월 14일

들어가며

Docker를 어느 정도 다루다 보면, 자연스럽게 하나의 의문에 도달한다.

"컨테이너가 여러 개인데, 매번 하나씩 docker run 해야 하나?"

웹 애플리케이션 하나를 띄우려 해도 WAS, DB, 리버스 프록시 등 최소 2~3개의 컨테이너가 필요한 경우가 많다. 이들을 개별적으로 실행하고, 네트워크를 연결하고, 환경 변수를 주입하는 작업을 반복하는 것은 비효율적이고 실수를 유발하기 쉽다.

Docker Compose는 바로 이 문제를 해결하기 위해 존재한다.


1. Docker Compose란

Docker Compose는 다중 컨테이너 Docker 애플리케이션을 정의하고 실행하기 위한 도구이다. YAML 파일 하나에 서비스, 네트워크, 볼륨 등을 선언적으로 기술하고, 단일 명령어로 전체 스택을 올리거나 내릴 수 있다.

핵심 가치를 한 문장으로 정리하면 이렇다:

"인프라 구성을 코드로 관리한다(Infrastructure as Code)."

docker-compose.yml (혹은 compose.yaml) 파일 하나가 곧 인프라 명세서 역할을 하기 때문에, 팀원 누구나 동일한 환경을 재현할 수 있다. 온보딩(새 팀원이 프로젝트에 합류하는 과정) 시에도 docker compose up 한 줄이면 개발 환경이 세팅된다는 점은 상당한 이점이다.


2. 설치

Docker 20.10 버전부터 Docker Compose가 Docker CLI의 플러그인으로 기본 내장되었다. 따라서 최신 Docker Desktop이나 Docker Engine을 설치했다면 별도 설치 과정이 없다.

여기서 한 가지 짚고 넘어갈 것이 있다.

# 구 버전 (Compose V1) — Python 기반, 별도 설치 필요
docker-compose up

# 현재 버전 (Compose V2) — Go 기반, Docker CLI 내장
docker compose up

하이픈(-)이 빠진 것이 단순한 표기 변경이 아니다. V1은 Python으로 작성된 독립 바이너리였고, V2는 Go로 재작성되어 Docker CLI의 서브커맨드로 통합된 것이다. 2026년 현재, V1은 완전히 EOL(End of Life) 상태이므로 docker compose (공백 구분) 형태를 사용해주자.

레거시 프로젝트에서 CI 스크립트나 Makefile에 docker-compose(하이픈 포함)가 남아 있는 경우가 있다. 마이그레이션 시 반드시 확인하자.


3. Compose 파일 구조

3.1 기본 골격

# compose.yaml (권장 파일명)
services:
  web:
    image: nginx          # Docker Hub의 공식 이미지 사용
    ports:
      - "8080:80"         # 호스트 8080 → 컨테이너 80 포트 매핑

  app:
    build: .              # 현재 디렉토리의 Dockerfile로 이미지 빌드
    ports:
      - "8081:8080"
    depends_on:
      - db                # db 서비스가 먼저 시작되어야 함

  db:
    image: postgres       # PostgreSQL 공식 이미지
    environment:
      POSTGRES_PASSWORD: example  # 환경 변수 설정

3.2 주요 속성 정리

속성설명
services애플리케이션을 구성하는 각 컨테이너(서비스)를 정의하는 최상위 요소
image사용할 Docker 이미지명. Docker Hub 또는 프라이빗 레지스트리에서 Pull
buildDockerfile 경로를 지정하여 이미지를 직접 빌드
ports"호스트포트:컨테이너포트" 형식의 포트 매핑
depends_on서비스 간 시작 순서를 제어. 단, 애플리케이션 레벨의 준비 완료(readiness)를 보장하지는 않는다
environment컨테이너 내부 환경 변수 설정
volumes호스트와 컨테이너 간 데이터 공유 또는 영속성(persistence) 확보

3.3 version 필드 변경사항

예제에는 version: '3'이 포함되어 있지만, 2026년 현재 version 필드는 완전히 obsolete(폐기)되었다. Docker Compose V2는 항상 최신 Compose Specification 스키마를 자동 적용하므로, 버전 명시가 불필요하다.

# ❌ 구 방식 — 경고 메시지가 출력된다
version: '3'
services:
  web:
    image: nginx

# ✅ 현재 권장 방식 — version 필드 생략
services:
  web:
    image: nginx

version 필드가 남아 있으면 다음과 같은 경고가 출력된다:

WARN: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion

동작에는 영향을 주지 않지만, 불필요한 경고를 제거하고 파일을 깔끔하게 유지하는 것이 좋다.

기존 프로젝트를 인수받았을 때 version: '2.x'나 version: '3.x'가 남아 있는 경우가 흔하다. 제거해도 기능에 영향이 없으므로, 정리하는 것을 권장한다.

3.4 파일명 컨벤션

Docker Compose는 파일을 다음 우선순위로 탐색한다:

  1. compose.yaml (공식 권장)
  2. compose.yml
  3. docker-compose.yaml (하위 호환)
  4. docker-compose.yml (하위 호환)

새 프로젝트라면 compose.yaml을 사용하는 것이 표준이다. 다만 기존에 docker-compose.yml로 운영 중인 프로젝트가 대부분이므로, 무리하게 파일명을 변경할 필요까지는 없다.


4. 핵심 명령어

4.1 서비스 시작

# 포그라운드 실행 (로그가 터미널에 출력됨)
docker compose up

# 백그라운드(Detached) 실행 — 실제 개발 환경에서 주로 사용
docker compose up -d

# 특정 경로의 Compose 파일을 지정하여 실행
docker compose -f /path/to/compose.yaml up -d

4.2 서비스 중지 및 정리

# 컨테이너, 네트워크 등을 중지하고 제거
docker compose down

# 볼륨까지 함께 제거 (DB 데이터 등이 삭제되므로 주의)
docker compose down -v

down과 stop의 차이를 명확히 알아두자.

  • docker compose stop — 컨테이너를 정지만 한다. 컨테이너 자체는 남아 있다.
  • docker compose down — 컨테이너를 정지하고 제거까지 한다. 네트워크도 함께 정리된다.

4.3 빌드 / 상태 확인 / 로그

# Dockerfile 기반 서비스의 이미지를 (재)빌드
docker compose build

# 현재 실행 중인 서비스 목록과 상태 확인
docker compose ps

# 서비스 로그 확인
docker compose logs

# 특정 서비스의 로그만 실시간 추적 (-f: follow)
docker compose logs -f app

5. 실습 흐름 예시

실제로 Compose를 활용하는 전형적인 흐름을 정리하면 다음과 같다.

Step 1 — Compose 파일 작성

# compose.yaml
services:
  web:
    image: nginx
    ports:
      - "8080:80"

  app:
    build: .              # 현재 디렉토리에 Dockerfile 필요
    ports:
      - "8081:8080"
    depends_on:
      - db

  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: example

Step 2 — Dockerfile 작성 (app 서비스용)

# Java 애플리케이션을 컨테이너화하는 Dockerfile
FROM openjdk:11-jre-slim
# 빌드된 JAR 파일을 컨테이너 내부로 복사
COPY target/myapp.jar /app/myapp.jar
# 작업 디렉토리 설정
WORKDIR /app
# 컨테이너 시작 시 실행할 명령어
ENTRYPOINT ["java", "-jar", "myapp.jar"]

Step 3 — 서비스 시작 및 확인

# 전체 서비스를 백그라운드로 기동
docker compose up -d

# 상태 확인
docker compose ps

# 로그 확인
docker compose logs

# 작업 종료 후 정리
docker compose down

이 세 단계가 Compose를 사용하는 가장 기본적인 워크플로우이다.


6. depends_on 에 대해

depends_on은 컨테이너의 시작 순서만 보장한다. 즉, db 컨테이너가 "시작"되었다고 해서 PostgreSQL이 커넥션을 받을 준비가 되었다는 의미가 아니다.

실제 개발 환경에서는 healthcheck와 condition을 조합하여 서비스의 준비 상태까지 확인하는 것이 안정적이다:

services:
  app:
    build: .
    depends_on:
      db:
        condition: service_healthy  # db가 healthy 상태일 때만 시작

  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: example
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]  # PostgreSQL 준비 상태 확인
      interval: 5s       # 5초 간격으로 체크
      timeout: 3s        # 3초 내 응답 없으면 실패
      retries: 5         # 5번 실패하면 unhealthy 판정
      start_period: 10s  # 컨테이너 시작 후 10초간은 실패해도 무시

이 패턴은 DB 커넥션 에러로 인한 앱 기동 실패를 효과적으로 방지한다.


7. 2026년 기준 — 알아두면 유용한 최신 기능들

7.1 Docker Compose Watch

Compose v2.22.0부터 도입된 watch 기능은, 소스 코드 변경 시 컨테이너를 자동으로 갱신해 준다. 프론트엔드 개발의 Hot Reload와 유사한 경험을 Docker 환경에서 제공하는 것이다.

services:
  app:
    build: .
    develop:
      watch:
        - action: sync           # 파일 변경 시 컨테이너로 동기화
          path: ./src
          target: /app/src
          ignore:
            - node_modules/
        - action: rebuild        # 의존성 변경 시 이미지 재빌드
          path: package.json
# watch 모드로 실행
docker compose up --watch

sync, rebuild, sync+restart 세 가지 액션이 있으며, 파일 유형에 따라 적절한 액션을 지정하면 된다. 매번 docker compose down → build → up을 반복하던 수고를 상당 부분 줄여준다.

7.2 Compose V5 릴리스

2025년 말~2026년 초에 걸쳐 Docker Compose는 V2 시리즈를 넘어 V5로 메이저 버전을 올렸다. (혼동 방지를 위해 V3, V4를 건너뛰었다.) V5에서는 Go SDK가 도입되어, CLI 없이도 프로그래밍 방식으로 Compose 기능을 활용할 수 있게 되었다. CI/CD 파이프라인이나 커스텀 툴링을 구축할 때 유용한 변화이다.

7.3 Compose Bridge (GA)

Compose 파일을 Kubernetes 매니페스트나 Helm 차트로 변환해 주는 Compose Bridge가 GA(General Availability, 정식 출시) 상태로 전환되었다. 로컬에서 Compose로 개발하고, 프로덕션에서는 Kubernetes로 배포하는 워크플로우가 한결 매끄러워졌다.


8. 보안 관련 권고

8.1 환경 변수와 시크릿 관리

위 예제에서 POSTGRES_PASSWORD: example처럼 비밀번호를 YAML에 직접 기술하는 것은 학습 목적에서만 허용되는 방식이다. 실제 개발 환경에서는 다음과 같은 방법을 사용한다:

# .env 파일을 활용하는 방식
services:
  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}  # .env에서 읽어옴
# .env 파일 (반드시 .gitignore에 추가)
DB_PASSWORD=my_secure_password_here

Docker Secrets를 활용하면 보다 안전하게 민감 정보를 관리할 수 있으나, Swarm 모드에서만 완전히 지원되므로 프로젝트 환경에 맞게 선택하면 된다.


마치며

Docker Compose는 "컨테이너 오케스트레이션(여러 컨테이너의 조율과 관리)"의 가장 기초적이면서도 강력한 도구이다. 물론 대규모 프로덕션 환경에서는 Kubernetes와 같은 전문 오케스트레이터가 필요하겠지만, 로컬 개발 환경 구성, 테스트 환경 세팅, 소규모 서비스 배포 등에서 Compose의 가치는 여전하다.

결국 중요한 것은, YAML 파일 하나에 인프라를 선언적으로 기술하고 버전 관리할 수 있다는 사고방식 자체이다. 이 개념은 Kubernetes, Terraform 등 더 큰 스케일의 IaC 도구로 확장될 때도 그대로 통용된다.


참고 자료

profile
알면 좋은 것보단 잊어버리기 싫은 것들을 기록합니다.

0개의 댓글