쿠버네티스 이해하기

요즘 흔히 사용되는 쿠버네티스, 왜 사용하고 어떤 이득이 있을까? 간단하게 정리해봤다.

모놀리틱 아키텍처(Monolithic Architecture)

과거에는 하나의 고성능 하드웨어에서 하나의 큰 어플리케이션을 동작시키는 모놀리틱 아키텍처가 일반적이었다.

모놀리스(Monolith)는 몇가지 단점이 있다.

  • 시스템에 부하가 증가할 경우 수직, 수평확장을 해야 하는데 수직확장에는 한계가 있고, 수평확장도 데이터베이스 등은 까다로워 현실적으로 어려운 경우가 있다.
  • 하나의 거대한 서비스에 새 기능을 추가할 경우, 서비스 전체를 다시 배포해야 하므로 잦은 패치가 어렵다.
  • 릴리즈 주기가 느리고 복잡한 코드베이스는 개발자들의 작업 속도를 저하시킨다.

이런 이유에서 MSA(Micro-service Architecture)가 등장했다.

마이크로서비스 아키텍처(Micro-service Architecture)

마이크로서비스는 하나의 복잡하고 거대했던 서비스를 기능 단위로 쪼개 여러 개의 서비스로 분할한 한 단위를 의미한다. 마이크로서비스는 서로 분리되어 있기 때문에 개별적으로 개발, 패치, 배포가 가능하다. 따라서 신속하게 구성요소를 변경할 수 있고, 병목지점의 마이크로서비스만 증설하는 것도 가능해 확장이 용이하다.

하지만 마이크로서비스에도 몇가지 단점이 있다.

  • 물리적인 리소스의 활용률을 높이고 하드웨어 비용을 낮출 수 있도록 각 마이크로서비스를 배포할 위치를 찾는 것은 매우 어렵다. 마치 디스크 조각모음처럼 물리장비들은 메모리나 CPU 사용량, 하드디스크 용량 면에서 유휴 자원이 생길텐데, 그 유휴자원에 꼭 맞는 마이크로서비스를 찾아야 그 자원을 활용할 수 있기 때문이다.
  • 각 마이크로서비스의 배포를 자동으로 스케줄링하거나 관리하고 장애처리를 하는 것도 어렵다.
  • 마이크로서비스는 여러 개가 함께 작업을 수행하므로 여러 마이크로서비스들이 하나의 시스템처럼 동작하기 위해서는 상호 종속성이나 네트워크를 처리해주는 것이 까다로운 문제가 된다.
  • 각 마이크로서비스가 프로세스이므로 여러 프로세스로 분산된 시스템은 디버깅 하기가 까다롭다.
  • 각 마이크로서비스는 독자적으로 개발하고 배포하기 때문에 같은 라이브러리라도 서로 다른 버전에 의존하고 있을 수도 있다. 이런 경우 하나의 물리장비 위에 각 마이크로서비스별로 같은 라이브러리도 서로 다른 버전을 설치해 운용해야 하므로 운영팀이나 SE(System Engineer) 입장에서는 운용이 상당히 고생스럽게 된다.

이러한 이유에서 쿠버네티스, 그리고 도커와 같은 도구들이 필요해졌다.

데브옵스(DevOps)

살펴본 바와 같이 서비스는 빠르게, 자주 배포할 수 있을수록 고객 경험을 크게 개선할 수 있다. 가장 이상적인 방법은 개발자, 운영팀, QA가 전체 프로세스에 걸쳐 긴밀하게 협업하는 것이다. 이를 DevOps라 한다.

DevOps를 통해 개발, 운영, QA는 서로를 긴밀히 이해하고 서로에게 어떤 문제와 어떤 니즈가 있는지 잘 이해할 수 있다. 더불어 이전보다 보다 신속하게 새 기능을 고객에게 전달할 수 있게 된다.

