TIL

53일차 컨테이너 오케스트레이션

김영재0412 2022. 7. 1. 01:11

쿠버네티스(Kubernetes, k8s)란

  • 오픈소스로 만들어진 컨테이너 오케스트레이션 도구
  • 컨테이너화된 애플리케이션을 자동으로 배포, 스케일링하는 등의 관리 기능을 제공
    • 각기 다른 환경(온프레미스 서버, VM, 클라우드)에 대응 가능

 

무엇을 오케스트레이션 한다는 것인가?

 

즉, 컨테이너 오케스트레이션 도구는, 수십~수백개의 컨테이너를 관리하고자 할 때 보다 더 잘 관리하기 위한 툴이다. 앞서 배운 컨테이너 조합은 기껏 해봐야 서너개에 불과한데, 어떻게 수십~수백개의 컨테이너가 생길 수 있다는 말일까?

  • 이는 아키텍처의 트렌드가 모놀리식에서 마이크로서비스로 바뀌고,
  • 이로 인해서 컨테이너의 갯수가 증가하고,
  • 여기에 확장성을 고려해 스케일링까지 더할 경우에 발생할 수 있는 일이다.

다섯개 정도의 마이크로서비스를 운영하는 조직이 회사 내에 세 팀 정도가 존재하고, 최소한 두 개 이상의 레플리카(복제본)을 만든다고 가정하면, 벌써 서른(5 x 3 x 2) 개의 컨테이너가 존재할 것이다. 이럴 때에는 쿠버네티스 도입을 고려해볼 수 있다.

쿠버네티스 공식 웹사이트에는 쿠버네티스를 만든 Google의 경험을 바탕으로 다음과 같이 안내하고 있습니다.

Google이 일주일에 수십억 개의 컨테이너들을 운영하게 해준 원칙들에 따라 디자인되었기 때문에, 쿠버네티스는 운영팀의 규모를 늘리지 않고도 확장될 수 있다.

 

언제 사용하지 말아야 할까?

 

닭 잡는데 소 잡는 칼을 사용하지 않는 것처럼, 어떤 기술을 적용하기에 앞서 “언제 사용하지 말아야 할지를 아는 것”이 진정한 고수로 가는 길이다.

따라서 언제 사용하지 말아야 할지를 알아봅시다.

  • 여러 Tier(단계)로 나뉘어지지 않은 모놀리식 아키텍처에서는 적합하지 않다.
    • 모놀리식 아키텍처는 먼저 마이크로서비스 분해 전략을 세우는 것이 우선이다.
  • 고작 서너 개에 불과한 컨테이너만을 다루려면 적합하지 않다.
    • 서너 개의 불과한 컨테이너는 docker-compose로도 충분하다.
  • 비교적 단순한 아키텍처에, 스케일링이 필요하지 않은 경우에는 적합하지 않다.
    • 이후 트래픽이 증가하거나, 스케일링이 필요한 경우, 서버리스 서비스를 사용하면 다소 저렴한 가격에 관리형 서비스를 이용할 수 있으며, AWS 내에서도 오토 스케일링과 같은 관리형 서비스가 배포 환경에서 제공된다.

 

언제 사용해야 하며 어떤 기능이 있는가?

어떤 기능을 제공하는지를 살펴보면, AWS를 배웠을 때와 비슷한 기능을 제공한다.

  • 마이크로서비스를 컨테이너 방식으로 운영하는 조직이 확장성을 고려해야 할 때
  • 무중단 서비스, 즉 고가용성을 제공해야 할 때
  • 그 밖에 다양한 기능들: 자가 치유,배치 실행, 구성 관리, 로드 밸런싱...

보시다시피 특징적인 기능은 AWS와 같은 퍼블릭 클라우드 공급자에서도 비슷하게 제공하지만 비용의 문제에서 자유로울 수 없다.

이를 피하기 위해 쿠버네티스를 사용하기도 한다.

 

