1. 스케일업에 따른 컨테이너 운용 방식 변화
Docker로 이제 막 컨테이너들을 다뤘을 때는 모든 애플리케이션을 하나의 호스트(localhost
) 머신 안에서 서비스하였습니다. 그러나 단순 실습/테스트 수준을 넘어 Netflix나 여타 다른 웹 플랫폼 수준의 서비스로 나아가기 위해서는, 단일 호스트가 감당할 수 있는 시스템 자원 및 트래픽 양, 이용자 수를 훨씬 초월하는 수준의 거대한 시스템을 구상해야만 합니다. 당연히 컨테이너도 수십, 수백, 정말 많게는 수천 개가 필요해지겠죠.
여러 대의 PC 또는 서버 각각에 역할을 부여하고 컨테이너를 분산시켜 서비스를 효율적으로 운용해야할 때인 겁니다. 그러나 Docker 자체만으로 전체 시스템을 구축하려다보면 다양한 문제들을 마주칠 수 밖에 없습니다.
- 컨테이너를 상황에 따라 더 많이/적게 배치해야 할 때는 어떻게 하지?
- 컨테이너 하나가 다운되었을 때 어떤 조치가 이뤄줘야 할까?
- 서로 다른 호스트에 있는 컨테이너끼리 어떻게 통신하지?
- 다른 서버로 컨테이너 이전이 쉽게 가능할까?
- 서버마다의 리소스 현황을 따져서 효율적으로 컨테이너를 배치하려면 어떻게 해야하지?
- 각 컨테이너의 보안 파라미터(secret key, password 등)를 어떻게 안전하게 관리할까?
Docker의 가장 큰 장점이 유연성인데 반해, 시스템 스케일이 커지자마자 뭔가 복잡한 점이 이만저만이 아닙니다. 바로 위와 같은 이슈를 풀기 위한 다른 어떤 도구가 필요했습니다. 즉, 컨테이너 그룹 전반에 대한 생성·제어·모니터링의 자동화를 위해서 말이죠. 이를 용어로 개념화한 것이 “컨테이너 오케스트레이션(Container Orchestration)”입니다.
Container Orchestration의 목적
컨테이너 오케스트레이션이 제공하는 기능들을 아래와 같이 정리해볼 수 있습니다.
- 클러스터 관리: 컨테이너 간의 네트워킹, 노드 스케일 조정
- 상태 관리: 어떤 컨테이너에 문제가 생겼을 때의 대처
- 리소스 스케줄링: 어떤 서버가 앱을 띄울 여유 공간이 있는지 확인
- 배포 버전 관리: 각 컨테이너 이미지 버전을 사람이 하나하나 업데이트/롤백하는 것이 아니라 중앙 시스템에서 한번에 직접 관리
- 서비스 등록 및 조회: 서비스 구조가 변경되었을 때 자동으로 체크하고 프로세스 재시작
- 볼륨 스토리지 관리: 컨테이너끼리 공유할 스토리지 공간을 적절히 마운트
2. Container Orchestration 도구
컨테이너 오케스트레이션을 돕는 프레임워크는 Docker엔진에서 자체 개발한 ‘Swarm’, 그리고 십여년 전부터 범용적으로 활용되어온 Apache Mesos나 Openshift 등도 있지만, 사실상 가장 유명세가 높고 표준이나 다름없는 도구는 “Kubernetes”입니다.
제 포스트를 주기적으로 구독하시는 분들은 앞으로 Kubernetes에 대한 글들을 많이 접하시게 될 것 같습니다. Docker Swarm 같은 경우에는…
사실상 그 활용률이 Kubernetes를 역전하기는 커녕 바닥에서 반등하기도 어려울 정도로 전망이 좋지 않습니다. 무엇을 왜 더 선호하게 되었느냐를 딱 잘라 설명하기 어렵지만, 아래 표에 나타난 Docker Swarm과 Kubernetes의 기능/운영 측면에서의 특징 차이를 보시면 이유를 가늠해볼 수 있을 겁니다.
Docker Swarm은 Docker만 있으면 곧바로 이용이 가능하며, 기존 docker 명령어를 그대로 사용할 수 있어서 입문이 수월한 편입니다. 반면, Kubernetes는 시스템 구조를 이해하고 클러스터를 세팅하는 데에 시간이 좀 더 소요됩니다. 그럼에도 Kubernetes는 전반적으로 상당히 풍부한 도구와 강력한 자동화 기능이 탑재되어 있습니다. 그에 반해 Docker Swarm은 Docker API에 묶여 있어서 그런지 부실한 확장 기능과 자동화, 제한된 수준의 컨테이너 스케줄링·모니터링 등의 문제를 안고 있습니다.
앞서 점유율 그래프에서도 봤듯이 Docker Swarm을 단독이 아니더라도 실제 잘 쓰이게 될지는 모르겠습니다. 저 역시 Docker Swarm을 시간 내서 공부해야하나 싶었지만, 컨테이너 오케스트레이션 프레임워크의 구조와 Workflow를 이해하고 정리하는 데에 있어서는 도움이 될 것 같아 본 포스트를 작성하게 되었습니다.
3. Swarm의 구조
3-1. Manager Node vs Worker Node
Swarm은 클러스터링 도구답게 다수의 노드들이 그룹지어져 있습니다. 각각의 노드는 VM (Virtual Machine), 또는 실제 서버 (Physical Host) 위에 Linux나 Windows Server가 탑재된 형태일 수 있습니다. 노드 별 역할은 크게 “Manager” 그리고 “Worker”, 2가지로 구분합니다.
Manager Node
Swarm 안에서 이뤄지는 서비스 동작을 관리·감독하는 주체입니다. Manager Node는 서비스를 구성하는 미세한 작업(Task) 하나하나를 생성하고, 자신의 하위에 있는 Worker Node에게 할당합니다. Manger Node 간에는 현재 진행중인 Task를 어떤 Worker들이 담당하고 있는지, 그리고 각 Worker들의 상태 정보를 공유합니다.
Worker Node
Manager로부터 내려진 작업(Task)를 수행합니다. 각각의 Task를 위해 컨테이너를 배포한 뒤, 작업 현황을 지속적으로 Manager에게 보고하고 새로운 Task를 내려줄 것을 요청합니다.
3-2. Raft consensus group
앞서 전체 다이어그램에서 볼 수 있었다시피, Swarm에서 Manager Node는 둘 이상이 되기도 합니다. Worker Node야 본인에게 부여된 작업만 잘 수행하면 되겠지만, 전체 시스템을 관리하는 Manager Node가 일관성 없이 동작하게 두는 것은 바람직하지 않습니다.
Swarm은 여러 Manager Node 중에서도 가장 ‘Leader’격에 가까운 역할을 단 하나의 Node에게 부여합니다. 이 ‘Leader Node’는 분산된 Manager Node가 동일한 상태를 유지할 수 있게 하고, 이들 중 일부에 장애가 발생하더라도 전체 서비스에 지장이 가지 않도록 Node들을 통솔합니다.
Manager Node들은 ‘Raft consensus group’ 이라는 그룹을 꾸리게 되고, 그룹의 내부 로직에 의하여 Leader Node가 선출됩니다. Leader Node에 결함이 생겨도 같은 로직에 기반하여 새로운 Leader Node가 정상적인 Manager들 중에서 뽑히게 되죠.
Raft Database
Raft consensus group 내에는 ‘Raft Database’라는 별도의 저장소가 존재합니다. Raft Database는 전체 시스템의 configuration과 log를 저장하고 공유함으로써 분산된 Manager Node가 완전히 동기화를 이루도록 합니다.
Node 간의 보안 정책
Swarm을 처음 가동시키면 PKI(Public Key Infrastructure) 시스템이 활성화되어 Root certificate를 비롯한 여타 Security stuff들이 생성됩니다. 이들을 토대로 모든 Node간의 trust를 형성하고 Node로부터 발생하는 모든 트래픽은 암호화가 이뤄지게 됩니다.
그리고, 새로운 Manager 또는 Worker Node가 추가되는 상황을 고려하여 일종의 출입 허가증 역할을 하는 ‘token’을 발행합니다. 이 token은 Root certificate와 무작위로 생성된 secret 파라미터 정보를 가지고 있어서, Swarm 클러스터에 참여하고자 하는 Node들은 이 token을 기반으로 인증 절차를 수행합니다. 그러고 나서, Manager Node는 클러스터에 새로 들어온 Node마다 certificate를 발행하고 Swarm의 생명 주기 동안 내부 통신이 가능하도록 유효성을 부여합니다.
이러한 보안 시스템이 Swarm 자체에 내장되어 있음으로서 얻을 수 있는 이점은 상당합니다. 백엔드 시스템 관리자 입장에서는 흔히 config db
라고 하는 별도의 데이터베이스로 key/value 쌍이나 클러스터 orchestration/automation과 관련된 정보들을 직접 저장해둘 필요가 없어집니다. 또한 침해 사고가 발생하여 Manager Node나 CA(Certificate Authority)에 문제가 생기더라도 인증서와 Key를 재발급하는 프로세스가 존재합니다.
3-3. Swarm Service API
Docker run
명령어는 딱 하나의 컨테이너를 생성하는 데 그쳤습니다. 기본적으로 스케일 확장/축소에 대한 Concept을 담고 있지는 않죠. Swarm에서는 다수의 컨테이너로 구성된 컨테이너를 구축하기 위해 Docker service
라는 별도의 API가 있습니다.
Swarm에서 정의하는 기본 제어 단위인 “Service”는 Node들이 운용하게될 컨테이너 집합과, 이와 관련된 자원 정책 및 네트워크 구성 등을 포함합니다. 어떤 Worker가 어느 시점에 어떤 task를 수행해야할지를 나타낸 명세서인 셈이죠.
예를 들어, 위에 도식화된 Service 명세서는 “**nginx:latest
이미지를 총 3개의 Node에 각각 하나씩 컨테이너로 생성하라*”는 지침을 담고 있습니다. 위 도식에는 생략되었으나 일반적으로 *Service 정의에는 다음과 같은 요소들이 포함됩니다.
- 배포할 컨테이너 이미지
- Replica 수(= Task가 몇 개의 Node에게 분산 될 것인지)
- 시스템 자원(CPU, Memory) 할당량
- Swarm Cluster 외부와 통신할 때의 Port 정보
- 컨테이너가 생성된 이후의 초기 작업·명령어
Service 생성은 사용자가 command-line 인터페이스를 통해 docker service create
명령어를 입력함으로써 이뤄집니다.
Manager Node*는 Swarm API를 통해 *Service 명세를 분석하고 이를 수행할 Node를 선별하고 Service 현황을 관리하기 위해 다양한 백그라운드 서비스(ex., scheduler
, dispatcher
)를 활용합니다. 가령 어떤 IP의 호스트에게 특정 Task를 맡길지 결정한다던가(allocater
), Worker Node가 가져가야 할 일감을 배치해 둔다던가(dispatcher
) 말이죠.
Worker Node는 HTTP로 Manager Node와 통신하며 배정된 일감을 주기적으로 확인합니다. Worker Node가 맡겨진 일을 수행하기 시작하면 Manager Node의 scheduler
가 Task의 성공/실패 여부를 감지하는 식으로 Service의 생명 주기가 지속됩니다.
Service의 종류
Swarm에서 정의하는 Service는 크게 2가지로 구분됩니다.
global service
: Swarm Cluster에 존재하는 모든 Node가 참여하는 Service입니다. 심지어 Manager Node도 포함해서요. 가장 대표적인 예시는 안티 바이러스와 같은 취약점 진단 서비스가 있습니다. 보안은 모든 Node에게 다 중요하니까요.replicated service
: Worker Node*에게만 배정되는 Task들의 집합으로, 사용자가 원하는 *Worker Node 그룹에 원하는 수만큼 할당할 수 있습니다.