한편 NoOps도 있는데, 인프라를 알지 못해도 개발자가 직접 어플리케이션을 배포하는 방식이다. 즉, 인프라에 대한 관리와 어플리케이션의 배포가 완전히 분리되며, 개발자는 개발하고 배포할 뿐 인프라는 신경쓰지 않는다. 운영팀은 인프라만 신경쓸 뿐, 어플리케이션에 대해서는 몰라도 된다. 쿠버네티스가 바로 이런 NoOps를 가능하게 하는 강력한 도구이다.

컨테이너

컨테이너 자체는 MSA의 모든 문제를 해결해주는 만능 툴은 아니다. 컨테이너는 MSA로 된 복잡한 시스템을 효율적으로 관리해주는 도구라기 보다는 각 어플리케이션에 일관된 환경을 제공하기 위한 일종의 도구이다.

컨테이너는 호스트 OS에서 실행되는 하나의 격리된 프로세스다. 즉 물리장비가 허락하는 한 다수의 컨테이너를 하나의 장비에서 운용할 수 있다. 각 컨테이너는 어플리케이션에 격리된 공간을 제공한다. 각 컨테이너는 그 컨테이너에서 실행될 어플리케이션을 위한 환경이 담겨 있다.

도커가 이런 컨테이너 시스템 중 가장 유명하다. 도커 컨테이너를 이용하면 프로덕션 머신과 개발자의 개발환경이 전혀 다르더라도 어플리케이션을 동일한 환경에서 실행할 수 있다.

도커는 어플리케이션과 환경을 패키지화한 이미지, 도커 이미지를 저장하고 공유할 수 있는 레지스트리, 이미지를 통해 생성된 리눅스 컨테이너인 컨테이너로 이뤄진다.

쿠버네티스는 컨테이너 기술을 이용한다. 그래서 쿠버네티스를 컨테이너 오케스트레이션 도구라고 부르는 것이다.

도커 간단 정리

도커에서 이미지가 빌드되는 과정은 다음과 같다.

  1. 도커 클라이언트가 디렉토리의 콘텐츠를 도커 데몬에 업로드한다.
  2. 리눅스가 아닌 OS에서 도커 사용시, 도커 클라이언트는 호스트 OS(맥, 윈도우 등)에, 도커 데몬은 가상머신 내부에서 실행된다.
  3. 도커 데몬은 업로드된 콘텐츠를 이미지로 빌드한다.

이미지는 하나의 큰 바이너리 덩어리가 아니라 여러 개의 레이어로 구성된다. 이로써 서로 다른 이미지가 레이어를 공유할 수 있어 효율적이다.

Dockerfile의 각 명령들은 하나의 레이어를 형성한다. 예를들어 COPY, ADD, RUN, CMD 등은 각각 하나의 레이어를 추가한다.

각 컨테이너는 격리된 프로세스이며 격리된 파일시스템을 갖고 있다.

쿠버네티스

앞서 살펴봤듯이, 모놀리스의 대안으로 등장한 마이크로서비스는 숫자가 늘어날 수록 관리가 어려워 지는 문제가 있었다. 또한, 새 마이크로서비스를 추가하거나 패치를 배포할 때마다 여전히 SE의 도움이 필요해 개발자는 배포 과정에서 SE가 도움을 줄 때까지 기다려야 하는 문제도 있었다.

이런 문제들을 뼈저리게 느꼈던 것은 전세계를 상대로 수십만 대의 물리 장비를 운용하는 구글이었다. 다른 기업들보다 더 빠르게 전 세계에서 글로벌 인터넷 기반 서비스를 제공했던 구글은 이런 문제를 뼈져리게 느꼈고, 쿠버네티스라는 솔루션을 만들게 된다.

수십만 대의 물리장비를 운용한다면, 물리장비의 운용률을 조금만 올려도 큰 비용을 절감할 수 있다.

운용툴이 만약 개발과 관리도 단순하게 만든다면 이런 운용툴을 만들 동기가 충분한 것이다.