쿠버네티스를 사용하는 사람들은 다음과 같이 사용한다.

  • 쿠버네티스를 이용해 온프레미스 상에서 (사설) 클라우드 인프라를 구성하고,
  • 저렴한 클라우드 서비스의 일부분을 도입하여 하이브리드 형태로 구성하기도 하며,
  • 필요에 따라 쿠버네티스로 구성한 인프라를 통째로 AWS 등에 마이그레이션 하기도 한다. (이식성)
    • 주요 퍼블릭 클라우드 공급자들은 관리형 쿠버네티스를 지원한다. 예) AWS EKS
 

 

쿠버네티스 작동 원리

 

 

쿠버네티스 컴포넌트를 크게 분류를 하자면, 쿠버네티스 기능 제어를 전체적으로 담당하는 컨트롤 플레인(Control Plane) 컴포넌트와 컨트롤 플레인 컴포넌트의 요청을 받아 각 노드에서 동작을 담당하는 노드(Node) 컴포넌트로 나누어볼 수 있다. 컨트롤 플레인 컴포넌트와 노드 컴포넌트는 세부적으로 많은 개별 구성 요소를 가지고 있는데 먼저 이 구조를 전체적으로 살펴보겠다.

 

앞에서 쿠버네티스의 컴포넌트는 컨트롤 플레인(Control Plane) 컴포넌트와 노드(Node) 컴포넌트로 나누어진다고 볼 수 있는데 이 부분을 회사 조직과 업무에 비유해겠다. 컨트롤 플레인 컴포넌트는 회사 전반에 해당하는 본사의 업무, 노드 컴포넌트는 지사의 업무 비유하겠다.

 

워커노드

 

워커 안에는 한개 이상의 컨테이너가 자리잡고 있으며, 즉 워커 노드는 실제로 애플리케이션이 실행되고 있는 곳이라고 할 수 있다.

 

kubelet 

 

kubelet

쿠블릿(kubelet)은 노드에서 컨테이너가 동작하도록 관리해 주는 핵심 요소이다. 각 노드에서 파드를 생성하고 정상적으로 동작하는지 관리하는 역할을 담당하고 있으며, 실제로 우리가 쿠버네티스의 워크로드를 관리하기 위해 내려지는 명령은 kubelet을 통해 수행된다고 볼 수 있다. 우리가 쿠버네티스 파드를 관리하기 위해 작성하는 YAML을 쿠버네티스 클러스터에 적용하기 위해 kubectl 명령어를 사용할 때, 이 YAML이 kube-apiserver로 전송된 후 kubelet으로 전달된다. kubelet은 이 YAML을 통해 전달된 파드를 생성 혹은 변경하고, 이후 이 YAML에 명시된 컨테이너가 정상적으로 실행되고 있는지 확인한다. 지사에서 본사의 업무 요청을 받아 확인하는 막중한 역할을 수행하고 있다고 볼 수 있다.

 

container runtime 

 

container runtime

컨테이너 런타임은 파드에 포함된 컨테이너 실행을 실질적으로 담당하는 애플리케이션을 의미한다. 단, 컨테이너 런타임은 쿠버네티스 구성 요소에 기본적으로 포함되어 있거나, 특정 소프트웨어를 지칭하는 것은 아니다. 쿠버네티스가 컨테이너를 제어하기 위해 제공하는 표준 규약인 컨테이너 런타임 인터페이스(CRI)를 준수하여 쿠버네티스와 함께 사용할 수 있는 외부 애플리케이션들을 의미한다. 쿠버네티스는 컨테이너 관리를 위해 특정 애플리케이션을 사용할 것을 강제하지는 않고 단지 쿠버네티스가 제공하는 규약에 따라 쿠버네티스와 연계할 것을 요구하고 있다. 이러한 규약을 따르는 대표적인 컨테이너 런타임은 컨테이너디(containerd), 크라이오(CRI-O) 등이 있다.

 

 좀 더 자세한 설명은 ‘흔들리는 도커(Docker)의 위상 - OCI와 CRI 중심으로 재편되는 컨테이너 생태계’  참고

 

쿠버네티스에서는 이러한 컨테이너(정확히는 컨테이너 그룹)와 컨테이너가 사용하는 볼륨, 그리고 컨테이너의 작동 정보를 특별히 파드(Pod)라는 이름으로 부른다.

 

 

 

제어판 (Control Plane) 컴포넌트

 

