
도커 컴포즈 설정, 왜 매번 헷갈릴까요
로컬 개발 환경에서는 아무 문제 없이 잘 돌아가던 컨테이너가 왜 운영 서버에만 올리면 갑자기 동작하지 않는 걸까요? 혹은 팀원마다 컨테이너 실행 결과가 조금씩 달라서 밤을 지새운 적은 없으신가요? 이런 혼란의 중심에는 대개 도커 컴포즈의 이미지 지정 방식에 대한 명확한 기준이 없다는 문제가 숨어 있어요.
어떤 개발자는 편의를 위해 build 옵션으로 매번 이미지를 새로 만들고, 어떤 운영자는 안정성을 위해 외부 레지스트리의 image만 사용해요. 이 두 방식은 단순히 코드를 적는 법이 다른 게 아니라, 전체적인 배포 파이프라인과 인프라 운영 철학이 완전히 갈리는 지점이에요. 기준 없이 섞어 쓰다가는 결국 버전 불일치라는 거대한 재앙을 맞이하게 돼요.
이 글에서는 단순히 문법을 나열하는 것이 아니라, 실제 프로젝트 규모와 운영 단계에 따라 어떤 방식을 선택해야 비용과 시간을 아낄 수 있는지 심층적으로 분석해 드릴게요. 컴포즈 image 옵션 비교를 통해 여러분의 인프라를 더 단단하게 만드는 법을 찾아보세요.
이 글에서 다루는 핵심 내용들
image와build옵션의 근본적인 차이점 분석- 개발, 테스트, 운영 환경별 최적의 이미지 전략
- CI/CD 파이프라인 구축 시 고려해야 할 이미지 관리 규칙
- 실무에서 흔히 발생하는 이미지 버전 이슈 해결법
시작 전 반드시 체크해야 할 판단 기준
이미지 옵션을 결정하기 전에 여러분의 팀이 처한 상황을 객관적으로 돌아봐야 해요. 무조건 최신 기술을 쓴다고 좋은 것도 아니고, 무조건 안정적인 방식이 정답인 것도 아니거든요. 선택의 기준은 크게 속도, 재현성, 그리고 관리 복잡도라는 세 가지 축으로 나뉘어요.
먼저 image 옵션만 사용하는 방식은 이미 만들어진 결과물을 가져다 쓰는 거예요. 마치 완성된 밀키트를 사는 것과 비슷하죠. 요리 시간은 짧지만 내가 원하는 재료를 마음대로 추가하기는 어려워요. 반면에 build 옵션은 직접 재료를 손질해서 요리하는 것과 같아요. 내 입맛에 딱 맞는 맛을 낼 수 있지만, 준비 과정이 길고 재료의 질에 따라 맛이 달라질 수 있어요.
도커 컴포즈에서
image 옵션은 레지스트리(Docker Hub, ECR 등)에서 이미지를 가져오는 경로를 지정하고, build 옵션은 로컬의 Dockerfile을 기반으로 이미지를 생성하는 과정을 담당해요. 두 옵션은 상호 보완적으로 사용할 수도 있어요.아래 표를 통해 여러분이 지금 어떤 전략을 세워야 할지 가늠해 보세요.
| 비교 기준 | image 단독 사용 | build 단독 사용 | image + build 혼합 |
|---|---|---|---|
| 배포 속도 | 매우 빠름 (Pull만 수행) | 느림 (빌드 시간 필요) | 중간 (최초 빌드 후 빠름) |
| 환경 재현성 | 매우 높음 (태그 고정 시) | 보통 (로컬 환경 영향) | 높음 (태그로 관리 가능) |
| 커스텀 자유도 | 낮음 (제공된 대로 사용) | 매우 높음 (Dockerfile 제어) | 높음 (빌드 후 태깅 가능) |
| 추천 대상 | DB, Redis 등 오픈소스 | 개인 학습 및 프로토타입 | 실무 프로젝트 및 CI/CD |
이 기준들을 머릿속에 넣어두면, 앞으로 어떤 설정을 마주하더라도 무작정 따라 하는 것이 아니라 의도를 가지고 선택할 수 있게 돼요. 이제 본격적으로 각 단계별 실행 전략을 살펴볼까요?
상황별 최적의 이미지 운용 전략
실무에서 사용하는 방식은 크게 다섯 가지 단계로 구분할 수 있어요. 각 단계는 프로젝트의 성장 단계와 인프라의 규모에 따라 명확한 역할 분담을 가져야 해요.
STEP 1. 외부 이미지를 활용한 빠른 프로토타이핑
가장 단순한 방법은 image 옵션만 사용하는 거예요. 예를 들어 데이터베이스를 설정할 때 PostgreSQL이나 MySQL을 직접 빌드할 필요가 있을까요? 그렇지 않아요. 이미 전 세계 전문가들이 검증하고 최적화해 놓은 이미지가 Docker Hub에 가득하니까요.
이 방식의 가장 큰 장점은 설정의 간결함이에요. docker-compose.yml 파일에 단 한 줄만 적으면 서비스 준비가 끝나요. 하지만 주의할 점이 있어요. 반드시 특정 버전을 명시해야 한다는 거예요. 단순히 postgres:latest라고 적으면, 내일 아침 서버를 재시작했을 때 갑자기 메이저 버전이 올라가서 데이터베이스 구조가 깨지는 끔찍한 상황을 마주할 수 있어요. postgres:15.3-alpine처럼 구체적인 태그를 사용하는 습관을 들여야 해요.
STEP 2. 로컬 개발을 위한 커스텀 빌드 전략
여러분이 직접 작성한 애플리케이션 코드를 컨테이너로 띄워야 할 때는 build 옵션이 필수적이에요. 이때는 Dockerfile이 중심이 되죠. 개발 단계에서는 코드를 수정할 때마다 이미지를 다시 만들어야 하므로, 빌드 속도가 매우 중요해요.
이 단계에서는 .dockerignore 파일을 얼마나 잘 활용하느냐가 승부처예요. 불필요한 node_modules나 로그 파일, 로컬 설정 파일들이 빌드 컨텍스트에 포함되면 빌드 속도가 급격히 느려질 뿐만 아니라 이미지 용량도 불필요하게 커져요. 빌드 컨텍스트를 최소화하는 것이 개발 생산성을 높이는 핵심 비결이에요.
STEP 3. 하이브리드 방식: 빌드와 이미지 태깅의 결합
가장 권장하는 실무 방식은 image와 build를 동시에 사용하는 하이브리드 모델이에요. 컴포즈 파일에 image: my-app:v1.0과 build: .을 함께 적는 방식이죠. 이렇게 하면 도커 컴포즈는 로컬에서 이미지를 빌드한 뒤, 그 결과물에 지정된 이름을 붙여줘요.
이 방식이 왜 강력할까요? 로컬에서는 빌드를 통해 즉각적인 수정을 반영하면서도, 동시에 빌드된 결과물이 어떤 이름을 갖게 될지 명확히 규정할 수 있기 때문이에요. 이는 나중에 이미지를 레지스트리에 푸시(Push)할 때 별도의 작업 없이도 매끄러운 흐름을 만들어줘요. 개발 환경과 운영 환경의 가교 역할을 하는 가장 영리한 방법이라고 할 수 있어요.
STEP 4. CI/CD 파이프라인을 통한 이미지 배포 자동화
프로젝트가 커지면 개발자의 컴퓨터에서 빌드하는 것은 위험해져요. 대신 Jenkins, GitHub Actions 같은 CI/CD 도구가 빌드를 전담해야 하죠. 이때의 흐름은 다음과 같아요.
- 개발자가 코드를 푸시해요.
- CI 서버가
build옵션이 포함된 설정으로 이미지를 빌드해요. - 빌드된 이미지를 고유한 버전 태그(예: Git Commit Hash)와 함께 레지스트리에 저장해요.
- 운영 서버의 컴포즈 파일은 오직
image: registry.com/my-app:hash형태의image옵션만 사용하여 이미지를 가져와요.
이렇게 하면 운영 서버는 빌드 과정을 거치지 않고 오직 pull만 수행하면 되기 때문에 배포 속도가 비약적으로 빨라지고, 무엇보다 빌드 환경의 불확실성을 완전히 제거할 수 있어요.
STEP 5. 운영 환경의 불변성(Immutability) 확보
마지막으로 가장 중요한 단계는 운영 환경에서의 안정성이에요. 운영 서버에서는 절대로 build 옵션을 사용해서는 안 돼요. 운영 서버에서 직접 빌드를 한다는 것은 서버 환경에 따라 결과물이 달라질 수 있다는 위험을 감수하는 일이에요.
운영 서버의 docker-compose.yml은 오직 검증이 끝난 이미지를 내려받는 image 옵션만 존재해야 해요. 만약 설정이 변경되었다면, 이미지를 새로 빌드해서 레지스트리에 올린 뒤, 운영 서버에서는 이미지 태그만 교체하는 방식으로 진행해야 해요. 이것이 바로 현대적인 컨테이너 운영의 핵심인 불변 인프라(Immutable Infrastructure)의 기초예요.
– 개발 단계:
build: . 사용 (코드 변경 즉시 반영)– 검증(Staging) 단계: 빌드된 이미지를 레지스트리에 푸시 후
image: my-app:rc-1 사용– 운영(Production) 단계:
image: my-app:v1.0.2 사용 (절대 빌드 금지)자주 하는 실수와 해결법
현장에서 개발자와 운영자들이 가장 많이 저지르는 실수들을 정리했어요. 비슷한 상황을 겪고 있다면 아래 해결책을 바로 적용해 보세요.
- ❌
latest태그 남용 → 왜 발생하는가: 가장 편하게 최신 버전을 유지하고 싶어서 사용해요. → ✅ 해결법: 반드시 `major.minor.patch` 형태의 구체적인 버전을 명시하세요. - ❌ 로컬 빌드 이미지의 운영 서버 의존 → 왜 발생하는가: 운영 서버에서
build명령어를 직접 실행해서 해결하려고 해요. → ✅ 해결법: 반드시 CI/CD를 통해 레지스트리에 등록된 이미지만 사용하세요. - ❌ 불필요한 컨텍스트 포함 → 왜 발생하는가:
.dockerignore를 설정하지 않아 대용량 파일이 빌드에 포함돼요. → ✅ 해결법: 프로젝트 루트에.dockerignore를 만들고 로그, 데이터, 의존성 폴더를 제외하세요. - ❌ 이미지 태그 미변경 → 왜 발생하는가: 새로운 버전을 빌드하면서 기존 태그를 그대로 덮어써요. → ✅ 해결법: Git 커밋 해시나 빌드 번호를 태그로 활용하여 모든 이미지를 고유하게 만드세요.
- ❌ 레이어 캐시 오염 → 왜 발생하는가: 소스 코드가 바뀌었는데 예전 레이어를 계속 사용하여 변경 사항이 반영되지 않아요. → ✅ 해결법:
docker-compose build --no-cache명령어로 깨끗하게 다시 빌드하세요.
자주 묻는 질문
Q. image 옵션과 build 옵션을 한 파일에 같이 써도 문제가 없나요?
전혀 문제없어요! 오히려 권장되는 방식이에요. 로컬 개발 시에는 두 옵션을 같이 써서 빌드와 실행을 동시에 하고, 운영 환경에서는 build 부분만 삭제하거나 주석 처리한 채 image 옵션만 남겨두는 전략을 많이 사용해요.
Q. build 옵션을 사용하면 왜 운영 서버에서 쓰면 안 되나요?
운영 서버의 환경(OS 버전, 패키지 매니저 상태 등)에 따라 빌드 결과가 달라질 수 있기 때문이에요. 개발자 PC에서는 성공했지만 운영 서버에서는 빌드 도중 에러가 나거나, 엉뚱한 라이브러리가 설치될 위험이 커요.
Q. 이미지 용량을 줄이는 가장 효과적인 방법은 무엇인가요?
멀티 스테이지 빌드(Multi-stage build)를 사용하세요. 빌드에만 필요한 도구들은 첫 번째 스테이지에서 사용하고, 최종 결과물(실행 파일)만 두 번째 스테이지로 복사하면 이미지 크기를 1/10 이하로 줄일 수 있어요.
Q. docker-compose.yml 파일 하나로 개발과 운영을 모두 관리할 수 있나요?
네, 가능해요. 환경 변수 파일(.env)을 활용하거나, 운영용 설정 파일(docker-compose.prod.yml)을 따로 만들어 docker-compose -f docker-compose.yml -f docker-compose.prod.yml up처럼 덮어쓰는 방식을 추천해요.
성공적인 컨테이너 운영을 위한 마무리
지금까지 컴포즈의 이미지 옵션을 어떻게 선택하고 관리해야 하는지 깊이 있게 살펴봤어요. 결국 핵심은 ‘예측 가능한 환경을 만드는 것’이에요. 내가 오늘 만든 이미지가 내일 서버에서도 똑같이 돌아갈 것이라는 확신이 있어야 안정적인 서비스 운영이 가능하니까요.
- DB 같은 오픈소스는
image옵션으로 검증된 버전만 사용하세요. - 개발 중에는
build와image를 혼합하여 작업 효율을 높이세요. - 운영 서버에서는
build를 금지하고 반드시 레지스트리의image만 사용하세요. - 모든 이미지에는
latest대신 구체적인 버전 태그를 붙이세요. - CI/CD를 통해 빌드와 배포 프로세스를 분리하고 자동화하세요.
.dockerignore를 통해 빌드 컨텍스트를 최소화하여 속도를 높이세요.
오늘 배운 내용을 바탕으로 지금 바로 여러분의 docker-compose.yml 파일을 열어보세요. 혹시 latest 태그를 쓰고 있거나, 운영 서버에서 빌드를 시도하고 있지는 않나요? 작은 변화가 모여 거대한 시스템의 안정성을 만듭니다.
우선순위 기준 세 가지(속도, 재현성, 관리 편의성)를 정해 두면 앞으로의 기술 선택이 훨씬 쉬워질 거예요. 지금 바로 프로젝트의 이미지 관리 전략을 한 단계 업그레이드해 보세요!
더 깊이 있는 도커 활용법이 궁금하다면 다음 글을 확인해 보세요.
도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드