도커 컴포즈 작성법 완벽 가이드: 볼륨과 네트워크 이해하기

도커 컴포즈 작성법 완벽 가이드: 볼륨과 네트워크 이해하기

눈으로만 보면 절대 내 것이 되지 않는다. docker-compose.yml을 직접 작성하며 볼륨과 네트워크의 원리를 손끝으로 익혀보자.

이 글은 2026년 7월 기준으로 작성됐다. 최신 Compose Specification은 공식 문서를 참고하길 바란다.

이 글은 'Docker Compose 마스터' 시리즈 2편이다. 아직 컴포즈의 기본 개념이 헷갈린다면, 1편: 도커 컴포즈란? Dockerfile과의 차이점 완벽 정리를 먼저 읽고 오는 것을 추천한다.

이번 편에서는 도커 컴포즈를 실무에서 다루기 위해 반드시 알아야 하는 YAML 파일 작성법과 핵심 문법 세 가지(services, volumes, networks)를 자세히 파헤쳐 본다.


docker-compose.yml 기본 구조 파악하기

docker-compose.yml 파일은 크게 버전을 명시하는 구문과 services, volumes, networks라는 세 가지 핵심 블록으로 구성된다.

작성된 파일의 전체적인 뼈대는 다음과 같다.

version: '3.8' # Compose 파일 포맷 버전

services:
  # 실행할 컨테이너들을 정의하는 곳
  web:
    # 웹 서버 설정...
  db:
    # 데이터베이스 설정...

volumes:
  # 컨테이너 데이터를 영구 저장할 볼륨 정의

networks:
  # 컨테이너 간 통신을 위한 네트워크 정의

참고: 최근 도커 컴포즈 최신 규격(Compose Specification)에서는 최상단의 version 속성을 생략하는 추세지만, 하위 호환성과 명시적인 관리를 위해 여전히 많은 현업에서 버전을 기입하고 있다.

Services 블록: 어떤 컨테이너를 띄울 것인가?

services 블록은 전체 시스템을 구성하는 개별 컨테이너(서비스)들의 설정값을 정의하는 가장 중요한 영역이다. Nginx 웹 서버와 PostgreSQL DB를 띄우는 예시를 살펴보자.

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      POSTGRES_USER: root
      POSTGRES_PASSWORD: secretpassword

핵심 옵션 정리: - image: 컨테이너를 생성할 때 사용할 도커 이미지 (예: nginx:latest). 만약 로컬에 있는 Dockerfile을 직접 빌드해서 사용하고 싶다면 build: . 옵션을 대신 사용한다. - ports: 호스트 PC의 포트와 컨테이너 내부 포트를 연결(포워딩)한다. "호스트포트:컨테이너포트" 형식으로 작성한다. 위 예시에서는 내 PC의 8080 포트로 접속하면 Nginx의 80 포트로 연결된다. - environment: 컨테이너 내부에 전달할 환경 변수를 설정한다. 주로 DB의 비밀번호, 앱의 API 키 등을 주입할 때 사용한다. - depends_on: 서비스 간의 실행 순서를 제어한다. 위 코드에서는 db 컨테이너가 먼저 실행된 후 web 컨테이너가 실행되도록 보장한다.

Volumes 블록: 데이터는 어떻게 유지할까?

컨테이너는 기본적으로 상태를 저장하지 않는(Stateless) 성질을 갖는다. 즉, DB 컨테이너가 종료되거나 삭제되면 그 안에 쌓인 소중한 유저 데이터도 함께 허공으로 날아간다. 이를 방지하기 위해 컨테이너 내부 데이터 저장소와 호스트 PC의 디스크를 연결하는 것이 바로 볼륨(Volume)이다.

services:
  db:
    image: postgres:15
    volumes:
      - pgdata:/var/lib/postgresql/data  # DB 데이터 영구 보존

volumes:
  pgdata: # 하단에 Named Volume 선언

위의 pgdata:/var/lib/postgresql/data 설정은 Named Volume(이름 지정 볼륨) 방식이다. 도커 엔진이 호스트 어딘가에 안전한 저장 공간(pgdata)을 알아서 생성하고, 이를 컨테이너의 /var/lib/postgresql/data 폴더와 동기화한다. 컨테이너를 지웠다 다시 띄워도 데이터는 완벽하게 유지된다.

💡 실무 팁 (Bind Mount): 소스 코드를 수정할 때마다 즉각적으로 반영되길 원하는 로컬 개발 환경에서는 ./src:/app/src처럼 호스트의 특정 폴더 경로를 직접 연결하는 바인드 마운트 방식을 자주 활용한다.

Networks 블록: 컨테이너끼리 어떻게 통신할까?

만약 services 블록에 여러 컨테이너를 선언하기만 했다면, 도커 컴포즈는 아주 똑똑하게도 '디폴트(default) 네트워크'를 하나 생성하여 모든 컨테이너를 그곳에 연결해 준다.

덕분에 IP 주소를 전혀 몰라도 서비스 이름 자체가 도메인(DNS) 역할을 한다. 예를 들어 web 컨테이너 내부에서 db 컨테이너의 5432 포트로 접근하고 싶다면, 주소란에 db:5432라고만 적으면 통신이 성공한다.

보안이 중요한 환경에서는 프론트엔드용 네트워크와 백엔드용 커스텀 네트워크를 분리하여 격리성을 높이기도 한다.

services:
  backend:
    image: my-api:1.0
    networks:
      - internal-net

  db:
    image: postgres:15
    networks:
      - internal-net

networks:
  internal-net:
    driver: bridge

이렇게 설정하면 backenddbinternal-net 안에서만 소통할 수 있어, 외부의 불필요한 접근을 원천 차단할 수 있다.


자주 발생하는 오류와 해결 방법 (FAQ)

Q. docker compose up을 쳤는데 yaml: line 10: did not find expected key 같은 오류가 납니다. A. YAML 파일은 들여쓰기(Indentation)에 매우 엄격하다. 탭(Tab) 대신 항상 스페이스바(Space) 2칸 단위로 들여쓰기했는지 확인해야 한다. 항목의 계층 구조가 어긋났을 때 주로 발생하는 오류다.

Q. Bind for 0.0.0.0:8080 failed: port is already allocated 오류가 뜹니다. A. 호스트 PC에서 8080 포트를 이미 다른 프로그램(로컬에서 띄워둔 톰캣이나 다른 도커 컨테이너)이 사용 중일 때 발생한다. docker-compose.ymlports: "8081:80"처럼 호스트 측 포트 번호를 변경하거나, 사용 중인 프로세스를 강제로 종료한 후 다시 실행하면 해결된다.


📝 정리

이번 글에서 다룬 핵심 내용:

  • [x] services: 실행할 도커 이미지, 포트 개방, 환경 변수, 실행 순서를 정의한다.
  • [x] volumes: 컨테이너 삭제 후에도 데이터를 보존하기 위해 호스트 디스크와 연결한다.
  • [x] networks: 컨테이너 간 안전한 통신을 위해 격리된 가상망을 구성한다.

이제 컴포즈 파일의 뼈대를 직접 짤 수 있게 되었다. 다음 편에서는 이 지식을 바탕으로 백엔드, 캐시, DB를 아우르는 다중 컨테이너를 실제로 배포해 보고 필수 명령어들을 완전히 마스터해 본다.