Docker Compose 정리 (1/9)

다음 편: 2. 02 서비스 이미지 컨테이너 포트의 관계

# [Docker Compose 개념] Compose 파일이 표현하는 실행 환경

Docker Compose 개념편

- Compose 파일이 표현하는 실행 환경 ← 현재 문서

- 서비스 이미지 컨테이너 포트의 관계

- 네트워크와 볼륨이 상태를 만드는 방식

- 환경 변수와 시작 순서 설계

Docker Compose는 "컨테이너 여러 개를 띄우는 명령어 묶음"이 아니다. 하나의 애플리케이션을 이루는 실행 단위를 선언하고, 그 단위들이 어떤 네트워크와 저장 공간을 공유하는지 정의하는 모델이다.

예시 프로젝트에서는 애플리케이션 서버와 데이터베이스가 따로 필요하다. Spring Boot 앱은 Java 프로세스이고, PostgreSQL은 DB 서버 프로세스다. 로컬에서 두 프로세스를 수동으로 띄울 수도 있지만, 그렇게 하면 실행 순서, 포트, 환경 변수, DB 데이터 위치를 매번 사람이 기억해야 한다. Compose 파일은 이 기억해야 할 내용을 YAML로 고정해준다.

Compose가 관리하는 단위

현재 docker-compose.yml의 핵심 구조는 다음과 같다.

services:
  app:
    build: .
    ports:
      - "8081:80"
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:15-alpine
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
  app_storage:

여기서 services 아래의 appdb는 각각 하나의 실행 역할이다. app은 Spring Boot 서버를 실행하고, db는 PostgreSQL 서버를 실행한다. 서비스는 컨테이너 하나와 거의 비슷하게 느껴지지만, 개념적으로는 "이 설정으로 실행되는 역할"에 가깝다.

volumes는 컨테이너가 삭제되어도 남겨야 하는 데이터를 다룬다. PostgreSQL 데이터는 컨테이너 내부에만 두면 컨테이너를 지울 때 같이 사라지므로 named volume으로 분리한다.

프로젝트 이름이 중요한 이유

Compose는 실행되는 묶음을 프로젝트로 본다. 프로젝트 이름은 네트워크와 볼륨 이름의 접두어로 쓰인다. 현재 docker compose config를 실행하면 다음처럼 정리된 모델이 만들어진다.

networks:
  default:
    name: discordit_default
volumes:
  app_storage:
    name: discordit_app_storage
  postgres_data:
    name: discordit_postgres_data

여기서 discordit은 현재 디렉터리 이름에서 온 프로젝트 이름이다. 같은 Compose 파일을 다른 폴더에서 실행하면 프로젝트 이름이 달라질 수 있고, 그에 따라 네트워크와 볼륨 이름도 달라질 수 있다.

그래서 프로젝트 이름을 고정하고 싶다면 Compose 파일 상단에 name을 둘 수 있다.

name: discodeit

services:
  app:
    build: .

이렇게 하면 실행 위치가 바뀌어도 Compose 리소스 이름이 더 예측 가능해진다. 단, 같은 컴퓨터에서 같은 name을 가진 Compose 프로젝트를 두 벌 동시에 띄우면 리소스 이름이 충돌할 수 있다. 여러 복사본을 동시에 실행해야 한다면 docker compose -p 다른이름 up처럼 실행 시점에 프로젝트 이름을 바꾸는 편이 낫다.

docker compose config는 설계도를 펼쳐 보는 명령이다

docker-compose.yml은 사람이 쓰기 좋은 형태이고, docker compose config는 Compose가 실제로 이해한 최종 모델을 보여준다.

docker compose config

이 명령은 다음을 확인할 때 유용하다.

  • ${POSTGRES_DB} 같은 변수가 실제 어떤 값으로 보간되는지 확인한다.
  • ports가 어떤 구조로 해석되는지 확인한다.
  • named volume과 default network의 실제 이름을 확인한다.
  • Compose 문법 경고를 먼저 확인한다.

현재 프로젝트에서는 이 명령이 version 속성에 대해 obsolete 경고를 낸다. 최신 Compose에서는 version이 스키마 선택에 쓰이지 않고, 호환성을 위한 정보로만 남아 있기 때문이다. 그래서 새로 정리하는 Compose 파일에서는 version: '3.8'을 제거해도 된다.

config 출력 공유 금지

docker compose config.env 값을 실제 값으로 펼친다. 비밀번호와 access key가 출력될 수 있으므로, 결과를 이슈나 질문 글에 그대로 올리면 안 된다.

예시 실행 환경을 한 장으로 보기

flowchart TD Host["호스트 머신"] subgraph Compose["Compose project"] App["app service
Spring Boot"] Db["db service
PostgreSQL"] PgVol[("postgres_data")] AppVol[("app_storage")] Net["default bridge network"] end Host -->|"localhost:8081"| App Host -->|"localhost:5432"| Db App -->|"jdbc:postgresql://db:5432"| Db App --- AppVol Db --- PgVol App --- Net Db --- Net

이 그림에서 가장 중요한 지점은 앱이 DB에 접근할 때 localhost가 아니라 db라는 서비스 이름을 쓴다는 점이다. localhost는 "내가 실행 중인 컨테이너 자신"을 가리킨다. 앱 컨테이너 안에서 localhost:5432를 호출하면 PostgreSQL 컨테이너가 아니라 앱 컨테이너 내부를 찾게 된다.

Compose 파일을 읽는 순서

처음 Compose 파일을 볼 때는 위에서 아래로 모든 줄을 해석하려고 하면 어렵다. 다음 순서로 읽는 편이 빠르다.

  1. services에 어떤 실행 단위가 있는지 본다.
  2. 각 서비스가 build로 이미지를 만드는지, image로 이미지를 받아오는지 본다.
  3. 외부에 열리는 ports가 무엇인지 본다.
  4. 서비스끼리 연결되는 이름과 DB URL을 본다.
  5. 사라지면 안 되는 데이터가 volumes로 빠져 있는지 본다.
  6. .env, environment, env_file이 어떤 값을 어디로 보내는지 본다.
  7. depends_onhealthcheck로 시작 순서를 어떻게 제어하는지 본다.

이 순서로 읽으면 Compose 파일이 "긴 YAML"이 아니라 "실행 환경의 지도"로 보인다.

참고한 공식 문서