[IT-정보] docker compose yml 오류 해결 가이드 – 문법 오류부터 설정 미스로 인한 장애 대응법까지

docker-compose.yml 기본 문법를 설명하는 자주 발생하는 오류와 해결법 대표 이미지

서버 배포 직후 마주하는 당혹스러운 YAML 오류

모든 설정이 완벽하다고 믿었습니다. 로컬 환경에서는 잘 돌아가던 컨테이너들이 서버에 올리는 순간 “failed to parse yaml”이라는 차가운 메시지를 뱉어내며 멈춰버려요. 야심 차게 준비한 배포 스크립트가 단 한 줄의 들여쓰기 실수 때문에 작동하지 않을 때의 그 막막함은 서버 운영자라면 누구나 한 번쯤 겪어봤을 거예요.

도커 컴포즈 파일은 단순한 설정 모음이 아니에요. 인프라의 설계도와 같아서, 아주 작은 문법적 균열만 생겨도 전체 서비스가 연쇄적으로 무너질 수 있어요. 특히 운영 환경에서는 이런 사소한 실수가 서비스 중단이라는 치명적인 결과로 이어지곤 하죠. 단순한 오타를 찾는 것을 넘어, 왜 이런 에러가 발생했는지 근본적인 구조를 이해하는 것이 필요해요.

이 글은 단순한 문법 나열이 아니에요. 실제 장애 상황에서 당황하지 않고 문제를 추적할 수 있는 논리적인 진단 프로세스를 제공하는 데 집중했어요. 문제를 해결하는 기술적인 방법뿐만 아니라, 다시는 같은 실수를 반복하지 않도록 만드는 방어적인 설정법까지 깊이 있게 다룹니다.

이 글을 다 읽고 나면 다음과 같은 능력을 갖추게 돼요.

  • YAML 문법 오류를 즉각적으로 식별하고 수정하는 능력
  • 네트워크와 볼륨 설정에서 발생하는 논리적 충돌 해결법
  • 환경 변수 누락으로 인한 컨테이너 실행 실패 방지 전략
  • 장애 발생 시 로그를 통해 원인을 빠르게 파악하는 기술

실수를 방지하는 도커 컴포즈 기본 설계 원칙

본격적으로 트러블슈팅에 들어가기 전에, 우리가 다루는 도구의 특성을 명확히 이해해야 해요. docker-compose.yml 파일은 YAML 형식을 따르는데, 이 형식은 인간이 읽기에는 매우 편하지만 기계 입장에서는 아주 예민한 구조를 가지고 있어요. 아주 작은 공백 하나가 전체 구조를 뒤바꿀 수 있다는 뜻이에요.

설정 파일을 작성하기 전에 반드시 확인해야 할 세 가지 핵심 기준이 있어요. 첫 번째는 들여쓰기(Indentation)의 일관성이에요. YAML에서는 탭(Tab) 대신 반드시 스페이스(Space)를 사용해야 해요. 많은 에디터가 탭을 스페이스로 자동 변환해주지만, 설정이 잘못되어 있으면 눈에 보이지 않는 오류의 씨앗이 됩니다. 두 번째는 데이터 타입의 명확성이에요. 숫자로 인식되어야 할 값이 문자열로 취급되거나 그 반대의 경우, 컨테이너 내부의 애플리케이션이 실행 중에 죽어버릴 수 있어요.

마지막은 계층 구조의 정합성이에요. 서비스(services) 아래에 네트워크(networks)가 있어야 하고, 각 서비스 아래에 환경 변수(environment)가 위치해야 하는 식의 엄격한 계층이 존재해요. 이 계층이 꼬이면 도커는 설정 파일을 읽는 단계에서부터 거부 반응을 보입니다.

💡 알아두기
YAML은 대소문자를 엄격하게 구분해요. volumesVolumes로 적으면 도커는 이를 전혀 다른 설정으로 인식하거나 오류를 발생시켜요.