구글 내부에서 오랜 기간 동안 내부적으로 사용되었던 보그, 오메가 등의 시스템이 오픈소스로 통합되어 공개도니 것이 쿠버네티스다.

쿠버네티스는 컨테이너화된 어플리케이션의 배포와 관리를 쉽게 하는 시스템이다. 리눅스 컨테이너 덕분에 서로 다른 환경을 가진 여러 어플리케이션을 컨테이너 단위로 배포할 수 있다. 더불어 배포 과정을 자동화함으로써 쉽게 커다란 시스템을 운용할 수 있게 한다. 각 어플리케이션은 독립된 컨테이너에서 실행되기 때문에 다른 컨테이너의 어플리케이션에 영향을 미치지 않는다.

쿠버네티스는 모든 노드가 하나의 거대한 시스템인 것처럼 실행되며, 인프라를 추상화했기에 개발, 배포, 관리가 단순하고 쉬워진다.

쿠버네티스를 쓰는 이유

쿠버네티스는 앞서 제기된 모놀리틱 아키텍처와 MSA의 문제점들을 대부분 해결한다.

  • 물리장비를 수평적으로 확장하고 쿠버네티스에서는 필요한 노드만 증설하면 되므로 확장이 쉬워진다.
  • 잘게 쪼개진 마이크로서비스를 단순하고 쉽게 배포할 수 있다.
  • 여러 물리 리소스를 쿠버네티스라는 시스템으로 묶어 관리하기 때문에 물리장비의 운용율을 극대화하고 비용을 절감할 수 있다.
  • 각 마이크로서비스간 상호 종속성이나 네트워크 연결을 쉽게 처리해 준다.
  • 개발자는 컨테이너를 쿠버네티스에 쉽게 배포할 수 있고, 운영팀은 쿠버네티스만 관리하면 된다. 둘의 역할이 분리되며 각자가 각자의 분야에 전념할 수 있고 개발 및 배포 속도가 빨라진다.

쿠버네티스의 동작원리

개발자가 어플리케이션 메니페스트(Application Manifest)를 마스터 노드에 배포하면, 쿠버네티스는 해당 어플리케이션을 워커 노드 클러스터에 배포한다. 개발자는 하나의 마스터 노드만 신경쓰면 되고, 쿠버네티스가 알아서 여러개의 워커 노드에 어플리케이션을 배포한다.

쿠버네티스는 이런 클러스터를 위한 운영체제와도 같다. 쿠버네티스를 통해 스케일링, 로드밸런싱 등을 쉽게 이용할 수 있다.

마스터 노드는 전체 쿠버네티스를 제어하고 관리하는 컨트롤 플레인을 실행한다.

컨트롤 플레인은 다음과 같은 4가지 요소로 구성된다.

  • API 서버: 사용자, 컨트롤 플레인 구성 요소와 통신
  • 스케줄러: 어플리케이션 배포
  • 컨트롤러 매니저: 워커 노드 추적, 장애처리 등 클러스터단 기능 수행
  • etcd: 컨테이너 구성을 저장하는 분산 데이터 저장소

한편 워커 노드는 3가지 요소로 구성된다.

  • 컨테이너 런타임: 컨테이너를 실행
  • Kubelet: API 서버와 통신하고 노드의 컨테이너 관리
  • kube-proxy: 어플리케이션 구성 요소 간 네트워크 트래픽을 로드밸런싱

쿠버네티스에서 어플리케이션의 실행은 다음과 같은 순서를 따른다.

  1. 어플리케이션 패키징: 하나 이상의 컨테이너 이미지로 패키징
  2. 패키징된 이미지를 레지스트리로 푸시
  3. 쿠버네티스 API 서버에 어플리케이션 디스크립션(컨테이너 이미지, 어플리케이션 구성 요소가 포함된 이미지, 구성요소간 통신 방법, 동일 서버에 함께 배치되야 할 구성요소 등 정보) 게시
  4. API 서버는 디스크립션을 참고해 각 컨테이너에 필요한 리소스를 계산하고 사용 가능한 워커 노드에 컨테이너 할당
  5. 어플리케이션 실행 후 쿠버네티스는 배포 상태를 디스크립션과 지속적으로 비교해 일치하는지 확인
  6. 프로세스가 중단되거나 문제가 생기면 자동으로 재시작