관리를 위해 필요한 프로세스들은 전부 제어판 컴포넌트에 있으묘 제어판 컴포넌트는 클러스터가 잘 작동할 수 있게 돕는다.

 

kube-apiserver

말 그대로 API 서버는 모든 클러스터 관리의 입구로서, 명령을 내릴 수 있는 관문이다. 실제로 쿠버네티스에서 제공되는 UI나 CLI등에서 클러스터 관리를 위해 뭔가 명령을 내리면 API가 호출되며 당연히 직접 호출할 수도 있다.

 

공식 문서에서는 kube-apiserver를 ‘쿠버네티스 컨트롤 플레인의 프론트 엔드’ 라고 소개하고 있다. 쉽게 감이 오지 않는 분들을 위해 비유를 하자면 우리가 어떤 회사에 방문하면 가장 먼저 안내데스크를 찾아간다. 여기서 방문 목적을 설명하고 안내를 받을 수 있으며 안내 데스크에서는 이 방문자가 적절한 방문자인지 시스템에서 검색하거나 관련된 직원에게 문의하여 방문증을 발급하고, 가야 할 곳을 안내해준다.

 

이런 역할을 하는 컴포넌트가 kube-apiserver이다. kube-apiserver는 쿠버네티스 클러스터로 들어오는 요청을 가장 앞에서 접수하는 역할을 한다. 예를 들어 쿠버네티스 커맨드 라인 도구인 kubectl을 사용해 각종 명령을 수행할 경우 이 명령은 kube-apiserver로 전송되며 이렇게 전달된 요청에 대하여 kube-apiserver는 이 요청의 처리 흐름에 따라 적절한 컴포넌트로 요청을 전달하는 역할까지 맡고 있다.

 

 

kube-controller-manager

 

Controller manager는 클러스터에서 무슨 일이 발생하는지를 추적하는 역할을 한다. 예를 들어 컨테이너가 죽거나 재시작되었을 경우, 컨트롤러 매니저는 이를 알 수 있다.

 

회사에서는 목표 수준을 달성하기 위해 끊임없이 확인하고, 목표치에 미달하는 부분에 대해서는 대책을 수립한다. 목표 달성한 후에도 이를 유지하려면 책임감 있는 관리자가 필요하며 쿠버네티스에서 이 역할을 하는 컴포넌트가 kube-controller-manager이다.

 

kube-controller-manager는 다운된 노드가 없는지, 파드가 의도한 복제(Replicas) 숫자를 유지하고 있는지, 서비스와 파드는 적절하게 연결되어 있는지, 네임스페이스에 대한 기본 계정과 토큰이 생성되어 있는지를 확인하고 적절하지 않다면 적절한 수준을 유지하기 위해 조치하는 역할을 하고 있다.

 

kube-scheduler

 

스케쥴러는 서버(노드) 리소스를 바탕으로 컨테이너(정확히는 pod)가 노드에 배치되게 만드는 역할을 담당하며 새로 생성된 컨테이너를 찾아 노드에 할당한다.

 

쿠버네티스 클러스터는 여러 노드로 구성되어있으며 기본적인 작업 단위라고 할 수 있는 파드는 여러 노드 중 특정 노드에 배치되어 동작하게 된다. 이때 새로 생성된 파드를 감지하여 어떤 노드로 배치할지 결정하는 작업을 스케줄링이라 한다. 이런 스케줄링을 담당하는 컴포넌트가 kube-scheduler이다. 스케줄링을 위해 노드 및 파드의 각종 요구사항과 제약사항을 종합적으로 판단할 필요가 있는데, 이러한 판단 또한 kube-scheduler의 역할이다. 회사로 비유하자면, 각 부서 인력 소요 계획과 신입사원 역량을 고려해 적절한 부서로 배치하는 인사 담당 부서에 비유할 수 있다.

 

etcd

ETCD 데이터베이스가 존재하는데, 이는 Key-value 저장소이며 클러스터 관리에 필요한 모든 데이터를 저장하는 공간이다. 인프라를 원하는 상태로 만들기 위해서는, 정상 상태에 대한 snapshot 및 관리에 필요한 메타데이터가 어딘가에 저장되어야 하는데, ETCD는 바로 이를 담당한다.

 