작업 환경에 따라 어떤 설정을 우선순위에 두어야 할지 판단하는 기준을 아래 표로 정리했어요. 상황에 맞는 접근 방식이 필요해요.

구분 기준 로컬 개발 환경 운영(Production) 환경
설정 유연성 높음 (빠른 변경 위주) 낮음 (변경 시 검증 필수)
데이터 저장 방식 Bind Mount (로컬 폴더 연결) Named Volume (도커 관리형)
환경 변수 관리 직접 입력 또는 .env 파일 Secret 관리 도구 및 외부 주입
네트워크 구조 단일 브릿지 네트워크 격리된 다중 네트워크 구성

위의 기준을 바탕으로 설계 방향을 먼저 정하는 것이 오류를 줄이는 가장 빠른 길이에요. 무턱대고 코드를 치기 전에, 이 서비스가 어떤 환경에서 어떻게 돌아가야 하는지 머릿속으로 먼저 그려보세요.

실패 없는 도커 컴포즈 구성을 위한 5단계 실행 전략

이제 실제 설정을 구성하는 단계로 넘어가 볼게요. 단순히 명령어 하나를 치는 것이 아니라, 서비스 간의 관계와 데이터의 흐름을 설계하는 과정이에요. 각 단계는 서로 긴밀하게 연결되어 있으므로 순서를 지키는 것이 중요해요.

STEP 1. 서비스 구조와 버전 정의하기

가장 먼저 해야 할 일은 전체적인 틀을 잡는 것이에요. 최신 도커 컴포즈 사양(Compose Specification)에서는 상단의 version 표기를 생략할 수 있지만, 명시적으로 작성하는 것이 나중에 발생할 호환성 문제를 방지하는 데 도움이 돼요. 서비스(services) 섹션은 이 파일의 심장부예요. 각 서비스는 독립적인 컨테이너 역할을 하며, 여기서 이미지(image)나 빌드(build) 방식을 결정하게 됩니다.

이미지를 사용할 때는 항상 태그(Tag)를 명시하세요. latest 태그는 편리해 보이지만, 배포할 때마다 예기치 못한 버전 업데이트가 발생하여 서비스가 깨질 수 있는 매우 위험한 습관이에요. nginx:1.25-alpine처럼 구체적인 버전을 명시하는 습관을 들여야 해요.

STEP 2. 네트워크 격리와 통신 경로 설계하기

서비스들이 서로 어떻게 대화할지 결정해야 해요. 도커 컴포즈는 기본적으로 모든 서비스가 하나의 기본 네트워크에 속하게 만들지만, 실무에서는 이를 분리하는 설계가 필요해요. 예를 들어, 웹 서버와 데이터베이스는 통신해야 하지만, 외부에서 데이터베이스로 직접 접근하는 것은 보안상 매우 위험해요.

이를 위해 커스텀 네트워크를 정의하세요. 웹 서버용 네트워크와 데이터베이스용 네트워크를 나누어 배치하면, 웹 서버는 두 네트워크 모두에 속해 통신이 가능하지만, 외부의 다른 서비스는 데이터베이스 네트워크에 접근할 수 없게 격리할 수 있어요. 네트워크를 설계할 때는 서비스 간의 의존 관계를 고려하여 최소 권한의 원칙을 적용하는 것이 핵심이에요.

STEP 3. 데이터 영속성을 위한 볼륨 전략 수립하기

컨테이너는 태생적으로 ‘휘발성’이에요. 컨테이너가 삭제되면 그 안에서 생성된 데이터도 모두 사라지죠. 이를 방지하기 위해 볼륨(Volumes) 설정은 필수적이에요. 여기서 많은 운영자가 실수를 저질러요. 바로 경로 설정 오류입니다.