쿠버네티스는 컨테이너를 클러스터 안에서 종종 이동시킬 수 있지만, 하나의 고정 IP 주소로 모든 컨테이너를 노출하고 이 주소를 클러스터 내 모든 어플리케이션에 노출한다. 따라서 클러스터 내에서 컨테이너가 이동하더라도 컨테이너에 항상 연결할 수 있다.

쿠버네티스의 장점

오토 스케일링

쿠버네티스를 이용할 경우 복제본 수를 지정할 수도 있고, 쿠버네티스에 위임해 자동으로 그 수를 늘리거나 줄이게 할 수도 있다. 즉, 하드웨어 자원이 충분히 공급될 수 있다면 편리하게 오토스케일링할 수 있다.

SE의 관리포인트 최소화

컨테이너화된 어플리케이션은 실행에 필요한 모든 환경을 가지고 있다. 따라서 SE나 시스템 관리자가 어플리케이션을 배포하거나 실행하기 위해 별도로 라이브러리를 설치할 필요가 없다.

인프라와 어플리케이션 분리

쿠버네티스를 이용해 인프라와 어플리케이션을 분리할 수 있다. 이를 통해 컨테이너가 클러스터 내에서 자유롭게 이동할 수 있으므로 하드웨어 자원을 보다 효율적으로 이용할 수 있게 된다.

쿠버네티스 사용하기

쿠버네티스를 제대로 사용하려면 GKE(Google Kubernetes Engine)나 EKS(Elastic Kubernetes Service)를 사용하는 것이 좋다. 완전한 다중 노드 클러스터를 살펴볼 수 있기 때문이다.

하지만 로컬환경에서도 간단하게 사용해 볼 수 있는데, minikube를 이용하면 된다.

minikube start // minikube 실행
kubectl cluster-info // 실행 중인 cluster 정보 불러오기

파드(pod) 간단 소개

쿠버네티스에서 최소 단위는 컨테이너가 아니라 하나 또는 여러개의 컨테이너가 묶인 “파드(pod)”라는 단위다.

각 파드는 자체적인 IP, 호스트 이름, 프로세스가 있고 논리적으로 분리된 머신으로 동작한다. 따라서 다른 파드에서 실행 중인 컨테이너는 같은 워커노드에서 실행 중이라고 하더라도 다른 머신에서 실행되는 것과 같이 취급된다.

쿠버네티스에서는 컨테이너 단위로는 조회가 불가능하고 가장 작은 원자단위인 pod 단위로만 조회가 가능하다.

kubectl get pods

한편, 파드는 자체 IP 주소를 가지고 있지만 클러스터 외부에서 접근이 불가능하다. 파드에 접근하기 위해서는 로드밸런서 유형의 서비스를 생성하고 파드에 연결해야 한다.

kubectl expose po kubia --type=LoadBalancer --name kubia-http

파드는 언제든 사라지거나 재시작 될 수 있기 때문에 IP가 잦게 변경될 수 있다. 따라서 외부에서 항상 연결하기 위해서는 서비스를 생성해 이용한다. 서비스는 정적 IP를 할당받고 서비스가 존속하는 동안 변경되지 않아 외부에서 쉽게 서비스를 통해 각 파드들로 연결이 가능하다.

참고로 쿠버네티스는 선언적이다. 구체적으로 동작을 지시하는 것이 아니라, ‘원하는 상태’에 대해 묘사하면 쿠버네티스가 그 상태를 맞추는 식이다.

참고문헌

  • <쿠버네티스 인 액션>(마르코 룩샤, 에이콘 출판)