멀티 컨테이너 애플리케이션
멀티 컨테이너 애플리케이션
하나의 컨테이너로 애플리케이션을 띄우는 건 쉽지만, 여러 개의 컨테이너로 이루어진 애플리케이션을 docker run 명령만으로 관리하는 건 점점 어려워져요. 이 가이드에서는 docker run으로 여러 컨테이너를 띄우는 방법과, Docker Compose로 전체 배포를 훨씬 간단하게 만드는 방법을 배워요.
출처: 문서
본문
설명
단일 컨테이너 애플리케이션을 시작하는 건 쉬워요. 예를 들어 특정 데이터 처리 작업을 수행하는 Python 스크립트는 모든 의존성과 함께 컨테이너 안에서 실행돼요. 마찬가지로 작은 API 엔드포인트가 있는 정적 웹사이트를 제공하는 Node.js 애플리케이션도 필요한 라이브러리와 의존성과 함께 효과적으로 컨테이너화할 수 있어요. 하지만 애플리케이션이 커질수록 각각을 개별 컨테이너로 관리하는 건 점점 어려워져요.
데이터 처리 Python 스크립트가 데이터베이스에 연결해야 한다고 상상해 보세요. 갑자기 스크립트뿐 아니라 데이터베이스 서버도 같은 컨테이너 안에서 관리해야 하게 돼요. 스크립트에 사용자 로그인이 필요하다면 인증 메커니즘도 필요해져 컨테이너 크기가 더 커져요.
컨테이너의 모범 사례 중 하나는 "각 컨테이너는 한 가지 일을 하고, 그것을 잘 해야 한다"는 거예요. 이 규칙에는 예외가 있지만, 한 컨테이너가 여러 일을 하게 만드는 경향은 피하는 게 좋아요.
이쯤에서 "이 컨테이너들을 따로 실행해야 하나요? 따로 실행한다면 어떻게 서로 연결하죠?"라고 물을 수 있어요.
docker run은 컨테이너를 실행하기에 편리한 도구지만, 커져가는 애플리케이션 스택을 이걸로 관리하는 건 어려워져요. 이유는 이래요.
- 개발, 테스트, 프로덕션 환경에 서로 다른 구성을 가진 여러 개의
docker run명령(프론트엔드, 백엔드, 데이터베이스)을 실행한다고 상상해 보세요. 오류가 나기 쉽고 시간도 오래 걸려요. - 애플리케이션은 종종 서로 의존해요. 특정 순서로 컨테이너를 수동으로 시작하고 네트워크 연결을 관리하는 건 스택이 커질수록 어려워져요.
- 각 애플리케이션마다 자체
docker run명령이 필요해, 개별 서비스를 확장하기 어려워져요. 전체 애플리케이션을 확장하면 확장이 필요 없는 구성 요소에 리소스를 낭비할 수 있어요. - 각 애플리케이션의 데이터를 영구화하려면 각
docker run명령 안에 별도의 볼륨 마운트나 구성을 넣어야 해요. 이는 데이터 관리 방식을 분산시키게 만들어요. - 각 애플리케이션의 환경 변수를 별도의
docker run명령으로 설정하는 건 번거롭고 오류가 나기 쉬워요.
바로 여기서 Docker Compose가 구세주로 등장해요.
Docker Compose는 compose.yml이라는 단일 YAML 파일에 멀티 컨테이너 애플리케이션 전체를 정의해요. 이 파일은 모든 컨테이너의 구성, 의존성, 환경 변수, 심지어 볼륨과 네트워크까지 지정해요. Docker Compose를 사용하면:
- 여러 개의
docker run명령을 실행할 필요가 없어요. 단일 YAML 파일에 멀티 컨테이너 애플리케이션 전체를 정의하면 돼요. 이렇게 하면 구성을 중앙화하고 관리를 단순화해요. - 특정 순서로 컨테이너를 실행하고 네트워크 연결을 쉽게 관리할 수 있어요.
- 멀티 컨테이너 설정 안에서 개별 서비스를 쉽게 확장하거나 축소할 수 있어요. 실시간 필요에 기반한 효율적인 리소스 할당이 가능해요.
- 영구 볼륨을 쉽게 구현할 수 있어요.
- Docker Compose 파일에 환경 변수를 한 번만 설정하면 돼요.
멀티 컨테이너 설정에 Docker Compose를 활용하면 모듈성, 확장성, 일관성을 핵심으로 한 복잡한 애플리케이션을 구축할 수 있어요.
직접 해보기
이 실습 가이드에서는 먼저 docker run 명령으로 Node.js 기반 카운터 웹 애플리케이션, Nginx 리버스 프록시, Redis 데이터베이스를 빌드하고 실행하는 방법을 보여 드릴게요. 그다음 Docker Compose로 전체 배포 과정을 얼마나 간단하게 만들 수 있는지도 보여 드릴게요.
준비
- 샘플 애플리케이션을 받아요. Git이 있다면 샘플 애플리케이션 저장소를 클론할 수 있고, 아니면 샘플 애플리케이션을 다운로드할 수 있어요. 다음 옵션 중 하나를 선택하세요.
터미널에서 다음 명령을 사용해 샘플 애플리케이션 저장소를 클론해요.
$ git clone https://github.com/dockersamples/nginx-node-redisnginx-node-redis디렉터리로 이동해요.
이 디렉터리 안에는$ cd nginx-node-redisnginx와web두 개의 하위 디렉터리가 있어요. 소스를 다운로드해서 압축을 풀어요.nginx-node-redis-main디렉터리로 이동해요.
이 디렉터리 안에는$ cd nginx-node-redis-mainnginx와web두 개의 하위 디렉터리가 있어요. - Docker Desktop을 다운로드하고 설치해요.
이미지 빌드
nginx디렉터리로 이동해 다음 명령을 실행해 이미지를 빌드해요.$ docker build -t nginx .web디렉터리로 이동해 다음 명령을 실행해 첫 번째 웹 이미지를 빌드해요.$ docker build -t web .
컨테이너 실행
- 멀티 컨테이너 애플리케이션을 실행하려면 먼저 모두가 통신할 네트워크를 만들어야 해요.
docker network create명령으로 만들 수 있어요.$ docker network create sample-app - 다음 명령을 실행해 Redis 컨테이너를 시작해요. 이 명령은 앞서 만든 네트워크에 연결하고 네트워크 별칭(DNS 조회에 유용)을 만들어요.
$ docker run -d --name redis --network sample-app --network-alias redis redis - 다음 명령을 실행해 첫 번째 웹 컨테이너를 시작해요.
$ docker run -d --name web1 -h web1 --network sample-app --network-alias web1 web - 다음 명령을 실행해 두 번째 웹 컨테이너를 시작해요.
$ docker run -d --name web2 -h web2 --network sample-app --network-alias web2 web - 다음 명령을 실행해 Nginx 컨테이너를 시작해요.
$ docker run -d --name nginx --network sample-app -p 80:80 nginxNote Nginx는 보통 웹 애플리케이션의 리버스 프록시로 사용되어 트래픽을 백엔드 서버로 라우팅해요. 이 경우 Node.js 백엔드 컨테이너(web1 또는 web2)로 라우팅해요.
- 다음 명령을 실행해 컨테이너가 떠 있는지 확인해요.
다음과 같은 출력이 보여요.$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2cf7c484c144 nginx "/docker-entrypoint.…" 9 seconds ago Up 8 seconds 0.0.0.0:80->80/tcp nginx 7a070c9ffeaa web "docker-entrypoint.s…" 19 seconds ago Up 18 seconds web2 6dc6d4e60aaf web "docker-entrypoint.s…" 34 seconds ago Up 33 seconds web1 008e0ecf4f36 redis "docker-entrypoint.s…" About a minute ago Up About a minute 6379/tcp redis - Docker Desktop 대시보드를 보면 컨테이너를 확인하고 구성 내부를 자세히 들여다볼 수 있어요.
- 모든 게 실행되고 나면 브라우저에서 http://localhost 를 열어 사이트를 볼 수 있어요. 페이지를 여러 번 새로고침해 요청을 처리하는 호스트와 총 요청 수를 확인해 보세요.
web2: Number of visits is: 9 web1: Number of visits is: 10 web2: Number of visits is: 11 web1: Number of visits is: 12Note 리버스 프록시 역할을 하는 Nginx가 들어오는 요청을 라운드 로빈 방식으로 두 백엔드 컨테이너에 분배한다는 걸 눈치챘을 거예요. 즉 각 요청이 교대로 다른 컨테이너(web1과 web2)로 보내질 수 있어요. 출력에서 web1과 web2 컨테이너 모두의 카운트가 연속적으로 증가하는 걸 볼 수 있고, 실제 카운터 값인 Redis에 저장된 값은 응답이 클라이언트로 돌아간 뒤에만 갱신돼요.
- Docker Desktop 대시보드에서 컨테이너를 선택하고 Delete 버튼을 눌러 컨테이너를 제거할 수 있어요.
Docker Compose로 배포 단순화
Docker Compose는 멀티 컨테이너 배포를 관리하는 구조적이고 간소화된 접근 방식을 제공해요. 앞서 말했듯이 Docker Compose를 사용하면 여러 개의 docker run 명령을 실행할 필요가 없어요. compose.yml이라는 단일 YAML 파일에 멀티 컨테이너 애플리케이션 전체를 정의하면 돼요. 어떻게 동작하는지 볼게요.
프로젝트 디렉터리의 루트로 이동해요. 이 디렉터리 안에 compose.yml이라는 파일이 있어요. 이 YAML 파일에서 모든 마법이 일어나요. 애플리케이션을 구성하는 모든 서비스와 그 구성을 정의해요. 각 서비스는 자신의 이미지, 포트, 볼륨, 네트워크, 그리고 기능에 필요한 기타 설정을 지정해요.
docker compose up명령으로 애플리케이션을 시작해요.
이 명령을 실행하면 다음과 비슷한 출력이 보여요.$ docker compose up -d --build✔ Network nginx-node-redis_default Created 0.0s ✔ Container nginx-node-redis-web2-1 Created 0.1s ✔ Container nginx-node-redis-web1-1 Created 0.1s ✔ Container nginx-node-redis-redis-1 Created 0.1s ✔ Container nginx-node-redis-nginx-1 Created- Docker Desktop 대시보드를 보면 컨테이너를 확인하고 구성 내부를 자세히 들여다볼 수 있어요.
- 또는 Docker Desktop 대시보드에서 애플리케이션 스택을 선택하고 Delete 버튼을 눌러 컨테이너를 제거할 수 있어요.
이 가이드에서 docker run이 오류가 나기 쉽고 관리하기 어려운 것에 비해, Docker Compose로 멀티 컨테이너 애플리케이션을 시작하고 중지하는 게 얼마나 쉬운지 배웠어요.