볼륨은 크게 두 가지 방식으로 나뉘어요. 첫째, 호스트의 특정 디렉토리를 연결하는 바인드 마운트(Bind Mount) 방식이에요. 개발 단계에서 코드를 즉시 반영할 때 유용하지만, 운영 환경에서는 호스트 운영체제의 권한 문제(Permission denied)에 직면할 가능성이 높아요. 둘째, 도커가 직접 관리하는 네임드 볼륨(Named Volume) 방식이에요. 성능이 더 뛰어나고 관리가 체계적이라 운영 환경에 적합해요. 어떤 방식을 선택하느냐에 따라 데이터 백업 전략과 복구 속도가 완전히 달라진다는 점을 명심해야 해요.

STEP 4. 환경 변수를 통한 설정 분리하기

데이터베이스 비밀번호나 API 키를 docker-compose.yml 파일에 직접 적는 것은 보안상 매우 위험해요. 설정 파일이 Git 같은 버전 관리 시스템에 올라가는 순간, 모든 비밀 정보가 유출됩니다. 이를 해결하기 위해 .env 파일을 적극적으로 활용해야 해요.

컴포즈 파일에서는 ${DB_PASSWORD}와 같은 변수 형식을 사용하고, 실제 값은 별도의 파일에 저장하는 방식이에요. 이렇게 하면 동일한 설정 파일을 유지하면서도, 환경(개발, 스테이징, 운영)에 따라 값만 갈아 끼울 수 있어 관리가 매우 용이해져요. 다만, .env 파일 자체가 노출되지 않도록 .gitignore에 등록하는 것을 절대 잊지 마세요.

STEP 5. 의존성 및 실행 순서 제어하기

마지막으로 서비스 간의 실행 순서를 조율해야 해요. 예를 들어, 애플리케이션 서버가 데이터베이스보다 먼저 실행되면, 서버는 DB 연결에 실패하고 즉시 종료되어 버릴 거예요. 이때 사용하는 명령어가 depends_on이에요. 하지만 depends_on은 단순히 컨테이너가 ‘시작’되는 것만 확인하지, 내부의 데이터베이스 엔진이 ‘준비’되었는지는 확인하지 못해요.

완벽한 실행을 위해서는 헬스체크(Healthcheck)를 함께 구성해야 해요. 데이터베이스가 요청을 받을 준비가 되었을 때 ‘Healthy’ 상태가 되도록 설정하고, 애플리케이션이 이 상태를 확인한 뒤에 실행되도록 설계해야 서비스의 안정성을 극대화할 수 있어요.

💡 알아두기
실제 운영 시나리오를 예로 들어볼게요. 웹 서버(Nginx)와 API 서버(Python), DB(PostgreSQL)를 띄운다면, Nginx는 API 서버의 네트워크에 연결되어야 하고, API 서버는 DB의 헬스체크가 완료된 후에만 구동되도록 구성하는 것이 가장 견고한 모델이에요.

자주 하는 실수와 해결법

실전에서 가장 빈번하게 발생하는 문제들을 모아 정리했어요. 에러 메시지만 보고 당황하기보다, 아래의 패턴을 먼저 확인해 보세요.

  • 실수: “mapping values are not allowed here” 에러 발생
    ➡️ 원인: 콜론(:) 뒤에 공백을 넣지 않았거나, 들여쓰기 간격이 일정하지 않음
    ➡️ ✅ 해결법: 콜론 뒤에는 반드시 한 칸의 공백을 넣고, 모든 들여쓰기를 스페이스 2칸 단위로 통일하세요.
  • 실수: 컨테이너가 실행되자마자 종료됨
    ➡️ 원인: 포트 충돌이 발생했거나, 설정된 명령(command)에 오류가 있음
    ➡️ ✅ 해결법: docker compose logs [서비스명] 명령어로 로그를 확인하여 에러 메시지를 파악하세요.
  • 실수: 볼륨에 데이터가 저장되지 않음
    ➡️ 원인: 호스트 경로와 컨테이너 내부 경로를 반대로 적었거나, 권한 부족
    ➡️ ✅ 해결법: 호스트경로:컨테이너경로 형식을 다시 확인하고, 호스트 디렉토리의 쓰기 권한을 점검하세요.
  • 실수: 서비스 간 통신이 불가능함
    ➡️ 원인: 서로 다른 네트워크에 속해 있어 격리된 상태임
    ➡️ ✅ 해결법: 두 서비스가 동일한 네트워크 이름을 공유하도록 설정하세요.
  • 실수: 환경 변수가 적용되지 않음
    ➡️ 원인: .env 파일의 위치가 잘못되었거나 변수명이 오타가 남
    ➡️ ✅ 해결법: .env 파일이 docker-compose.yml과 같은 위치에 있는지 확인하고, 오타를 검사하세요.
