G:
Docker Compose YML 파일, 이렇게 작성하세요! 개발 환경 구축 첫걸음
개발 환경 설정, 매번 복잡하고 번거로우셨나요? 🤯 특히 여러 서비스가 얽혀 있는 프로젝트라면 더욱 그렇죠. 하지만 Docker Compose YML 파일만 제대로 활용하면 이러한 고민을 한 번에 날려버릴 수 있습니다! 🎉 단 몇 줄의 코드로 개발 환경을 즉시 구축하고, 팀원들과 완벽하게 동일한 환경을 공유할 수 있게 될 거예요. 이 글에서는 Docker Compose YML 파일의 기본부터 실제 활용 예시, 그리고 고급 팁까지, 여러분의 개발 생산성을 폭발적으로 끌어올릴 비법을 알려드립니다. 지금 바로 시작해 볼까요?
💡 왜 Docker Compose YML 파일이 필요할까요?
전통적인 개발 환경 구축은 수많은 수동 작업과 설정 오류의 위험을 안고 있습니다. 데이터베이스 설치, 웹 서버 설정, 캐시 서버 연결 등, 서비스가 많아질수록 복잡도는 기하급수적으로 증가하죠. 게다가 팀원마다 다른 운영체제나 라이브러리 버전을 사용하면 “내 컴퓨터에서는 잘 되는데…”와 같은 비극적인 상황이 발생하기도 합니다. 😱
이러한 문제들을 해결해 주는 마법 같은 도구가 바로 Docker Compose입니다. Docker Compose는 여러 개의 Docker 컨테이너를 하나로 묶어 정의하고 실행할 수 있게 해주는 도구예요. 그리고 이 모든 정의를 담는 파일이 바로 docker-compose.yml
(또는 compose.yaml
) 파일입니다. 🚀
✅ Docker Compose 사용의 핵심 이점
- 환경 일관성 확보: 팀원 모두 동일한 개발 환경에서 작업하여 “내 컴퓨터에서는 되는데…” 문제를 없앱니다.
- 설정 자동화: 복잡한 설치 및 설정 과정을 YML 파일 하나로 자동화합니다.
- 서비스 간 쉬운 연결: 웹 서버, DB, 캐시 등 여러 서비스를 쉽게 연결하고 관리합니다.
- 버전 관리: YML 파일을 Git과 같은 형상 관리 시스템으로 관리하여 환경 변경 이력을 추적할 수 있습니다.
- 개발 생산성 향상: 환경 설정에 드는 시간을 줄여 핵심 개발에 집중할 수 있게 합니다.
📜 YML 파일의 기본 구조 파헤치기
Docker Compose YML 파일은 YAML(YAML Ain’t Markup Language) 문법을 따릅니다. 들여쓰기가 매우 중요하니 주의해야 합니다. 기본적인 구조는 다음과 같습니다.
version: '3.8' # Docker Compose 파일 형식 버전 지정 (필수)
services: # 정의할 서비스들을 나열하는 섹션
[서비스 이름 1]:
image: [도커 이미지 이름]
ports:
- "[호스트 포트]:[컨테이너 포트]"
volumes:
- "[호스트 경로]:[컨테이너 경로]"
environment:
- [환경 변수 설정]
depends_on: # 의존성 정의
- [다른 서비스 이름]
networks: # 네트워크 연결
- [네트워크 이름]
[서비스 이름 2]:
# ...
networks: # 서비스들이 사용할 네트워크 정의 (선택 사항)
[네트워크 이름]:
driver: bridge
volumes: # 영구 데이터를 저장할 볼륨 정의 (선택 사항)
[볼륨 이름]:
driver: local
핵심 블록 상세 설명
version
: 사용하고 있는 Docker Compose 파일의 버전을 명시합니다. 최신 기능들을 사용하기 위해 일반적으로 ‘3.8’ 또는 ‘3.9’ 버전을 사용합니다.services
: Docker Compose의 핵심! 🏗️ 애플리케이션을 구성하는 각각의 컨테이너를 정의하는 곳입니다. 각 서비스는 독립적인 컨테이너로 작동하며, 이미지, 포트, 볼륨, 환경 변수 등 다양한 속성을 가질 수 있습니다.networks
: 서비스 간 통신을 위해 사용할 네트워크를 정의합니다. 별도로 정의하지 않으면 기본 네트워크가 생성되지만, 명시적으로 정의하면 더 유연하게 네트워크를 관리할 수 있습니다.volumes
: 컨테이너의 데이터를 영구적으로 저장할 볼륨을 정의합니다. 컨테이너가 삭제되어도 데이터가 유지될 수 있도록 할 때 사용합니다. 💾
⚙️ `services` 블록: 핵심 요소 살펴보기
services
블록 안에서 각 컨테이너의 동작 방식을 상세하게 정의할 수 있습니다. 여기서는 자주 사용되는 몇 가지 중요한 속성들을 살펴보겠습니다.
1. image
vs. build
image: [이미지 이름]
: Docker Hub와 같은 컨테이너 레지스트리에서 이미지를 다운로드하여 사용합니다. 가장 일반적인 방법입니다.services: web: image: nginx:latest # Nginx 최신 이미지 사용
build: .
또는build: [경로]
: 로컬에 있는 Dockerfile을 사용하여 직접 이미지를 빌드합니다. 커스텀 이미지가 필요할 때 유용합니다.services: app: build: ./app # 현재 디렉토리의 app 폴더에 있는 Dockerfile로 빌드 context: . # Dockerfile이 있는 경로 dockerfile: Dockerfile # Dockerfile 파일명 (기본값)
2. ports
: 포트 매핑
호스트 머신과 컨테이너 간의 포트를 연결합니다. 외부에서 컨테이너 서비스에 접근할 수 있게 합니다.
services:
web:
image: nginx:latest
ports:
- "80:80" # 호스트 80번 포트 -> 컨테이너 80번 포트
- "443:443" # 호스트 443번 포트 -> 컨테이너 443번 포트
- "8080" # 호스트의 임의 포트 -> 컨테이너 8080번 포트 (비권장)
3. volumes
: 데이터 영속성 유지
컨테이너 내부의 특정 경로를 호스트 머신의 경로 또는 Docker 볼륨에 연결하여 데이터를 영구적으로 저장하고 컨테이너 간에 공유할 수 있게 합니다.
services:
db:
image: postgres:13
volumes:
- db_data:/var/lib/postgresql/data # named volume 사용
- ./app:/usr/src/app # 로컬 코드 마운트 (개발용)
volumes:
db_data: # named volume 정의
💡 팁: 개발 시에는 로컬 코드를 컨테이너에 마운트하여 코드 변경 시 컨테이너를 재빌드할 필요 없이 바로 반영되도록 설정하는 것이 매우 편리합니다! ✍️
4. environment
: 환경 변수 설정
컨테이너 내부에서 사용할 환경 변수를 정의합니다. 데이터베이스 연결 정보나 API 키 등 민감한 정보를 전달할 때 유용합니다. (.env 파일과 함께 사용하는 것이 일반적)
services:
app:
image: my_app:latest
environment:
- DATABASE_URL=postgres://user:password@db:5432/mydb
- NODE_ENV=development
5. depends_on
: 서비스 의존성
특정 서비스가 다른 서비스에 의존할 때 사용합니다. 예를 들어, 웹 애플리케이션이 데이터베이스보다 먼저 시작되어야 할 때 유용합니다. 하지만 이는 “시작 순서”만 보장할 뿐, 서비스가 “준비 완료” 상태임을 보장하지는 않습니다. (healthcheck
와 함께 사용 권장)
services:
app:
image: my_app:latest
depends_on:
- db # db 서비스가 먼저 시작된 후에 app 서비스가 시작됨
db:
image: postgres:13
6. networks
: 서비스 네트워킹
어떤 네트워크에 서비스를 연결할지 지정합니다. 서비스 간의 통신을 분리하거나 특정 서비스 그룹만 통신하도록 할 수 있습니다.
services:
web:
image: nginx
networks:
- frontend_net
api:
image: my_api
networks:
- frontend_net
- backend_net
db:
image: postgres
networks:
- backend_net
networks:
frontend_net:
backend_net:
🚀 실전 예제: 간단한 웹 애플리케이션 개발 환경 구축
이제 실제로 Nginx 웹 서버와 Node.js 백엔드, 그리고 MongoDB 데이터베이스로 구성된 간단한 웹 애플리케이션 개발 환경을 Docker Compose로 구축하는 예제를 살펴보겠습니다.
# docker-compose.yml 파일
version: '3.8'
services:
nginx:
image: nginx:latest
container_name: web_nginx
ports:
- "80:80" # 호스트 80포트 -> 컨테이너 80포트
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro # Nginx 설정 파일 마운트
- ./nginx/html:/usr/share/nginx/html:ro # 정적 파일
depends_on:
- backend # backend 서비스가 먼저 시작되도록 의존성 설정
networks:
- my_app_network
backend:
build: ./backend # backend 폴더의 Dockerfile로 빌드
container_name: node_backend
ports:
- "3000:3000" # 호스트 3000포트 -> 컨테이너 3000포트 (API 접근)
volumes:
- ./backend:/usr/src/app # 로컬 backend 코드 마운트 (개발용)
- /usr/src/app/node_modules # node_modules는 호스트에서 제외
environment:
NODE_ENV: development
MONGO_URI: mongodb://mongo:27017/my_database # MongoDB 연결 문자열 (서비스 이름으로 접근)
depends_on:
- mongo # mongo 서비스가 먼저 시작되도록 의존성 설정
networks:
- my_app_network
mongo:
image: mongo:4.4 # MongoDB 4.4 버전 이미지 사용
container_name: mongodb_instance
ports:
- "27017:27017" # 호스트 27017포트 -> 컨테이너 27017포트 (선택 사항, 내부 통신만 필요하면 생략 가능)
volumes:
- mongo_data:/data/db # 영구 데이터 저장을 위한 볼륨 마운트
networks:
- my_app_network
networks:
my_app_network: # 모든 서비스가 사용할 네트워크 정의
driver: bridge
volumes:
mongo_data: # MongoDB 데이터를 위한 이름 있는 볼륨 정의
디렉토리 구조 예시:
.
├── docker-compose.yml
├── nginx
│ ├── nginx.conf
│ └── html
│ └── index.html
└── backend
├── Dockerfile
├── package.json
├── server.js
└── ...
간단한 `backend/Dockerfile` 예시:
FROM node:14
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD [ "npm", "start" ]
실행 방법
- 위
docker-compose.yml
파일을 프로젝트 루트 디렉토리에 저장합니다. nginx
,backend
,mongo
폴더와 필요한 파일들을 위 예시와 같이 구성합니다. (간단한 Node.js 서버 파일과 Nginx 설정 파일 필요)- 터미널을 열고
docker-compose.yml
파일이 있는 디렉토리로 이동합니다. - 다음 명령어를 실행합니다:
docker compose up -d
-d
옵션은 백그라운드에서 컨테이너를 실행하라는 의미입니다. (docker-compose
명령어가 아닌docker compose
로 변경됨) - 컨테이너가 성공적으로 실행되면, 웹 브라우저에서
http://localhost
에 접속하여 Nginx가 잘 작동하는지 확인할 수 있습니다. Node.js 백엔드는 Nginx를 통해 프록시되거나, 직접http://localhost:3000
으로 접근하여 테스트할 수 있습니다.
✨ 더 나아가기: 고급 설정과 유용한 팁
1. .env
파일 활용: 민감 정보 관리
환경 변수에 직접 민감한 정보를 넣는 대신, .env
파일을 사용하여 분리하고 Git에는 포함시키지 않는 것이 좋습니다. Docker Compose는 docker-compose.yml
파일과 같은 디렉토리에 있는 .env
파일을 자동으로 로드합니다.
# .env 파일 예시
DB_USER=myuser
DB_PASSWORD=mypassword
API_KEY=your_secret_api_key
# docker-compose.yml 파일 (환경 변수 사용)
services:
backend:
environment:
- DB_USER=${DB_USER} # .env 파일의 DB_USER 값을 가져옴
- DB_PASSWORD=${DB_PASSWORD}
2. 여러 개의 Compose 파일 사용
개발, 테스트, 운영 환경에 따라 다른 설정을 사용하고 싶을 때 유용합니다. 예를 들어, 개발 시에는 볼륨 마운트를 사용하고, 운영 시에는 이미지 빌드 후 실행하는 식으로요.
docker-compose.yml
(기본 설정)docker-compose.dev.yml
(개발용 오버라이드)docker-compose.prod.yml
(운영용 오버라이드)
실행 시에는 -f
옵션으로 여러 파일을 지정합니다. 나중에 지정된 파일이 이전 파일의 설정을 덮어씁니다.
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
3. healthcheck
: 서비스 준비 상태 확인
depends_on
은 서비스 시작 순서만 보장할 뿐, 서비스가 실제로 “준비 완료” 상태인지 보장하지 않습니다. healthcheck
를 사용하면 서비스가 정상적으로 동작하는지 주기적으로 확인하여, 다른 서비스가 의존하는 서비스가 완전히 준비된 후에 시작되도록 할 수 있습니다.
services:
db:
image: postgres:13
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"] # postgres DB 준비 상태 확인
interval: 5s # 5초마다 체크
timeout: 5s # 5초 안에 응답 없으면 실패
retries: 5 # 5번 실패하면 비정상으로 간주
app:
image: my_app
depends_on:
db:
condition: service_healthy # db 서비스가 healthcheck를 통과해야 시작
4. 스케일링: 여러 개의 인스턴스 실행
동일한 서비스를 여러 개의 인스턴스로 실행하고 싶을 때 사용합니다.
docker compose up --scale backend=3 -d
이 명령어는 backend
서비스를 3개의 컨테이너로 실행합니다. 로드 밸런싱이 필요한 경우 Nginx나 다른 프록시 서비스를 앞에 두어야 합니다.
5. 유용한 명령어들
docker compose up
: 컨테이너 빌드 및 시작docker compose up -d
: 백그라운드에서 컨테이너 시작docker compose down
: 컨테이너 중지 및 제거 (볼륨은 제거 안 함)docker compose down --volumes
: 컨테이너 중지 및 제거 (볼륨까지 제거)docker compose ps
: 실행 중인 서비스 상태 확인docker compose logs [서비스 이름]
: 특정 서비스의 로그 확인docker compose exec [서비스 이름] [명령어]
: 실행 중인 컨테이너 내부에서 명령어 실행
마무리하며: 개발 환경, 이제는 즐겁게!
Docker Compose YML 파일은 복잡한 개발 환경을 명확하고 효율적으로 관리할 수 있게 해주는 강력한 도구입니다. 이제 더 이상 환경 설정 때문에 시간을 낭비하거나, “내 컴퓨터에서만 되는” 문제로 스트레스받을 필요가 없습니다. 😌
오늘 배운 내용을 바탕으로 여러분의 프로젝트에 Docker Compose를 적용해 보세요. 처음에는 다소 생소하게 느껴질 수 있지만, 몇 번 사용하다 보면 그 편리함에 푹 빠지게 될 것입니다. ✨ 궁금한 점이 있다면 언제든지 검색하거나 커뮤니티에 질문하여 해결해 나가세요!
이 글이 여러분의 개발 생산성을 한 단계 업그레이드하는 데 도움이 되었기를 바랍니다. 🚀 Happy Coding!