
작은 버전 숫자가 불러오는 대형 운영 사고
금요일 오후 5시, 평소와 다름없이 새로운 기능을 반영하기 위해 도커 컴포즈(Docker Compose) 설정 파일을 업데이트했습니다. 로컬 환경에서는 아무런 문제 없이 컨테이너가 정상적으로 올라갔고, 모든 테스트도 통과했지요. 하지만 실제 운영 서버에 반영하는 순간, 서비스가 예기치 않게 중단되거나 데이터베이스 연결이 끊어지는 비상 상황이 발생합니다.
원인은 의외로 아주 사소한 곳에 있었습니다. 바로 파일 최상단에 적힌 버전(version) 번호 하나가 잘못 지정되어 있었던 것이에요. 개발 환경의 도커 엔진 버전과 운영 서버의 엔진 버전이 미세하게 다르다는 사실을 간과한 결과였습니다.
단순히 숫자 하나를 잘못 적는 것이 왜 이렇게 큰 문제를 일으킬까요? 컴포즈 파일의 버전은 단순히 문서를 구분하는 번호가 아니라, 도커 엔진이 해당 파일을 어떻게 해석하고 어떤 기능을 허용할지를 결정하는 핵심 규칙(Schema)이기 때문입니다. 잘못된 버전 지정은 설정한 리소스 제한이 무시되거나, 네트워크 구성이 꼬이거나, 심지어 소중한 볼륨 데이터가 제대로 마운트되지 않는 치명적인 사고로 이어집니다.
운영 관리자로서 이런 사고를 겪지 않으려면 컴포즈 파일의 버전을 바라보는 시각부터 바꿔야 해요. 이 글에서는 컴포즈 파일 버전 설정 시 발생하는 흔한 실수들을 유형별로 분석하고, 어떤 상황에서 어떤 버전을 선택해야 안전한지 상세히 알려드릴게요.
오늘 이 글을 통해 배우게 될 내용은 다음과 같아요.
- 버전 필드가 컨테이너 실행에 미치는 실질적인 영향
- 환경별 버전 불일치로 발생하는 대표적인 사고 패턴
- 최신 컴포즈 사양(Compose Specification)으로의 전환 방법
- 안전한 배포를 위한 단계별 체크리스트
안전한 설정을 위한 사전 지식과 체크리스트
컴포즈 파일을 수정하기 전에 가장 먼저 확인해야 할 것은 현재 사용 중인 도커 엔진(Docker Engine)과 도커 컴포즈 도구의 버전이에요. 많은 분이 설정 파일에 적는 버전 번호와 실제 설치된 도구의 버전을 동일시하는 실수를 범하곤 합니다. 하지만 이 둘은 엄연히 다른 개념이라는 점을 반드시 기억해야 해요.
파일에 적는 `version: ‘3.8’`은 “나는 이 파일에 3.8 버전에 정의된 문법을 사용할 것이다”라는 선언이에요. 반면, 서버에 설치된 `docker compose version` 결과는 “이 명령어를 실행하는 도구의 엔진 버전”을 의미하죠. 만약 서버의 도구 버전이 너무 낮다면, 파일에 3.8이라고 적었더라도 해당 기능을 제대로 지원하지 못해 오류가 발생하거나 설정이 무시될 수 있어요.
최신 도커 환경에서는 `version` 필드 자체를 생략하는 것이 권장되기도 해요. 이는 ‘컴포즈 사양(Compose Specification)’이라는 통합 표준이 도입되었기 때문입니다. 하지만 레거시 시스템을 관리한다면 여전히 버전 명시가 매우 중요합니다.
본격적인 설정에 앞서, 현재 여러분의 환경이 아래의 기준에 부합하는지 검토해 보세요. 단순히 버전 숫자를 맞추는 것을 넘어, 운영 환경의 일관성을 확보하는 과정이 필요합니다.
| 구분 기준 | 체크 포인트 | 권장 조치 |
|---|---|---|
| 도구 버전 일치성 | 로컬과 서버의 CLI 버전이 같은가? | 서버의 도커 엔진 업데이트 |
| 스키마 호환성 | 사용할 기능이 지정한 버전에 포함되는가? | 기능에 맞는 버전 상향 |
| 운영 모드 확인 | 단일 서버인가, 스웜(Swarm) 모드인가? | 운영 모드에 맞는 버전 선택 |
| YAML 문법 검증 | 들여쓰기와 문법 오류는 없는가? | Linter 도구 사용 |
특히 스웜(Swarm) 모드를 사용하는 경우에는 버전 선택이 더욱 까다로워져요. 스웜 모드에서 컨테이너의 복제본 개수나 배치 전략을 설정하는 `deploy` 섹션은 버전 3 이상의 스키마에서만 제대로 작동하기 때문이죠. 만약 예전 방식인 버전 2를 사용하면서 `deploy` 설정을 넣는다면, 도커는 이를 아무런 오류 없이 무시해 버립니다. 설정이 적용되지 않은 채 컨테이너가 뜨는 것이 가장 무서운 상황인 셈이에요.
준비가 되셨나요? 이제 구체적으로 어떤 실수들이 우리의 밤잠을 설치게 만드는지, 실전 사례를 통해 깊이 있게 파헤쳐 보겠습니다.
컴포즈 파일 버전 지정 시 발생하는 단계별 실수 패턴
컴포즈 파일의 버전을 결정하는 과정은 단순히 숫자를 고르는 작업이 아닙니다. 이는 컨테이너의 생명 주기와 자원 관리 방식을 결정하는 설계 과정에 가까워요. 실무에서 반복되는 실수들을 5가지 단계로 나누어 상세히 살펴보겠습니다.
STEP 1. 레거시 버전(V2)과 현대적 버전(V3)의 혼용 실수
가장 흔한 실수는 과거에 작성된 템플릿을 그대로 가져와 사용하는 경우입니다. 예전에는 리소스 제한(CPU, Memory)을 설정할 때 `mem_limit`이나 `cpus` 같은 키워드를 직접 사용했습니다. 이는 버전 2 스키마의 특징이죠. 하지만 버전 3로 넘어오면서 이 방식은 deploy 섹션 안으로 이동했습니다.
만약 여러분이 `version: ‘3’`을 사용하면서 예전 방식인 `mem_limit: 512mb`를 작성한다면 어떻게 될까요? 도커 컴포즈는 오류를 내뱉으며 멈추는 대신, 해당 설정을 그냥 무시하고 컨테이너를 실행해 버립니다. 결과적으로 컨테이너는 서버의 모든 자원을 써버리며 메모리 부족(OOM)으로 서버 전체를 다운시키는 사고를 일으킬 수 있어요. 설정이 적용되지 않았다는 경고조차 제대로 보지 못할 때가 많아 더욱 위험합니다.
STEP 2. 스웜(Swarm) 모드와 단일 노드 모드의 설정 착각
도커 컴포즈는 크게 두 가지 환경에서 사용됩니다. 일반적인 개발/운영 환경인 단일 노드 모드와, 여러 대의 서버를 묶어 관리하는 스웜 모드입니다. 여기서 발생하는 실수는 deploy 키워드의 해석 차이에서 옵니다.
버전 3 스키마에서 `deploy` 섹션은 스웜 모드에서의 오케스트레이션을 위해 설계되었습니다. 하지만 최근의 도커 컴포즈 V2(CLI 버전)는 단일 노드 모드에서도 이 `deploy` 설정을 어느 정도 읽어 들여 리소스 제한으로 해석해 주기도 합니다. 문제는 이 동작이 도커 엔진 버전과 컴포즈 도구의 조합에 따라 매우 불안정하다는 점이에요. 어떤 환경에서는 작동하고 어떤 환경에서는 무시되는 이 불확실성이 운영의 불확실성을 키우는 주범이 됩니다.
STEP 3. 버전 번호와 실제 사용 기능의 불일치
컴포즈 파일의 버전은 일종의 “약속”입니다. `version: ‘3.8’`이라고 적었다면, 여러분은 3.8 버전에서 새롭게 추가된 기능들을 사용할 권리를 얻는 동시에, 그 버전이 지원하지 않는 기능을 사용하지 않겠다고 약속하는 것이기도 합니다.
예를 들어, 네트워크 설정에서 특정 인터페이스를 지정하거나 복잡한 볼륨 드라이버를 사용하는 기능 중 일부는 높은 버전의 스키마에서만 지원됩니다. 만약 파일에는 `version: ‘3.0’`이라고 적어두고, 실제로는 `3.8`에서 지원하는 최신 네트워크 옵션을 사용한다면 어떻게 될까요? 구형 엔진이 설치된 운영 서버에서는 파일을 읽는 단계부터 거부당하거나, 설정이 누락된 채 불안정한 상태로 구동될 확률이 매우 높습니다.
설정 파일의 버전과 서버의 도커 엔진 버전을 맞추는 가장 확실한 방법은, 서버에서 `docker compose version`을 실행하여 지원 가능한 범위를 먼저 파악하는 것입니다.
STEP 4. 컴포즈 사양(Compose Specification)에 대한 오해
최근 도커는 버전 번호를 명시하지 않는 컴포즈 사양(Compose Specification)으로 전환하고 있습니다. 이제는 `version: ‘3.8’` 같은 줄을 아예 삭제해도 도커가 알아서 최신 규칙을 적용한다는 뜻이죠. 하지만 여기서 많은 개발자가 실수를 합니다. “버전이 필요 없다니 그냥 다 지워도 되겠구나”라고 생각하며 무작정 삭제하는 것입니다.
물론 최신 환경에서는 문제가 없지만, 만약 여러분의 운영 서버가 아직 레거시 엔진을 사용 중이라면, 버전 명시가 없는 파일은 기본적으로 가장 낮은 버전의 스키마로 해석될 위험이 있습니다. 즉, 명시적인 버전 지정이 오히려 안전장치 역할을 하던 시대에서, 명시하지 않음으로써 발생하는 하위 호환성 문제가 새로운 이슈로 떠오르고 있습니다.
STEP 5. 환경 변수와 버전 간의 상호작용 간과
마지막으로, `.env` 파일과 컴포즈 파일의 버전을 결합할 때 발생하는 문제입니다. 버전별로 환경 변수를 처리하는 방식이나, 변수가 정의되지 않았을 때의 기본값 처리 메커니즘이 미세하게 다를 수 있습니다. 특히 버전에 따라 네트워크 이름에 변수를 적용하는 규칙이 달라질 수 있는데, 이를 고려하지 않고 배포를 진행하면 서비스 간 통신이 끊기는 장애가 발생합니다.
이러한 복잡한 관계를 이해하기 위해, 아래의 예시 시나리오를 통해 올바른 설정과 잘못된 설정의 차이를 확인해 보세요.
[실제 장애 시나리오 예시]
- 상황: 메모리 제한이 중요한 데이터베이스 컨테이너 운영 중
- 잘못된 설정: `version: ‘3’` 사용 + `mem_limit: 1g` (버전 2 방식)
- 결과: 도커는 설정 오류를 내지 않고 그냥 무시함 $
ightarrow$ DB가 메모리를 무한정 사용 $
ightarrow$ 서버 전체의 커널이 메모리 부족으로 중단(Panic) - 올바른 설정: `version: ‘3.8’` 사용 + `deploy: resources: limits: memory: 1g`
- 결과: 설정값이 정확히 적용되어 메모리 사용량이 1GB로 제한됨
결국, 컴포즈 파일의 버전을 다루는 기술은 단순히 문법을 맞추는 것이 아니라, 내 컨테이너가 어떤 자원 제약 조건 하에서 움직일지를 명확히 정의하는 행위임을 잊지 마세요.
자주 하는 실수와 해결법
현장에서 바로 적용할 수 있도록, 실무자들이 가장 많이 겪는 실수와 그 해결책을 정리했습니다.
- ❌ 실수: 로컬에서는 잘 되는데 서버에서는 `deploy` 설정이 무시됨
👉 원인: 서버의 도커 엔진이 너무 낮거나, 스웜 모드가 아닌 단일 노드 환경에서 구형 컴포즈 도구를 사용 중임
👉 ✅ 해결법: 서버의 도커 엔진을 최신 버전으로 업데이트하거나, 단일 노드라면 리소스 제한 방식이 최신 컴포즈 도구(V2 CLI)와 호환되는지 확인하세요. - ❌ 실수: `version` 필드에 적은 숫자보다 높은 기능(예: 3.9 전용 기능)을 사용함
👉 원인: 스키마 버전과 사용 문법 간의 불일치
👉 ✅ 해결법: 사용하는 모든 키워드가 지원되는 최소 버전을 확인하고, 가급적 가장 높은 버전(예: 3.8)을 명시하세요. - ❌ 실수: `.env` 파일을 사용하면서 버전별 변수 치환 규칙을 모름
👉 원인: 버전마다 환경 변수 해석 우선순위가 다를 수 있음
👉 ✅ 해결법: 컴포즈 실행 전 `docker compose config` 명령어를 통해 최종적으로 해석된 YAML 결과물을 반드시 검증하세요. - ❌ 실수: 버전 필드를 아예 삭제했더니 동작 방식이 달라짐
👉 원인: 도커가 해당 파일을 아주 오래된 레거시 버전으로 간주하여 해석함
👉 ✅ 해결법: 최신 환경이 아니라면 명시적으로 버전을 작성하고, 최신 환경이라면 도커 엔진의 버전을 먼저 체크하세요. - ❌ 실수: 볼륨(Volume) 설정 시 버전별 경로 지정 방식 차이를 모름
👉 원인: 버전 2와 3 사이에서 볼륨 드라이버 및 옵션 선언 위치가 변경됨
👉 ✅ 해결법: 볼륨 설정 시에는 반드시 사용하는 스키마의 공식 문서를 대조하여 `driver_opts` 위치를 확인하세요.
자주 묻는 질문
Q. 이제 `version` 필드를 안 써도 된다는데, 정말 삭제해도 되나요?
최신 도커 엔진과 컴포즈 V2를 사용한다면 삭제해도 ‘컴포즈 사양’에 따라 동작합니다. 하지만 운영 중인 서버의 엔진 버전을 확신할 수 없다면, 명시적으로 버전을 적어주는 것이 예상치 못한 동작을 막는 가장 안전한 방법입니다.
Q. 버전 2와 버전 3 중 무엇을 쓰는 것이 더 유리한가요?
대부분의 경우 버전 3가 유리합니다. 버전 3는 스웜 모드와 호환되도록 설계되었으며, 현대적인 컨테이너 관리 기능을 모두 포함하고 있습니다. 버전 2는 아주 오래된 시스템을 유지보수하는 경우가 아니라면 권장하지 않습니다.
Q. 설정 파일의 오류를 실행 전에 미리 알 수 있는 방법이 있나요?
네, 있습니다. docker compose config 명령어를 사용하세요. 이 명령은 작성한 파일을 실제로 해석하여 최종적인 구조를 보여줍니다. 만약 버전 불일치나 문법 오류가 있다면 이 단계에서 즉시 에러 메시지를 확인할 수 있습니다.
Q. `deploy` 섹션이 왜 이렇게 복잡한가요?
그 이유는 이 섹션이 원래 ‘오케스트레이션(여러 대의 서버를 관리하는 기능)’을 위해 만들어졌기 때문입니다. 단일 서버에서 리소스 제한만 하고 싶더라도, 최신 표준을 따르려면 `deploy` 구조를 사용하는 것이 맞습니다.
Q. 도커 컴포즈 V1과 V2의 차이가 무엇인가요?
V1은 파이썬 기반의 `docker-compose` 명령어였고, V2는 Go 언어로 작성되어 도커 엔진에 통합된 `docker compose` 명령어입니다. V2가 훨씬 빠르고 최신 사양을 잘 지원하므로, 가급적 V2를 사용하세요.
안전한 운영을 위한 마지막 점검
컴포즈 파일의 버전 설정은 단순한 텍스트 입력이 아닙니다. 이는 여러분의 서비스가 어떤 환경에서, 어떤 제약 조건 속에서 살아 숨 쉴지를 결정하는 설계도입니다. 작은 실수 하나가 전체 시스템을 멈추게 할 수 있다는 사실을 항상 염두에 두어야 해요.
- 파일의 `version` 번호와 서버의 도커 엔진 버전을 반드시 대조하세요.
- 리소스 제한 설정 시 `mem_limit` 대신 `deploy.resources` 사용을 권장합니다.
- 설정 반영 여부를 확인하기 위해 반드시 `docker compose config`로 검증하세요.
- 스웜 모드 사용 시에는 반드시 버전 3 이상의 스키마를 선택하세요.
- 최신 환경이라도 레거시 시스템과의 호환성을 위해 버전을 명시하는 것이 안전합니다.
오늘 배운 내용을 바탕으로 당장 다음 작업을 수행해 보세요. 오늘 할 일은 지금 운영 중인 서버의 도커 엔진 버전을 확인하는 것이고, 이번 주 할 일은 모든 컴포즈 파일에 `config` 명령어를 통한 검증 절차를 배포 파이프라인에 추가하는 것입니다. 실행 직전에는 반드시 로컬과 운영 환경의 차이를 재차 확인하는 습관을 가지세요.
작은 습관이 모여 대형 장애를 막는 강력한 방어선이 됩니다. 지금 사용 중인 설정 파일에서 위에서 언급한 잘못된 패턴이 발견되지는 않았나요? 지금 바로 확인해 보세요.
더 깊이 있는 컨테이너 관리가 궁금하시다면 아래 글을 참고해 보세요.
도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드