Scale Up(수직확장)
하나의 호스트 OS 에서 컨테이너가 많아져 CPU, 메뢰, 디스크 용량과 같은 자원이 부족해졌다고 해봅시다. 새로운 컨테이너를 추가하려는데, 자원 부족으로 추가하지 못하는 상황이면 이를 어떻게 해결할 수 있을까요?
하지만 자원의 확장성, 비용 측면에서 좋지않은 해답입니다. 자원이 부족해질때마다 자원을 더 사야하거나 기껏 자원을 사놨는대, 그만큼의 자원이 필요하지 않을수도 있기 때문이죠.
Scale Out (수평확장)
Scale In 방식보다 더 좋은 방법은 여러대의 서버를 클러스터로 만들어서, 자원을 병렬롤 확장하는 것입니다. 이를 Scale Out 이라고합니다. (클러스터는 뒤이어서 계속 설명할겁니다!)
예를들어 4GB 의 메모리가 탑제된 서버 1대에 도커엔진을 설치해 실제 운영 환경에서 사용한다고 해봅시다.
이 1대의 서버에 컨테이너가 너무 많이 생성되있어 더 이상 컨테이너를 사용할 수 없다고 판단되면, 새로운 서버를 추가해서 자원을 늘리는 방식입니다. 만약, 놀고있는 자원이 많다면 다시 줄이면 되는것이죠.
클러스터(Cluster)
클러스터란?
앞서 게속 클러스터란 용어가 등장했는데, 이것은 무엇일까요? 서버 클러스터란, 각기 다른 서버들을 하나로 묶어서 하나의 시스템같이 동작하게 함으로써, 클라이언트들에게 고가용성의 서비스를 제공하는 것을 말합니다.
동일한 네트워크 풀을 쓰면서, 요청에 따라 트래픽을 분산시킬 수 있는것이죠.
클러스터 도입의 어려움
하지만 여러대의 서버를 하나의 자원 풀로 만드는 것은 어렵습니다. 새로운 서버나 컨테이너가 추가됐을 때 이를 발견하는 작업부터, 어떤 서버에 어떤 컨테이너를 할당할 것인지에 대한 작업들 등 처리해야할 작업이 많습니다.
다행히 이러한 문제들을 해결하는 여러 솔루션을 오픈소스로 활용할 수 있습니다. 그 중 대표적인 것이 바로 도커스웜입니다.
도커 스웜(Docker Swarm)
도커스웜은 스웜모드를 지원합니다. 스웜모드는 마이크로 서비스 아키텍처의 컨테이너를 다루기위한 클러스터링 기능에 초점을 맞추고 있습니다.
같은 컨테이널을 동시에 여러개 생성해, 필요에 따라 유동적으로 컨테이너 수를 조절할 수 있습니다.
-
컨테이너로의 연결을 분산하는 로드밸런싱 기능을 자체적으로 지원 하고 있습니다.
-
또한 Scale Out 은 기본적으로 지원하지만, 자체적으로 인스턴스를 늘리거나 줄이거나는 할 수 없고 개발자가 직접 늘려줘야합니다.
스웜은 도커 엔진 자체에 내장되어 있습니다.
Swarm 모드의 구성
스웜모드는 매너지 모드와 워커 모드로 구성되어있습니다. 각 모드를 설명드리자면 다음과 같습니다.
- 워커 노드 : 실제로 컨테이너가 생성되고 관리되는 Docker 서버
- 매너저 노드 : 워커 노드를 관리하기 위한 Docker 서버
매너저 노드는 기본적으로 워커 노드의 역할을 포함하며, 운영환경에서 다중화 (여러개를 구축) 를 하는것이 좋습니다. 왜냐면 매너지 노드의 부하를 분산하고, 특정 워커 노드가 다운됐을 떄 정상적으로 스웜 클러스터를 유지할 수 있기 때문이죠.
클러스터 구축
그렇다면 클러스터 구축은 어떻게 이루어질까요? AWS EC2 인스턴스의 예를 들어보죠. 4개의 인스턴스가 준비됐다고 가정해봅시다.
우선 매니저 노드의 역할을 한 인스턴스, 노드의 역할을 할 인스턴스를 지정합니다. 저는 인스턴스 1을 매너저 노드로 지정하고, 2,3,4를 워커 노드로 지정하겠습니다. 노드를 지정했다면, 역할을 할당해줘야겠죠?
도커 명령어인 docker swarm init 을 통해 매니저 노드를 설정합니다.
이제 워커 노드를 추가할 때입니다. docker swarm join 을 통해 노드를 추가할 수 있습니다.
이렇게 docker swarm join 으로 워커 노드들을 추가하면서, 하나의 클러스터 단위를 완성시키게 됩니다. 이때 몰론 매너저 노드 딱 1개만으로도 클러스터라고도 표현할 수는 있긴합니다.
이로써 저희는 이제 서비스를 시작할 수 있게 되었습니다. (서비스란 무엇인지에 대해 바로 이어서 설명하겠습니다.)
서비스란
일반적인 도커 명령어의 제어 단위는 컨테이너입니다. 이는 도커 클라이언트(CLI) 가 제어하는 컨테이너라는 말입니다.
서비스
도커 스웜에서 제어단위는 서비스입니다. 서비스는 같은 이미지에서 생성되는 컨테이너의 집합을 의미합니다. 기본적인 배포 단위로써, 기동할 이미지, 컨테이너 수, 설정등을 정의한 것이죠. 아래와 같이 서비스를 제어하면 해당 서비스내의 컨테이너에 같은 명령이 수행됩니다.
Task
이때 서비스내의 동일한 이미지로부터 생성된 컨테이너 각각을 Task(테스크) 라 부릅니다. 서비스의 정의에따라 테스크를 할당할 적합한 노드를 선정하고, 선택된 노드에 테스크를 분배하는 방식인것이죠.
- 즉 하나의 서비스는 여러개의 테스크를 실행할 수 있는것입니다.
레플리카(Replica)
또, 생성된 테스크를 한묶음으로 묶어서 레플리카(Replica) 라고하고, 개발자가 서비스에 설정한 레플리카의 수만큼 테스크가 스웜 클러스터 내에 존재해야합니다.
- --replicas 옵션을 사용하여 동일 이미지를 가동할 컨테이너 수를 지정할 수 있습니다.
예제
예제를 더 확인하며 이해해봅시다. 저는 아래와 같이 스프링부트, Nginx 2개의 서비스를 정의했습니다. 앞서 말씀드렸듯이 "서비스"란 동일한 이미지로부터 생성되는 컨테이너의 집합이라고 했었죠? 또 테스크(Task) 란 동일한 이미지(스프링부트 이미지, Nginx 이미지 등등)로 부터 생성되는 컨테이너들을 의미했습니다.
스프링부트는 모든 노드에 생성될 테스크의 수를 3개로 설정했고, Nginx 는 2개로 설정한 것입니다. 클러스터안에 스프링부트 컨테이너가 3개, Nginx 컨테이너는 2개 할당된 것을 볼 수 있죠.
Swarm 의 서비스와 테스크의 필요성
그렇다면 이러한 도커 스웜에서 서비스, 테스크라는 것은 왜 필요한걸까요? 이들이 있어야지 여러 대의 노드에서 같은 역할을 하는 컨테이너를 실행하여 로드밸런싱 하거나, 장애 상황에 대비한 이중화 구성을 할 수 있는것이죠.
Swarm 모드에서 제공하는 기능
이러한 스웜모드가 제공하는 기능들은 정말 다양합니다. 여러 기능들중 메인 기능만을 나열해보면 다음과 같습니다.
- 클러스터 관리
- 서비스 관리
- 네트워크 관리
- 노드 관리
이 기능들은 도커 엔진에서 제공하는 기능들과 많이 비슷하죠? 이 또한 내용이 넓어서 자세한 내용은 스킵하고 넘어가겠습니다.
특정 노드에 장애가 발생한경우
만약 스웜모드에서 노드에 장애가 발생하면 어떻게될까요? 스웜은 서비스의 테스크들에 대한 상테를 계속 확인하다가 서비스내에다 정의한 레플리카 수만큼 스웜 클러스터 내에 존재하지 않으면, 새로운 테스크 레플리카를 생성합니다.
예를들어 앞선 예제에서 첫번째 워커노드의 서버가 다운되었다고 해봅시다.
이렇게 노드 하나가 다운되면, 정상적으로 돌아가고있는 또 다른 노드에다 테스크를 생성하는 것입니다. 몰론, 이때 매너저모드가 다운돼버리면 클러스터가 사라져버립니다.
Swarm 모드의 필요성
스웜모드를 왜 쓰는것이 좋을까요? 그것은 바로 "서비스 확장/관리를 편하게" 하기위함입니다. 이에 관하여, 컨테이너를 어떻게 효율적으로 생성할 수 있을지에 대해 알아보겠습니다.
각각의 서비스(스프링부트, Nginx 서비스) 가 동작하기 위해선 컨테이너가 필요하겠죠. 서비스에 필요한 컨테이너가 1~2개 정도라면, 도커 명령어를 직접 입력해 생성할 수 있지만 여러개라면 말이 달라집니다. 명령어를 일일이 다 쳐서 컨테이너(테스크) 여러개를 매번 생성하자니 불편해지죠.
만일 아래처럼 하나의 프로젝트 단위를 클러스터 내에 묵었고, 프로젝트가 구동되기 위해서는 스프링부트, Nginx 컨테이너가 필요합니다. 이 컨테이너들을 기반으로 테스크가 생성되기 때문이죠.
Docker Compose
앞선 설명처럼 매번 명령어를 쳐서 테스크(Task) 를 여러개 생성하는 작업은 불편합니다. 이는 도커 컴포즈(Compose) 를 통해서 해결이 가능합니다.
컴포즈는 여러개의 컨테이너를 하나의 서비스로 정의하고 실행합니다. 스웜 모드의 서비스와 비슷하게 설정파일에 정의된 서비스의 컨테이너 수를 유동적으로 조절할 수 있습니다.
- 도커 컴포즈는 도커 엔진밖에 위치해있습니다. 따라서 도커에 내장되있지 않으므로 별도로 컴포즈를 직접 설치해줘야합니다.
도커 컴포즈 파일 작성법
1.도커 컴포즈는 컨테이너의 설정이 정의된 yml 파일(docker-compose.yml)을 읽어 도커엔진을 통해 컨테이너를 생성합니다. 따라서 도커 컴포즈를 사용하려면 yml 파일을 먼저 작성해야하죠.
2.파일을 작성후 docker --compose 명령어를 통해 도커 컨테이너를 생성할 수 있습니다.
도커 컴포즈 내부구조
도커 컴포즈는 기본적으로 docker --compose.yml 에 위치한 디렉토리 이름을 프로젝트 이름으로 사용합니다. 즉 yml 이 저장된 디렉토리의 이름에 따라 프로젝트의 이름이 달라집니다. (몰론 직접 설정도 가능합니다)
하나의 프로젝트는 여러개의 서비스로 이루어지고, 각 서비스에는 여러개의 컨테이너가 존재할 수 있습니다.
마치며
이렇게 도커의 클러스터부터 시작해 도커 스웜모드, 그리고 컴포즈까지 컨테이너 확자엥 대한 상세한 내용을 상세하게 다루었습니다. 이번 포스팅과 관련한 내용중 가장 핫한 기법이 바로 쿠버네티스인데, 이번 내용을 이해하셨다면 쿠버네티스 또한 쉽게 적용할 수 있을겁니다. 쿠버네티스를 비롯해 도커를 학습하시는 분들에게 이번 포스팅이 도움이 되었으면 합니다 😉
참고
Docker Docs 초보를 위한 도커 안내서 - 도커란 무엇인가? SERIES 1/3 Docker 가상 환경 구축 입문 (15) - Swarm의 서비스와 태스크 가상화 기술의 유형 도커 컴포즈 및 기본문법