이전 포스트에서 도커 이미지를 빌드하고 실행(컨테이너화)하는 방법에 대해 알아보았다. 이번에 설명할 도커 기능은 도커 컴포즈이다. 이전과 마찬가지로 도커 컴포즈에 대해 공부하기 전에, 먼저 왜 필요한지를 이해해보자.
1. Docker Compose 필요성
Docker compose의 필요성을 설명하기 위해서는 이전 포스팅에 언급했던 "VM 활용 가상화와 컨테이너 활용 가상화의 차이"를 다시 되돌아볼 필요가 있다.
VM은 각각의 하이퍼바이저 (OS빌드를 위한 기초)위에 각각의 OS를 올리고, 그 위에 앱을 실행하고 컨테이너는 동일한 호스트 머신의 OS를 공유하면서 여러개의 컨테이너를 각각의 목적에 맞춰 독립적으로 실행할 수 있다는 것이었다.
이때 이 장점을 활용하는 방법이 docker compose이다. 하나의 명령어 파일로 여러개의, 여러 종류의 컨테이너를 한번에 빌드하고 종료하는 기능이라고 생각하면 편하다. 실제로 그게 전부이기도 하고.
근데 이렇게만 설명하면 그냥 docker run 명령어 여러개를 하나의 sh 쉘로 만들어서 관리하는거랑 뭔차이가 있는가 의문이 들 수 있다. 예를 들면
#!/bin/bash
docker run -d image 1
docker run -d image 2
이런식으로 두개의 이미지를 순차적으로 실행하도록 만드는 것과 큰차이가 없다고 생각들 수 있다.
하지만 두개의 이미지간의 선행 관계, 의존성이 존재하는 경우 docker compose의 필요성이 극대화된다. 필자는 이전 스마트워치앱 개발 프로젝트에서 Django 웹서버를 Nginx 이미지와 같이 올리는 과정에서 docker compose의 필요성을 느낄 수 있었다.
2. Docker Compose 파일 작성법
자, 이제 도커 컴포즈가 무엇인지와 왜 필요한지 알았으니, 실제로 어떻게 사용하는지 알아보자.
도커 컴포즈는 docker-compose.yml이라는 YAML 형식의 파일을 통해 다중 컨테이너 애플리케이션을 정의하고 실행하는 도구다. 이 파일에는 애플리케이션을 구성하는 서비스, 네트워크, 볼륨 등을 선언적으로 정의한다.
기본적인 docker-compose.yml 파일의 구조는 다음과 같다
version:"3"# 도커 컴포즈 파일 버전services:# 실행할 컨테이너들 정의service1:# 첫 번째 서비스 (컨테이너) 이름image:image_name:tag# 사용할 이미지# 기타 설정...service2:# 두 번째 서비스 (컨테이너) 이름build:./path/to/dockerfile# Dockerfile로부터 빌드# 기타 설정...volumes:# 필요한 볼륨 정의 (선택사항)volume1:# 볼륨 설정...networks:# 필요한 네트워크 정의 (선택사항)network1:# 네트워크 설정...
2.1 실제 예시: ItsMe 프로젝트
이 개념을 좀 더 명확하게 이해하기 위해 필자가 진행했던 스마트워치 앱 프로젝트(ItsMe)에서 사용한 실제 예시를 살펴보자:
여기서 주목할 부분은 depends_on 항목이다. 이는 서비스 간의 의존성을 정의하는 것으로, nginx 서비스가 web 서비스에 의존한다는 것을 명시한다. 이렇게 하면 도커 컴포즈는 다음 순서로 컨테이너를 시작한다:
먼저 웹 애플리케이션(web)이 시작
그 다음 Nginx(nginx)가 시작하여 웹 애플리케이션에 요청을 프록시
이것이 바로 앞서 말한 셸 스크립트로 여러 docker run 명령어를 실행하는 것과의 차이점이다. 셸 스크립트로는 이런 의존성 관계를 쉽게 표현하기 어렵다. 각 컨테이너가 제대로 시작되었는지 확인하는 로직을 직접 작성해야 할 것이다. 그것도 비동기 작업이라 생각보다 구현하기 까다롭다...
2.2 주요 설정 옵션
docker-compose.yml 파일에서 사용할 수 있는 주요 설정 옵션들을 살펴보자:
image: 사용할 도커 이미지 지정
build: 이미지를 직접 빌드할 경우 Dockerfile의 위치 지정
ports: 호스트와 컨테이너 간의 포트 매핑 (HOST:CONTAINER)
expose: 다른 컨테이너에는 노출하지만 호스트에는 노출하지 않을 포트
volumes: 파일 시스템 마운트 지정
environment: 환경 변수 설정
depends_on: 서비스 간 의존성 설정
restart: 컨테이너 재시작 정책 (no, always, on-failure, unless-stopped)
networks: 컨테이너가 연결될 네트워크 지정
이 외에도 condition 등의 옵션들이 있지만, 이건 나중에 airflow docker compose 배포 포스팅에서 설명할 예정이다ㅎㅎ
(어차피 다 외울수도 없음)
3. Docker Compose 관련 명령어
이제 Docker Compose 파일을 작성했으니, 이를 실행하고 관리하는 명령어들을 알아보자.
3.1 기본 명령어
컨테이너 시작
docker-compose up
이 명령어는 docker-compose.yml 파일에 정의된 모든 서비스를 시작한다. 터미널에 모든 컨테이너의 로그가 출력된다.
백그라운드에서 실행하려면 -d 옵션을 사용한다:
docker-compose up -d
컨테이너 중지
docker-compose down
이 명령어는 모든 서비스를 중지하고 컨테이너를 제거한다. 네트워크도 기본적으로 제거되지만, 볼륨은 유지된다.
볼륨까지 함께 제거하려면:
docker-compose down -v
컨테이너 상태 확인
docker-compose ps
이 명령어는 현재 실행 중인 컨테이너의 상태를 보여준다.
로그 확인
docker-compose logs
모든 서비스의 로그를 확인한다. 특정 서비스의 로그만 보려면:
docker-compose logs service_name
실시간 로그를 보려면 -f 옵션을 추가한다:
docker-compose logs -f
3.2 고급 명령어
특정 서비스만 실행
docker-compose up service_name
docker-compose.yml에 정의된 여러 서비스 중 특정 서비스만 실행한다. 이 때 depends_on에 정의된 의존성이 있는 서비스도 함께 시작된다.
서비스 재시작
docker-compose restart service_name
특정 서비스만 재시작한다.
이미지 재빌드
소스 코드가 변경되어 이미지를 다시 빌드해야 할 경우:
docker-compose up --build
또는 빌드만 하려면:
docker-compose build
컨테이너 내부 명령 실행
실행 중인 컨테이너에서 명령을 실행하려면:
docker-compose exec service_name command
예를 들어, 웹 서비스의 bash 셸에 접속하려면:
docker-compose exec web bash
3.3 스케일링
Docker Compose를 사용하면 서비스를 쉽게 스케일링할 수 있다:
docker-compose up -d --scale service_name=3
이 명령어는 지정한 서비스의 인스턴스를 3개 실행한다. 물론 이를 위해서는 포트 충돌을 피하도록 docker-compose.yml 파일을 적절히 구성해야 한다.
4. Docker Compose vs Docker Swarm
4.1 Docker Compose는 언제 사용해야 할까?
Docker Compose는 단일 호스트에서 다중 컨테이너 애플리케이션을 관리하는 데 최적화되어 있다. 따라서 다음과 같은 상황에서 특히 유용하다:
로컬 개발 환경 구성
자동화된 테스트 환경 구성
작은 규모의 서비스 배포
필자도 개인 프로젝트에서는 대부분 Docker Compose를 사용한다. 간단한 설정으로 빠르게 환경을 구성할 수 있기 때문이다. 실제로 위에서 언급한 ItsMe 프로젝트도 Docker Compose로 배포했다.
4.2 Docker Swarm
단일 호스트를 넘어 여러 서버에 분산 배포해야 한다면 Docker Swarm을 고려할 수 있다.
Docker Swarm은 Docker의 기본 제공 클러스터링 및 오케스트레이션 도구로, 여러 Docker 호스트를 하나의 가상 Docker 호스트로 묶는다. Compose와 비교했을 때 주요 차이점은 다음과 같다:
다중 호스트 지원: 여러 서버에 걸쳐 컨테이너를 분산 배포할 수 있다
고가용성: 서비스 복제를 통한 장애 대비
로드 밸런싱: 자동으로 트래픽을 분산
롤링 업데이트: 중단 없이 서비스 업데이트 가능
Docker Compose에서 Docker Swarm으로의 전환이 비교적 쉬운 편이다. Docker Compose 파일을 약간만 수정하면 Docker Swarm에서 사용할 수 있다:
(Docker Swarm은 이후에 Spark 클러스터 구현 및 연동에 대한 포스팅에서 설명할 예정이다!) (도커만 쓸 줄 알아도, 많은 기술 스택에 쉽게 접근할 수 있으니 화이팅해보자!)
5. 주의사항과 팁
5.1 환경변수 관리
민감한 정보(비밀번호, API 키 등)는 docker-compose.yml 파일에 직접 입력하지 않는 것이 좋다. 대신, .env 파일을 사용하거나 환경변수로 제공하는 것이 좋다: