
복잡한 컨테이너 실행 명령어를 하나로 묶는 마법
새로운 프로젝트를 시작할 때마다 터미널에 길게 늘어진 docker run 명령어를 수없이 입력하던 기억이 있나요? 데이터베이스를 먼저 띄우고, 네트워크를 만들고, 환경 변수를 하나하나 설정하며 컨테이너를 실행하는 과정은 생각보다 번거롭고 실수하기 쉬워요. 특히 팀원들에게 개발 환경을 공유해야 할 때, 내가 입력했던 그 긴 명령어를 일일이 전달하는 것도 매우 비효율적인 일이에요.
이런 상황에서 겪게 되는 가장 흔한 문제는 바로 환경의 불일치예요. 내 컴퓨터에서는 잘 돌아가던 서비스가 동료의 컴퓨터에서는 포트 충돌이나 네트워크 설정 문제로 실행되지 않는 상황이 빈번하게 발생하죠. 명령어가 길어질수록 오타가 날 확률은 높아지고, 어떤 옵션을 사용했는지 잊어버리는 일도 다반사예요. 결국 서비스가 커지면서 관리해야 할 컨테이너가 5개, 10개로 늘어나면 수동으로 관리하는 것은 불가능에 가까워져요.
이 문제를 한 번에 해결해 주는 도구가 바로 도커 컴포즈예요. 설정 파일 하나에 모든 실행 옵션을 기록해 두면, 단 한 줄의 명령어로 복잡한 다중 컨테이너 환경을 즉시 구축할 수 있어요. 도커 컴포즈 설정 예제를 제대로 익혀두면, 단순히 명령어를 줄이는 것을 넘어 인프라를 코드로 관리하는 IaC(Infrastructure as Code)의 기초를 다질 수 있어요.
이 글을 읽고 나면 다음과 같은 능력을 갖추게 될 거예요.
- docker-compose.yml 파일의 핵심 구조를 완벽히 이해해요.
- 실무에서 바로 가져다 쓸 수 있는 서비스별 설정 예제를 습득해요.
- 네트워크와 볼륨 설정을 통해 데이터와 통신을 제어해요.
- 설정 오류로 인해 컨테이너가 뜨지 않을 때 스스로 해결할 수 있어요.
도커 컴포즈를 시작하기 전 반드시 알아야 할 기초 지식
본격적으로 파일을 작성하기 전에, 우리가 다루는 도구가 무엇인지 명확히 짚고 넘어가야 해요. 도커 컴포즈는 여러 개의 컨테이너를 하나의 서비스 단위로 정의하고 관리하는 도구예요. 이를 위해 가장 먼저 익혀야 할 것은 YAML 문법이에요. YAML은 사람이 읽기 아주 편하게 설계된 데이터 직렬화 양식인데, 여기서 가장 중요한 점은 들여쓰기(Indentation)예요. 단 한 칸의 공백 차이로도 전체 설정이 깨질 수 있으니 주의가 필요해요.
또한, 도커 엔진이 이미 설치되어 있어야 하며, 최신 버전의 도커를 사용 중이라면 별도의 설치 없이 docker compose 명령어를 바로 사용할 수 있어요. 예전에는 도커 컴포즈가 별도의 파이썬 프로그램이었지만, 지금은 도커의 플러그인 형태로 통합되어 훨씬 빠르고 안정적으로 동작해요.
YAML 파일에서는 탭(Tab) 대신 반드시 스페이스(Space)를 사용해야 해요. 많은 코드 에디터가 자동으로 변환해 주지만, 설정을 잘못하면 나중에 원인을 찾기 힘든 오류가 발생할 수 있어요.
도커 명령어를 직접 사용하는 것과 컴포즈를 사용하는 것 사이에는 어떤 차이가 있을까요? 아래 표를 통해 기준을 확인해 보세요.
| 비교 항목 | 도커 명령어 (docker run) | 도커 컴포즈 (docker compose) |
|---|---|---|
| 관리 단위 | 개별 컨테이너 하나씩 | 여러 서비스를 묶은 전체 스택 |
| 설정 보존 | 명령어를 매번 입력하거나 스크립트 작성 | yaml 파일에 영구적으로 기록 |
| 네트워크 연결 | 수동으로 네트워크를 만들고 연결 | 자동으로 서비스 간 내부 네트워크 생성 |
| 환경 공유 | 불가능하거나 매우 어려움 | 파일 공유만으로 즉시 동일 환경 구축 |
만약 여러분이 단 하나의 컨테이너만 띄우고 끝낼 예정이라면 굳이 컴포즈를 쓸 필요는 없어요. 하지만 데이터베이스, 백엔드 서버, 프론트엔드 서버가 서로 통신해야 하는 마이크로서비스 아키텍처를 다룬다면 도커 컴포즈는 선택이 아닌 필수예요.
실전에서 바로 쓰는 단계별 도커 컴포즈 설정 예제
이제 본격적으로 코드를 작성해 볼 시간이에요. 이론보다는 실제 동작하는 예제를 보면서 감을 익히는 것이 훨씬 빨라요. 단계별로 난이도를 높여가며 설명해 드릴게요.
STEP 1. 가장 단순한 웹 서버 띄우기
가장 먼저 웹 서버 하나만 실행하는 최소 구성 예제예요. 복잡한 설정 없이 특정 이미지를 가져와서 포트만 열어주는 구조예요. 이 구조를 이해했다면 이미 절반은 성공한 셈이에요.
version: '3.8'
services:
web-server:
image: nginx:latest
ports:
- "8080:80"
여기서 version은 컴포즈 파일의 형식을 정의해요. services 아래에는 우리가 띄울 컨테이너들의 이름이 와요. ports 설정은 매우 중요한데, 앞의 8080은 내 컴퓨터의 포트이고 뒤의 80은 컨테이너 내부의 포트예요. 즉, 브라우저에서 localhost:8080으로 접속하면 컨테이너 내부의 80번 포트로 연결된다는 뜻이에요.
STEP 2. 웹 서비스와 데이터베이스 연결하기
실제 애플리케이션은 데이터를 저장해야 하므로 데이터베이스가 꼭 필요해요. 이번에는 웹 서버와 MySQL 데이터베이스를 함께 띄우는 예제를 살펴볼게요. 서비스 간의 연결이 어떻게 이루어지는지 주목해 보세요.
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: mysecretpassword
MYSQL_DATABASE: myappdb
volumes:
- db_data:/var/lib/mysql
web:
image: my-app-image:latest
ports:
- "3000:3000"
depends_on:
- db
environment:
DB_HOST: db
DB_USER: root
DB_PASSWORD: mysecretpassword
volumes:
db_data:
이 예제에서 핵심은 세 가지예요. 첫째, environment 옵션을 통해 데이터베이스의 비밀번호와 이름 같은 민감한 정보를 컨테이너에 전달해요. 둘째, depends_on을 사용하여 데이터베이스가 먼저 실행된 후에 웹 서버가 실행되도록 순서를 정해줬어요. 셋째, volumes 설정을 통해 컨테이너가 삭제되어도 데이터가 사라지지 않게 저장 공간을 연결해 두었어요.
웹 서버에서 데이터베이스에 접속할 때 IP 주소를 쓸 필요가 없어요. 도커 컴포즈가 자동으로 만들어준 네트워크 덕분에 서비스 이름인
db를 호스트 이름처럼 그대로 사용할 수 있어요.STEP 3. 환경 변수(.env) 파일로 보안 강화하기
비밀번호를 yaml 파일에 직접 적는 것은 보안상 매우 위험해요. 특히 이 파일을 Git 같은 곳에 올린다면 큰 사고가 날 수 있죠. 그래서 실무에서는 .env 파일을 사용해 값을 따로 관리해요.
먼저 파일 이름을 .env로 만들고 아래 내용을 적어두세요.
DB_PASSWORD=very-secure-password-123
APP_PORT=8080
그 다음 yaml 파일에서는 이렇게 변수를 불러와서 써요.
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
web:
ports:
- "${APP_PORT}:3000"
이렇게 하면 yaml 파일은 코드로서 안전하게 공유할 수 있고, 실제 비밀번호는 내 컴퓨터의 .env 파일에만 머물게 되어 아주 안전해요.
STEP 4. 네트워크와 볼륨의 정밀 제어
서비스가 많아지면 어떤 컨테이너끼리 서로 대화할 수 있게 할지 정밀하게 결정해야 해요. 예를 들어, 웹 서버는 데이터베이스와 통신해야 하지만, 데이터베이스는 외부 인터넷으로부터 격리되어야 할 수도 있죠. 이때 networks 설정을 활용해요.
version: '3.8'
services:
proxy:
image: nginx:latest
networks:
- frontend
api:
image: my-api:latest
networks:
- frontend
- backend
db:
image: postgres:latest
networks:
- backend
networks:
frontend:
backend:
volumes:
db_data:
위 설정에서 proxy는 frontend 네트워크에만 속해 있어 api와 대화할 수 있어요. 하지만 db와는 직접 연결되지 않죠. api는 두 네트워크에 모두 속해 있어 양쪽과 소통하는 다리 역할을 수행해요. 이런 설계는 보안을 강화하는 데 결정적인 역할을 해요.
또한, 볼륨 설정 시 호스트 경로를 직접 지정할 수도 있어요. ./data:/var/lib/mysql처럼 적으면 내 컴퓨터의 현재 폴더 안에 있는 data 폴더가 컨테이너와 동기화돼요. 개발 단계에서 소스 코드를 실시간으로 수정하고 바로 확인하고 싶을 때 매우 유용하죠.
STEP 5. 운영 환경을 위한 헬스체크와 재시작 정책
실제 서비스 중인 서버에서 컨테이너가 갑자기 죽어버리면 큰일이겠죠? 도커 컴포즈는 스스로 컨테이너를 감시하고 살려내는 기능도 제공해요. restart 정책과 healthcheck를 함께 사용하면 훨씬 견고한 시스템을 만들 수 있어요.
services:
web:
image: my-app:latest
restart: always
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
여기서 restart: always는 컨테이너가 어떤 이유로든 종료되면 무조건 다시 시작하라는 명령이에요. healthcheck는 단순히 프로세스가 살아있는지를 넘어, 실제로 웹 서비스가 요청을 처리할 준비가 되었는지 30초마다 체크해요. 만약 세 번 연속으로 실패하면 컨테이너에 문제가 생긴 것으로 판단하고 대응할 수 있는 근거가 됩니다.
자주 하는 실수와 해결법 및 궁금한 점
도커 컴포즈를 처음 다루다 보면 아무리 봐도 틀린 게 없는 것 같은데 실행이 안 되어 답답할 때가 많아요. 가장 자주 발생하는 실수들과 그 해결책을 정리했어요.
- ❌ 들여쓰기 오류 → YAML은 스페이스 한 칸에도 민감해요.
✅ 에디터의 YAML Validator 기능을 사용하거나, 모든 들여쓰기가 스페이스로 되어 있는지 확인하세요. - ❌ 포트 충돌 발생 → “port is already allocated”라는 에러가 뜬다면 이미 다른 프로그램이 그 포트를 쓰고 있는 거예요.
✅docker ps로 사용 중인 포트를 확인하거나, yaml 파일에서 호스트 포트 번호를 변경하세요. - ❌ 환경 변수 인식 실패 → .env 파일을 만들었는데도 값이 적용되지 않아요.
✅ .env 파일이docker-compose.yml과 같은 경로에 있는지, 그리고 변수 이름에 오타가 없는지 확인하세요. - ❌ 데이터가 사라짐 → 컨테이너를 지웠더니 데이터베이스 데이터가 모두 증발했어요.
✅ 반드시volumes설정을 통해 호스트나 이름 있는 볼륨(named volume)에 데이터를 저장하도록 설정했는지 확인하세요. - ❌ 컨테이너 간 통신 불가 → 웹 서버가 DB를 못 찾는다고 에러를 내뱉어요.
✅ 서비스 이름(예: db)을 호스트 이름으로 썼는지, 그리고 두 서비스가 같은 네트워크에 속해 있는지 확인하세요.
설정 파일을 수정한 후에는 반드시
docker compose up -d 명령어를 다시 실행해야 변경 사항이 적용돼요. 단순히 파일을 저장한다고 컨테이너가 자동으로 바뀌지 않아요.자주 묻는 질문
Q. docker-compose와 docker compose의 차이가 무엇인가요?
두 명령어는 거의 같지만 내부적으로는 달라요. 하이픈이 있는 버전은 예전 방식이고, 띄어쓰기가 있는 버전은 최신 도커 플러그인 방식이에요. 지금은 띄어쓰기가 있는 docker compose를 사용하는 것을 권장해요.
Q. yaml 파일의 버전(version)은 꼭 써야 하나요?
최신 도커 컴포즈 버전에서는 버전 명시가 필수는 아니지만, 특정 기능을 사용하기 위해 호환성을 맞추는 용도로 써주는 것이 안전해요. 보통 '3.8' 정도를 가장 많이 사용해요.
Q. 설정이 맞는지 미리 검증할 수 있는 방법이 있나요?
네, 아주 유용한 명령어가 있어요. 터미널에 docker compose config를 입력해 보세요. 작성한 yaml 파일의 문법이 맞는지 검사하고, 최종적으로 적용될 설정 내용을 화면에 보여줘요.
Q. 컨테이너를 완전히 삭제하고 싶을 때는 어떻게 하나요?docker compose down 명령어를 사용하면 생성된 컨테이너와 네트워크를 깔끔하게 삭제할 수 있어요. 만약 볼륨까지 싹 지우고 싶다면 docker compose down -v를 사용하세요.
Q. 여러 개의 yaml 파일을 하나로 합쳐서 실행할 수 있나요?
네, docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d처럼 -f 옵션을 여러 번 써서 파일을 겹쳐 실행할 수 있어요. 개발용과 운영용 설정을 분리할 때 아주 유용해요.
도커 컴포즈 마스터를 위한 마지막 체크리스트
지금까지 도커 컴포즈의 기본 개념부터 실무 예제까지 상세히 살펴보았어요. 처음에는 복잡해 보일 수 있지만, 몇 번만 직접 작성해 보면 이보다 편리한 도구도 없다는 사실을 깨닫게 될 거예요. 마지막으로 여러분의 설정 파일이 제대로 만들어졌는지 아래 리스트로 점검해 보세요.
- YAML 파일의 들여쓰기가 스페이스로 정확히 맞추어졌는가?
- 민감한 정보(비밀번호 등)를 .env 파일을 통해 관리하고 있는가?
- 데이터 보존을 위해 볼륨(volumes) 설정을 빠뜨리지 않았는가?
- 서비스 간의 통신을 위해 네트워크(networks)를 적절히 구성했는가?
- 컨테이너 재시작 정책(restart)을 설정하여 안정성을 높였는가?
docker compose config명령어로 문법 검증을 마쳤는가?
이제 여러분은 단순히 명령어를 입력하는 사용자를 넘어, 인프라를 설계하는 운영자의 관점을 갖게 되었어요. 오늘 배운 예제 파일을 여러분의 프로젝트 구조에 맞게 조금씩 수정하며 직접 실행해 보세요. 작은 성공이 쌓여 숙련된 데브옵스 능력이 될 거예요.
🚀 다음 단계로 나아가기
- 오늘 할 일: 위 예제 중 하나를 복사해서 내 컴퓨터에서 직접 실행해 보기
- 이번 주 할 일: 실제 사용 중인 프로젝트의 데이터베이스를 도커 컴포즈로 전환해 보기
- 실행 직전 할 일: .env 파일을 작성하고 Git에 업로드되지 않도록 .gitignore에 추가하기
제시해 드린 예제 파일을 내려받아 프로젝트 구조에 맞게 값만 바꿔 사용해 보세요. 훨씬 수월한 개발 환경을 경험하실 수 있을 거예요.
도커의 더 깊은 활용법이 궁금하다면 도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드를 함께 읽어보시는 것을 추천드려요.