
이미지 설정 하나로 멈춰버린 서버, 왜 발생할까요?
새로 작성한 코드를 배포하려고 docker-compose up 명령어를 입력했는데, 갑자기 서버가 먹통이 된 경험이 있나요? 분명 어제까지는 잘 돌아가던 서비스인데, 원인을 찾아보니 엉뚱한 버전의 이미지가 실행되고 있는 경우가 생각보다 정말 많아요. 특히 이미지 태그를 명시하지 않고 latest만 사용했을 때 이런 사고가 빈번하게 일어나곤 해요.
도커를 막 도입한 운영자나 백엔드 개발자라면, 단순히 컨테이너를 띄우는 것을 넘어 어떤 이미지를, 어떻게, 어떤 버전으로 가져올지 제어하는 능력이 필수적이에요. 컴포즈 파일 내의 image 옵션은 단순히 이름만 적는 칸이 아니라, 여러분의 서비스 안정성을 결정짓는 아주 중요한 설계도와 같아요.
이 글을 끝까지 읽고 나면, 이미지 옵션의 기본 개념은 물론이고 실무에서 바로 써먹을 수 있는 태그 관리 전략과 설정 예시까지 완벽하게 내 것으로 만들 수 있어요. 더 이상 배포할 때마다 가슴 졸이지 않아도 괜찮아요.
- 컴포즈 image 옵션의 핵심 개념과 용어 정리
- 이미지 옵션과 빌드 옵션의 결정적인 차이점
- 실무에서 바로 활용하는 docker-compose.yml 설정 예시
- 자주 발생하는 실수와 해결을 위한 FAQ
본격적인 설정 전, 반드시 알아야 할 기본 개념
컴포즈 파일에 컴포즈 image 옵션 정리를 시작하기 전에, 우리가 다루는 용어들이 정확히 무엇을 의미하는지 짚고 넘어가야 해요. 개념이 흔들리면 나중에 복잡한 설정을 만났을 때 길을 잃기 쉽거든요.
이미지와 컨테이너의 관계 이해하기
가장 먼저 구분해야 할 것은 이미지(Image)와 컨테이너(Container)의 차이에요. 이미지는 실행 파일, 라이브러리, 설정 등이 모두 포함된 ‘설계도’ 혹은 ‘압축 파일’ 같은 존재예요. 반면 컨테이너는 이 설계도를 바탕으로 실제로 메모리에 올라가 살아 움직이는 ‘실행 상태’를 말해요. 도커 컴포즈의 image 옵션은 바로 이 설계도를 어디서, 어떤 것을 가져올지 지정하는 역할을 수행해요.
레지스트리와 이미지 태그의 역할
우리가 이미지를 가져오는 저장소를 레지스트리(Registry)라고 불러요. 가장 대표적인 것이 도커 허브(Docker Hub)죠. 그리고 이미지 이름 뒤에 붙는 태그(Tag)는 이미지의 특정 버전을 가리키는 별명이에요. 예를 들어 nginx:1.25라고 쓰면, nginx라는 이미지 중에서도 1.25 버전이라는 특정 시점의 설계도를 쓰겠다는 약속이에요.
설정 방식에 따른 선택 기준
도커 컴포즈를 사용할 때는 이미 만들어진 이미지를 가져다 쓸 것인지, 아니면 내가 직접 만든 파일을 빌드해서 쓸 것인지 결정해야 해요. 이 선택에 따라 설정 방식이 완전히 달라지므로 아래 표를 통해 차이점을 명확히 익혀두세요.
| 비교 항목 | image 옵션 중심 | build 옵션 중심 |
|---|---|---|
| 주요 용도 | 이미 만들어진 이미지 사용 (DB, 웹서버 등) | 내 소스코드를 포함한 커스텀 이미지 제작 |
| 실행 속도 | 매우 빠름 (이미 다운로드된 경우) | 느림 (Dockerfile 읽기 및 레이어 생성 필요) |
| 관리 대상 | 외부 레지스트리의 이미지 버전 관리 | Dockerfile과 빌드 컨텍스트 관리 |
| 유연성 | 낮음 (정해진 이미지 그대로 사용) | 매우 높음 (모든 환경 커스텀 가능) |
결론적으로, 외부에서 검증된 소프트웨어를 쓸 때는 image 옵션을, 우리 서비스만의 특수한 환경이 필요할 때는 build 옵션을 사용하는 것이 기본 원칙이에요. 이 두 가지를 적절히 조합하는 능력이 바로 숙련된 데브옵스의 시작이에요.
실무에서 바로 쓰는 컴포즈 image 옵션 활용 가이드
이제 이론을 넘어 실제 설정 파일인 docker-compose.yml을 어떻게 작성해야 하는지 단계별로 살펴볼게요. 단순히 이름을 적는 것을 넘어, 운영 환경의 안정성을 높이는 방법들을 담았어요.
STEP 1. 이미지 경로와 태그의 정석적인 작성법
이미지 옵션을 작성할 때는 레지스트리 주소/이미지 이름:태그 형식을 지켜야 해요. 만약 도커 허브의 기본 이미지를 쓴다면 레지스트리 주소는 생략할 수 있지만, 사내 프라이빗 레지스트리나 AWS ECR 같은 곳을 쓴다면 반드시 주소를 포함해야 해요.
예를 들어, 사내 레지스트리에 있는 특정 버전의 애플리케이션을 가져온다면 다음과 같이 작성해요.image: my-registry.com:5000/my-app:v1.2.3
여기서 가장 주의할 점은 태그를 생략하지 않는 것이에요. 태그를 쓰지 않으면 도커는 자동으로 :latest를 붙여서 가져와요. 문제는 :latest가 가리키는 이미지는 계속 변할 수 있다는 점이에요. 오늘 잘 돌아가던 서버가 내일 갑자기 깨진다면, 높은 확률로 이미지 태그가 고정되어 있지 않기 때문이에요.
STEP 2. image와 build 옵션의 전략적 조합
실무에서는 image와 build 옵션을 한 서비스 안에 동시에 쓰는 경우가 아주 흔해요. 이는 내가 직접 만든 소스코드를 빌드해서 이미지를 만들되, 그 결과물의 이름을 특정하게 지정하고 싶을 때 사용해요.
아래의 예시를 눈여겨보세요.
services:
web-app:
build: .
image: my-custom-app:v1.0.0
이렇게 작성하면 컴포즈는 먼저 현재 디렉토리(.)의 Dockerfile을 읽어 이미지를 빌드해요. 그리고 그 빌드된 이미지에 my-custom-app:v1.0.0이라는 이름을 붙여줘요. 이렇게 하면 빌드와 동시에 이미지 관리까지 한 번에 해결할 수 있어서 매우 효율적이에요.
STEP 3. 운영 환경을 위한 태그 관리 전략 세우기
어떤 태그를 사용할지는 서비스의 성격에 따라 달라져요. 무작정 아무 태그나 쓰는 것이 아니라, 아래의 세 가지 전략 중 하나를 선택하는 것이 좋아요.
- 버전 고정 전략 (추천):
nginx:1.25.3처럼 소수점 단위까지 명시하는 방법이에요. 가장 안전하고 배포 결과가 예측 가능해요. - 커밋 해시 전략: CI/CD 파이프라인을 사용한다면 Git의 커밋 해시(예:
app:a1b2c3d)를 태그로 쓰는 방법이에요. 어떤 코드가 배포되었는지 추적하기 매우 용이해요. - latest 사용 지양: 개발 환경에서는 편할 수 있지만, 운영 환경에서는 절대 금물이에요. 언제 어떤 코드가 들어올지 모르기 때문이죠.
STEP 4. 실무용 docker-compose.yml 종합 예시
위의 개념들을 모두 녹여낸 종합 예시를 보여드릴게요. 실제 업무에서 웹 애플리케이션, 데이터베이스, 캐시 서버를 함께 운영하는 시나리오를 가정했어요.
version: '3.8'
services:
# 1. 데이터베이스: 검증된 특정 버전을 사용 (image 중심)
db:
image: postgres:15.4-alpine
environment:
POSTGRES_PASSWORD: example_password
volumes:
- db_data:/var/lib/postgresql/data
# 2. 캐시 서버: 가벼운 버전을 사용하여 리소스 절약 (image 중심)
redis:
image: redis:7.2-alpine
# 3. 메인 애플리케이션: 소스코드를 빌드하여 이미지 생성 (build + image 조합)
app:
build:
context: .
dockerfile: Dockerfile.prod
image: my-company/backend-api:v2.1.0
depends_on:
- db
- redis
ports:
- "8080:8080"
volumes:
db_data:
이 설정의 핵심은 모든 이미지에 명확한 버전(Tag)을 부여했다는 점이에요. Postgres는 15.4 버전을, Redis는 7.2 버전을, 그리고 우리 앱은 v2.1.0이라는 이름을 갖게 했죠. 이렇게 하면 어떤 환경에서도 동일한 구성으로 컨테이너가 실행됨을 보장할 수 있어요.
alpine 이미지는 일반 이미지보다 용량이 매우 작고 보안 취약점이 적어 운영 환경에서 선호돼요. 이미지 이름 뒤에
-alpine이 붙어있는지 확인해 보세요!자주 하는 실수와 해결법 + FAQ
설정은 완벽해 보이는데 왜 안 될까요? 현장에서 개발자들이 가장 자주 겪는 문제들을 모아봤어요.
자주 하는 실수와 해결법
❌ 실수: image: nginx라고만 적고 배포함
왜 발생하는가: 태그를 생략하면 자동으로 :latest가 붙기 때문이에요. 이후에 Nginx 이미지가 업데이트되면 내 서버의 설정과 맞지 않는 최신 버전이 내려받아져 에러가 발생해요.
✅ 해결법: nginx:1.25처럼 반드시 명시적인 버전을 사용하세요.
❌ 실수: 이미지를 수정했는데 docker-compose up만 실행함
왜 발생하는가: 도커는 이미 로컬에 해당 이름의 이미지가 있으면 새로 다운로드하거나 빌드하지 않고 기존 것을 그대로 사용하기 때문이에요.
✅ 해결법: 새로운 이미지를 강제로 반영하려면 docker-compose pull 명령어로 먼저 이미지를 가져오거나, 빌드 시에는 --build 옵션을 붙여주세요.
❌ 실수: build와 image를 같이 썼는데 이름이 꼬임
왜 발생하는가: build만 쓰면 도커가 임의의 해시값으로 이름을 붙여버려 관리가 안 돼요.
✅ 해결법: 빌드한 결과물에 이름을 붙여 관리하려면 반드시 image: [이름:태그]를 함께 명시하세요.
❌ 실수: 로컬 이미지를 쓰려는데 image: my-app을 적었는데 에러가 남
왜 발생하는가: 도커가 로컬에 해당 이미지가 없으면 자동으로 레지스트리에서 찾으려고 시도하기 때문이에요.
✅ 해결법: 로컬 이미지를 쓰려면 먼저 docker build -t my-app .으로 이미지를 직접 만들어 두어야 해요.
이미지 태그를 관리할 때
latest는 개발 서버에서만 사용하고, 실서비스(Production) 환경에서는 반드시 고정된 버전을 사용해야 서버 다운타임을 방지할 수 있어요!자주 묻는 질문
Q. docker-compose pull은 언제 사용해야 하나요?
이미지 태그는 같은데(예: :latest나 특정 버전) 레지스트리에 올라온 실제 내용이 업데이트되었을 때, 로컬에 있는 구버전 이미지를 최신 내용으로 교체하기 위해 사용해요.
Q. image 옵션만 써도 빌드가 자동으로 되나요?
아니요, image 옵션은 이미 존재하는 이미지를 ‘가져오는’ 설정이에요. 이미지를 직접 만드는 과정이 필요하다면 반드시 build 옵션이 포함되어야 해요.
Q. 태그를 어떻게 정하는 게 가장 효율적일까요?
가장 추천하는 방식은 Semantic Versioning(SemVer)이에요. 예를 들어 v1.2.3처럼 메이저, 마이너, 패치 버전을 나누어 관리하면 변경 사항을 파악하기 매우 쉬워요.
Q. 이미지를 너무 많이 다운로드받아서 디스크 용량이 부족해요.
사용하지 않는 오래된 이미지나 컨테이너 찌꺼기가 쌓여서 그럴 거예요. docker system prune 명령어를 사용하면 사용하지 않는 이미지와 네트워크 등을 한 번에 정리할 수 있어요.
Q. 사내 전용 이미지를 쓰고 싶은데 어떻게 경로를 적나요?
레지스트리 주소를 앞에 붙여주면 돼요. image: registry.mycompany.com/project/my-image:1.0와 같은 식이죠. 이때 사내 레지스트리에 로그인(docker login)이 되어 있어야 해요.
안정적인 운영을 위한 마지막 체크리스트
오늘 배운 컴포즈 image 옵션 정리 내용을 바탕으로, 여러분의 인프라가 얼마나 준비되었는지 마지막으로 점검해 보세요. 이 내용만 지켜도 배포 사고의 80% 이상은 막을 수 있어요.
- 운영 환경에서는
:latest태그 사용을 절대 금지하세요. - 이미지 이름 뒤에 반드시 명시적인 버전(Tag)을 붙여주세요.
- 커스텀 이미지를 만들 때는
build와image를 함께 사용하여 관리하세요. - 사내 레지스트리 사용 시에는 전체 URL 경로를 정확히 기입하세요.
- 이미지 업데이트가 필요할 때는
docker-compose pull로 먼저 최신 상태를 확인하세요. - 리소스 최적화를 위해 가능하면
-alpine이미지를 활용해 보세요.
자, 이제 이론은 충분해요. 이제 바로 실행해 볼 차례예요! 위에서 설명해 드린 설정 예시 코드를 그대로 복사해서 여러분의 테스트 서버에서 먼저 실행해 보세요. 작은 환경에서 직접 성공해 보는 경험이 가장 빠른 학습 방법이에요.
만약 도커 컴포즈의 더 깊은 동작 원리가 궁금하다면, 도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드 글을 함께 읽어보시는 것을 추천드려요. 다음 단계에서는 Dockerfile을 활용한 커스텀 이미지 최적화 기법에 대해 더 자세히 다뤄볼 예정이니 기대해 주세요!