⚠️ 주의
로그를 확인할 때 docker compose up을 실행 중인 터미널에서만 보지 마세요. 컨테이너가 이미 죽었다면 docker compose logs --tail=50 명령어를 사용하여 마지막 로그를 반드시 확인해야 합니다.

자주 묻는 질문

Q. docker-compose 명령어가 안 먹혀요. 어떻게 해야 하나요?

설치 여부를 먼저 확인해야 해요. 최신 환경에서는 docker compose (하이픈 없음)를 사용해야 할 수도 있어요. 설치가 되어 있다면 실행 경로에 해당 파일이 있는지 확인해 보세요.

Q. YAML 파일을 수정했는데 적용이 안 돼요.

단순히 다시 실행하는 것이 아니라, docker compose up -d를 입력하여 변경 사항을 감지하고 컨테이너를 재생성하도록 유도해야 해요.

Q. 컨테이너 내부의 파일을 호스트에서 바로 수정하고 싶어요.

이때는 바인드 마운트를 사용해야 해요. ./my-config:/etc/config와 같이 상대 경로를 사용하여 호스트의 디렉토리를 연결하세요.

Q. 특정 서비스만 다시 띄울 수 있나요?

네, 가능해요. docker compose up -d [서비스이름]을 입력하면 전체를 끄지 않고 해당 서비스와 그 의존성만 다시 시작할 수 있어요.

Q. 에러 메시지가 너무 복잡해서 읽기 힘들어요.

에러 메시지의 가장 윗부분을 보세요. 보통 YAML 문법 오류는 몇 번째 줄에서 문제가 생겼는지 정확한 라인 번호를 알려줍니다. 그 부분을 중심으로 들여쓰기를 집중 점검하세요.

장애 없는 운영을 위한 마지막 점검

도커 컴포즈를 다루는 숙련도는 얼마나 많은 오류를 겪었느냐가 아니라, 얼마나 체계적으로 대응하느냐에 달려 있어요. 오늘 배운 내용을 바탕으로 이제는 에러 메시지를 두려워하지 말고, 논리적으로 문제를 파헤칠 수 있을 거예요.

✅ 핵심 요약

  • YAML 문법은 탭이 아닌 스페이스 2칸을 사용하세요.
  • 이미지 태그는 latest 대신 구체적인 버전을 명시하세요.
  • 데이터 보존을 위해 네임드 볼륨 사용을 권장해요.
  • 비밀 정보는 반드시 .env 파일로 분리하세요.
  • 서비스 실행 순서는 depends_on과 Healthcheck의 조합으로 제어하세요.

지금 바로 실천해야 할 단계는 다음과 같아요.

  • 오늘 할 일: 현재 운영 중인 컴포즈 파일의 모든 이미지 태그가 구체적인 버전인지 확인하기
  • 이번 주 할 일: 환경 변수가 파일에 직접 노출되어 있지는 않은지 점검하고 .env로 분리하기
  • 실행 직전 할 일: 새로운 설정을 반영하기 전 docker compose config 명령어로 문법 검증하기

만약 같은 유형의 오류가 반복된다면, 자신만의 트러블슈팅 체크리스트를 만들어 두는 것을 추천해요. 나중에 비슷한 상황이 왔을 때 해결 시간을 획기적으로 줄여줄 거예요. 같은 오류가 반복된다면 진단 순서를 체크리스트로 만들어 두세요.

더 자세한 인프라 운영 팁이 필요하다면 도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드 글을 함께 읽어보시길 권장해요.

댓글 남기기