겉으로는 쉽게 드러나지 않아 놓치기 쉽지만 매우 중요한 역할을 담당하고 있는 컴포넌트가 엣시디(이하 etcd)이다. 쿠버네티스 클러스터가 동작하기 위해서는 클러스터 및 리소스의 구성 정보, 상태 정보 및 명세 정보 등이 필요하며 etcd는 이를 키-값(key-value) 형태로 저장하는 저장소이다. 

 

만약 etcd가 정상적으로 동작하지 않는다면 쿠버네티스 클러스터에 존재하는 리소스들은 그저 바다에 떠다니는 난파선과 다를 바 없게 되며 etcd는 그만큼 매우 중요한 역할을 수행하고있다. 안정적인 동작을 위해 자료를 분산해서 저장하는 구조를 채택하고 있으며 비유해서 설명드리자면, etcd는 회사 각종 중요 정보가 모두 모여 있는 금고와 같은 곳이고, 매우 중요한 정보를 보관하는 금고이기 때문에 동일한 자료를 여러 금고에 나눠서 보관하고 있다고 생각하시면 될 것 같다.

 

 

 

쿠버네티스 워크로드

 

파드 (Pods)

 

파드(Pod)는 쿠버네티스의 배포 가능한 가장 작은 컴퓨팅 유닛이며 파드는 그 자체로 하나의 논리적인 호스트이다.

파드는 다음을 포함할 수 있습니다. 마치 도커 컨테이너처럼 파드 내에서 다음 요소들은 격리됩니다.

  • 하나 이상의 애플리케이션 컨테이너
  • IP 주소
  • 볼륨과 같은 공유 스토리지

쿠버네티스에서는 “워크로드 리소스”를 만들기 위해 YAML 파일과 같은 리소스 정의 파일을 사용하는 것이 보통이다.

워크로드란?

쿠버네티스에서는 “쿠버네티스 상에서 작동되는 애플리케이션”을 의미하며, 클라우드 분야에서는 “어떤 애플리케이션을 실행할 때 필요한 IT 리소스의 집합”이라는 의미로 통용된다.

마찬가지로 파드를 생성하기 위해 파드를 정의할 때 다음과 같은 형태의 YAML 파일을 사용할 수 있다

 

파드 적용하기

위 파드 정의 파일을 실제로 만들기 위해서는, 다음 명령을 사용한다.

1
kubectl apply -f simple-pod.yaml

위 명령을 통해 pod/nginx created 라는 결과가 출력되었다면, 즉시 명령어 kubectl get pods 를 통해 상태를 확인할 수 있다.

$ kubectl get pods
NAME    READY   STATUS              RESTARTS   AGE
cozserver   0/1     ContainerCreating   0          8s
$ kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
cozserver   1/1     Running   0          30s

STATUS를 통해 컨테이너가 만들어지고(ContainerCreating), 컨테이너가 실행중인(Running) 상태를 확인할 수 있다.

 

  • Q. kubectl describe pod/nginx 명령을 이용해서 파드에 대한 자세한 정보를 볼 수 있습니다. 이 정보를 통해서 알 수 있는 내용은 무엇인가요?

  • Q. 방금 만든 파드를 지우기 위해서는 어떤 명령어를 사용하면 좋을까요? 공식 문서의 치트 시트를 참고하여 직접 지워봅시다.

kubectl delete pods cozserver

 

 

 

 

 

 

디플로이먼트 (Deployment)

쿠버네티스에서 Deployment는 한글로 번역하면 안 된다. 흔히 우리는 배포라고 하면 서비스의 노출을 떠올리지만, 쿠버네티스의 Deployment는 서비스 노출의 의미가 아니다. 쿠버네티스에서의 Deployment는 파드의 교체/배치(placement)와 관련된 명세이다. 

 

파드의 진실

쿠버네티스에서는 사실 직접 사용자가 개별 파드를 만들 일이 그리 많지 않다. 왜냐하면 파드는 일시적이고, 언제나 삭제될 수 있음을 감안하고 만들기 때문이다. 예를 들어, 파드가 실행되는 공간인 노드가 만일 실패하는 경우, 그 안에서 실행되는 파드 역시 사용할 수 없게 된다.

 

