도커는 컨테이너 기술에 여러 기능들을 추가한 오픈소스 프로젝트로, 컨테이너 기반 가상화 도구이다. 도커와 컨테이너 기술에 대해 이해하기 위해서는 가상화 개념에 대한 이해가 필요하다. 가상화는 하나의 물리적 서버에서 여러 개의 서버 OS를 Guest로 실행할 수 있게 해주는 소프트웨어 아키텍처로, 물리적인 서버 자원을 효율적으로 사용하기 위한 목적에서 등장하게 된 기술이다. 이를 이용하면 하나의 물리 서버에서 서버 자원을 할당에 각기 다른 서비스를 제공할 수 있다. 가장 대표적인 가상화 기술에는 VM과 컨테이너가 있다.
가장 먼저 등장한 가상화 기술은 VirtualBox와 VMware와 같은 하이퍼바이저 기반의 VM이다. VM은 Host OS 위에 가상화를 시켜주는 소프트웨어인 하이퍼바이저가 실행되고 가상 머신이라는 단위로 구분된 환경별로 Guest OS가 설치가 되어 진행되는 가상화 방식이다. 하이퍼바이저는 논리적으로 구분되어 있는 가상 머신이라는 독립된 가상 환경을 만들고 Guest OS들에게 컴퓨터 자원을 나누어 주면서 조율을 하고 Guest OS들의 커널을 번역해서 하드웨어에 전달하면서 Host 시스템에서 가상 머신에 깔려있는 Guest OS를 구동하고 모니터링하는 역할을 한다.
이러한 하이퍼바이저 기반의 VM은 시스템 자원을 가상화하고 독립된 공간을 생성하는 작업이 항상 하이퍼바이저를 거치기 때문에 일반 Host에 비해 성능 손실이 발생한다. 또한 VM 가상화를 하기 위해서 무거운 OS를 띄워야 한다는 근본적인 문제가 있다. 이로 인해 가벼운 서비스를 하나 구동시키기 위해 이보다 더 큰 OS를 구동시켜야 하는 경우가 발생한다. 이러한 VM의 비효율적인 단점을 극복하기 위해 나온 가상화 기술이 컨테이너 기술이다.
컨테이너는 VM과는 달리 Host OS 위에서 별도의 하이퍼바이저가 실행되지 않고 각 컨테이너에도 Guest OS 없이 애플리케이션을 실행하는 데에 필요한 라이브러리와 실행 파일만 존재하기 때문에 VM에 비해 가볍고 효율적이다. 이를 테면, 가볍기 때문에 이미지를 만들어 배포하는 시간이 VM에 비해 빠르며, 하이퍼바이저를 거치지 않기 때문에 가상화된 공간을 사용했을 때 성능 손실도 거의 없다.
도커 컨테이너는 가상화된 공간을 생성하기 위해 하이퍼바이저 대신 리눅스 자체 기능인 chroot, namespace, cgroup을 사용함으로써 프로세스 단위의 격리 환경을 제공한다.
이를 통해 컨테이너는 Host와의 격리를 통해 독립된 개발 환경을 보장받으며 프로세스를 격리된 환경에서 관리할 수 있다. 그에 따라 컨테이너를 이용할 경우 개발 환경 걱정없이 배포가 가능하게 된다. 도커 파일을 이용해 실행 파일과 라이브러리를 도커 이미지로 빌드하고 이를 실행시키면 컨테이너 형태로 배포가 가능하다. 이렇게 컨테이너를 관리하고 실행해주는 것이 도커 엔진이다.
도커 엔진은 컨테이너를 관리 및 실행해주는 주체로 유저가 컨테이너를 쉽게 사용할 수 있도록 해준다. 도커 엔진의 기능은 다음과 같다.
도커 아키텍처는 다음과 같다.
도커가 실행되는 흐름을 살펴보면 다음과 같이 진행된다.
1. 도커 명령어로 도커 엔진에 명령
2. 도커 CLI 클라이언트가 전달받고 유닉스 소켓을 통해 도커 데몬의 API를 호출
3. 도커 데몬은 명령어에 해당하는 작업을 수행
4. 수행 결과를 도커 클라이언트에게 반환하며 사용자에게 결과를 출력
지금까지는 단일 서버에서의 컨테이너 기술 기반의 가상화를 살펴봤다. 여러 개의 서버들이 연결되어 있을 때의 컨테이너 관리는 도커 엔진의 도커 스웜을 통해 가능하다.
도커 스웜은 도커에서 제공하는 컨테이너 오케스트레이션 툴이다. 컨테이너 오케스트레이션이란 여러 Host의 컨테이너 배포, 확장 및 관리 작업을 자동화하는 것을 의미한다. 도커 스웜은 클러스터 환경에서 서비스의 확장 및 관리를 편하게 해주는 기능이며, 추가적인 서버나 컨테이너에 대한 동적인 관리, 컨테이너 스케줄링, 로드밸런싱, 고가용성 보장 등을 지원한다.
스웜 모드는 도커 엔진에 내장되어 있다.
스웜 모드의 기능은 크게 다음과 같다.
스웜 모드의 구조는 다음과 같다.
스웜 모드는 매니저 노드와 워커 노드로 구성되어 있다. 워커 노드는 실제로 컨테이너가 생성되고 관리되는 도커 서버이다. 매니저 노드는 워커 노드를 관리하기 위한 도커 서버이고 기본적으로 워커 노드의 역할을 포함한다. 위 그림과 같이 실제 운영 환경에서는 매니저 노드를 다중화하는 것이 좋다. 매니저 노드의 부하를 분산할 수 있고, 특정 매니저 노드가 다운됐을 때 정상적으로 도커 스웜 클러스터를 유지할 수 있기 때문이다.
일반적인 도커 명령어의 제어 단위가 컨테이너라면, 도커 스웜에서 제어하는 단위는 동일한 이미지에서 생성된 컨테이너의 집합인 서비스이다.
이러한 서비스가 여러 개 있다면 도커 명령어만으로는 이들을 관리하기 어려운데, 이 때 도움을 주는 것이 도커 컴포즈이다.
도커 컴포즈는 여러 개의 컨테이너를 하나의 서비스로 묶어서 관리할 수 있도록 해주는 툴이다. 도커 컴포즈는 컨테이너 설정이 정의된 yaml 파일을 읽어 도커 엔진을 통해 컨테이너를 생성한다. 이를 통해 번거롭고 반복적인 도커 명렁어 대신에 설정 파일을 이용하여 여러 컨테이너의 실행을 쉽게 관리할 수 있다.
도커 스웜과는 달리 도커 컴포즈는 도커 엔진에 내장되어 있지 않아, 직접 설치해서 사용해야 한다.
쿠버네티스는 앞에서 살펴봤던 도커, 도커 스웜, 도커 컴포즈의 기능들을 아우르며 더욱 다양하고 발전된 형태의 기능들을 제공하는 컨테이너 오케스트레이션 도구이다. 쿠버네티스는 사실상 컨테이너 오케스트레이션 기술 표준이며, 다수의 호스트로 구성된 클러스터 환경에서 컨테이너화된 애플리케이션의 대규모 배포, 스케일링 및 관리를 선언적 프로그래밍을 통해 효율적이고 확장성있게 할 수 있다.
컨테이너 오케스트레이션 기술은 대규모 서비스 환경에서 다음과 같은 컨테이너와 관련된 기술 문제를 해결하기 위해서 등장하였다.
쿠버네티스는 이러한 기술 문제들을 해결하기 위한 기능들을 제공한다.
도커 자체 컨테이너 오케스트레이션 도구인 도커 스웜과 비교하자면, 규모가 작은 프로젝트에서는 도커 스웜이 적합할지도 모른다. 러닝 커브가 상대적으로 낮고 설정 방법이 간편하기 때문이다. 하지만 어느 정도 규모가 크다면 쿠버네티스가 러능 커브가 높고 설정 방법이 복잡하지만 신뢰성이 높고 유연하며 확장 가능성이 높기 떄문에 쿠버네티스를 사용하는 것이 바람직하다. 도커 스웜의 경우 서비스 오브젝트만 제공하지만, 쿠버네티스는 파드, 서비스, 디플로이먼트, 컨트롤러 등 다양한 오브젝트를 통해 좀 더 복잡하고 다양한 기능들을 제공한다. 예를 들어 도커 스웜의 경우 Auto Scaling을 제공하지 않는다.
쿠버네티스의 구조는 다음과 같다.
도커 스웜과 유사하게 마스터 노드와 워커 노드로 구성되어 있으며 마스터 노드는 쿠버네티스의 전반적인 기능들을 컨트롤하는 역할을 수행하며 워커 노드들은 컴퓨터 자원들을 제공하여 실제 컨테이너들을 담고 있는 파드들이 스케줄링되어 동작하게 된다.
쿠버네티스는 다양한 오브젝트를 통해 클러스터의 상태를 사용자가 원하는 상태로 관리한다.
먼저 쿠버네티스 오브젝트들을 논리적으로 독립된 공간으로 만들어주는 네임스페이스가 있다. 네임스페이스에는 쿠버네티스의 최소 배포 단위인 파드들이 있다. 그리고 이 파드들에게 외부로부터 연결이 가능하도록 IP를 할당해주는 서비스가 있다. 서로 다른 네임스페이스에 있는 파드들에게는 연결할 수가 없다. 파드 안에는 여러 컨테이너들이 있다. 컨테이너 하나 당 하나의 앱이 동작하고 있다. 따라서 파드에는 여러 앱들이 돌아갈 수 있다. 파드에 문제가 생겨서 재생성되면 그 안의 데이터는 유실되는데 이를 별도로 저장해서 관리할 수 있도록 해주는 오브젝트가 볼륨이다. 그리고 이러한 파드들은 관리해주는 컨트롤러가 있다. 또한 파드 생성 시 컨테이너 안의 환경변수를 넣어줄 수 있는데 컨피그맵과 시크릿을 통해서 가능하다. 이처럼 쿠버네티스는 다양한 오브젝트들을 제공하는데 이와 관련된 구체적인 내용은 다른 글에서 다뤄보려고 한다.