사실 컨테이너를 수동으로 만들고 관리하는 것은, 그냥 도커만 단독으로 사용해도 충분히 할 수 있는 일이다. 쿠버네티스의 핵심은 컨테이너를 오케스트레이션하는 것으로, 파드 장애시 자동 복구하거나, 복제하거나 하는 등의 일을 자동으로 처리하는 데에 있다. AWS로 따지면 ECS(Elastic Container Service)가 하는 일과 비슷하다. 이 둘은, 컨테이너의 로드 밸런싱과 오토 스케일링과 같은 일을 담당한다.

 

결론적으로, 파드는 디플로이먼트, 스테이트풀셋, 데몬셋을 이용해 관리하는 것이 바람직하며 해당 워크로드 리소스는 파드 템플릿을 항상 포함하고 있다.

 

 

디플로이먼트가 무엇이며 어떤 일을 할 수 있는 리소스인가?

디플로이먼트는 파드를 업데이트하기 위한 선언적 명세이다.

 

디플로이먼트 리소스를 통해 다음을 할 수 있다.

  • (레플리카셋, 즉 복제본 구성을 이용하여) 파드를 원하는 개수만큼 실행시킬 수 있으며
  • (제어판 Control Plane을 이용하여) 파드를 업데이트할 수 있으며
  • 마찬가지로, 파드를 롤백하는 것도 가능하다.

 

리뷰: 다양한 배포 전략

애플리케이션의 여러 복제본이 존재할 경우, 이 각각의 복제본을 새 버전으로 업데이트하는 방법으로 배포 전략이 있음을 배웠다.

  • 재생성 (Recreate): 이전 버전을 삭제하고 새 버전 생성
  • 블루/그린 배포: 한 번에 이전 버전에서 새 버전으로 연결을 전환
  • 롤링 배포: 이전 버전을 scale down하고, 새 버전을 scale up 하는 방식으로 단계별로 교체, 롤아웃(rollout)이라고 부른다.
  • 카나리 배포: 새 버전이 잘 작동한다고 판단되면, 이전 버전을 교체한다.

디플로이먼트는 파드의 복제본을 자동으로 업데이트하게 해주는 명세이므로, 쿠버네티스가 지원하는 배포 전략으로는 재생성(Recreate)과 롤링 배포(RollingUpdate) 방식을 선택할 수 있다.

 

 

서비스(Service)

 

파드를 외부로 노출시키기

 

클러스터 안에 파드는 각각 고유의 IP를 가지고 있지만, 직접 우리가 내부망에 접속할 수 있는 것은 아닙니다. 그럼 어떻게 파드 안에 서비스가 외부로 노출될 수 있을까?

 

서비스, 로드밸런싱, 네트워킹 공식문서의 설명을 인용해봅시다

  • 서비스 리소스를 사용하면 파드에서 실행 중인 애플리케이션을 클러스터 외부에서 접근할 수 있으며
  • 또한 서비스를 사용하여 클러스터 내부에서 사용할 수 있는 서비스만 게시할 수 있다. 쿠버네티스에서 서비스는 파드의 집합에 접근할 수 있는 정책을 정의하는 추상적 개념이며 서비스 리소스가 정의된 YAML 파일에 selector라는 것을 이용해 서비스할 대상 타겟을 설정할 수 있다.

앞서 디플로이먼트를 통해 파드의 복제본을 원하는 개수만큼 실행시킬 수 있다고 언급했다. 서비스 리소스는 이러한 파드 집합에 접근할 수 있게 하며, 파드가 교체되거나, 어떤 특정 파드에 문제가 생긴 경우에도 사용 가능한 파드를 찾아 알아서 접속할 수 있게 돕는다.

 

minikube에서는 EXTERNAL-IP가 pending 상태로 진전이 되지 않으며 minikube tunnel 명령을 이용하면, EXTERNAL-IP가 127.0.0.1로 설정되며, 그때부터 http://localhost 로 접속이 가능해진다.

 

접속이 원활하지 않을 경우, tunnel을 중지하고 해당 명령을 다시 시도해봅시다. sudo 권한이 필요할 수 있다.

 

service를 deployment하면 그후부터 localhost로 접속이 가능해진다.