앞에서 얘기한 것과 같이 Kube controller manager는 Kubernetes에서 다양한 컨트롤러를 관리합니다. 컨트롤러는 마스터쉽에 있는 오피스라고 생각하면 됩니다. 오피스는 새로운 배가 도착하거나 배가 떠나거나 파괴될 때, 필요한 조치를 취하기 위해 배들을 모니터링하는 책임이 있습니다. 또 다른 오피스는 배의 컨테이너를 관리할 수 있습니다. 해당 오피스는 손상되거나 배에서 떨어진 컨테이너들을 살핍니다.
이러한 오피스들은 1. 지속적으로 배의 상태에 대해 모니터링하고 2. 상황을 개선하기 위해 필요한 조치를 취합니다. 쿠버네티스 용어로, 컨트롤러란 지속적으로 시스템 내 다양한 컴포넌트들의 상태를 모니터링하고, 전체 시스템이 의도된 기능과 상태로 작동하게 하는 프로세스 입니다.
예를 들어, node controller는 애플리케이션이 계속 실행될 수 있도록 노드의 상태를 모니터링하고 필요한 조치를 취할 책임이 있습니다. 노드 컨트롤러는 이 책임을 Kube API server를 통해 수행합니다.
노드 컨트롤러는 5초마다 노드의 상태를 테스트합니다. 이렇게 노드 컨트롤러가 노드의 health를 모니터링하는 것입니다. 만약 노드의 심장박동이 멈추면, 노드는 접근할 수 없는 것으로 표시됩니다. 그러나 바로 표시하지는 않고 표시하기 전에 40초 동안 기다립니다. 노드가 접근할 수 없는 것으로 표시되었다면 회복할 수 있는 5분의 시간을 줍니다. 5분 안에 회복이 안될 경우, 노드에 할당된 파드들을 제거하고 파드가 replica set의 일부인 경우, 건강한 그것 노드를 제공합니다.
다음 컨트롤러는 replication controller입니다. replica sets의 상태를 모니터링하고 replica set 내의 정해진 파드 수를 항상 보장하는 역할을 합니다. 파드가 죽으면 또다른 파드를 만들어냅니다.
예로 든 node controller와 replication controller는 컨트롤러의 두 가지 예시일 뿐이고, 쿠버네티스 안에서 더 많은 컨트롤러를 사용할 수 있습니다. 배포, 서비스, 네임스페이스, persistent 볼륨 등 지금까지 쿠버네티스에서 본 어떤 개념이든, 그리고 이러한 구조에 어떤 intelligence가 들어있든, 이 모든 것들은 다양한 컨트롤러를 통해 구현됩니다. 이러한 컨테이너들은 쿠버네티스 뒤에서 작동하는 뇌의 역할을 합니다.
이러한 컨트롤러들을 어떻게 볼 수 있을까요? 클러스터에서 어디에 위치하고 있을까요? 컨트롤러들은 'Kubernetes Controller Manager'라는 단일 프로세스로 패키징되어있습니다.
Kubernetes Controller Manager를 설치하면 다른 컨트롤러도 설치됩니다.
kubernetes scheduler는 노드에서 파드 스케쥴링을 담당한다고 앞에서 언급했습니다. 스케쥴러는 어떤 파드가 어떤 노드로 가야하는지 결정하는 것만 합니다. 실제로 파드를 노드에 배치하지는 않습니다.
그럼 파드를 노드에 배치하는 작업은 누가 할까요? 그 작업을 하는 것이 바로 kubelet입니다. kubelet(배의 선장)은 배에 파드를 만듭니다. 스케쥴러는 어떤 파드가 어디로 가는지만 결정합니다.
스케쥴러가 어떻게 결정하는지 더 자세히 살펴봅시다. 먼저, 스케쥴러가 필요한 이유는 무엇일까요? 배와 컨테이너가 많을 때, 컨테이너가 올바른 컨테이너인지, 올바른 배인지 확인하고 싶습니다. 예를 들어서, 배와 컨테이너의 크기가 다 다르다면, 배가 해당 컨테이너를 수용할 수 있을만큼 충분한 공간을 가지고 있는지 확인해야 할 것입니다.
또한, 컨테이너들이 올바른 배에 잘 배치되어있는지 확인해야 할 것입니다. Kubernetes에서 스케쥴러는 파드가 배치될 노드를 결정합니다. 이는 특정 기준에 따라 배치하게 됩니다. 리소스 요구사항이 다른 파드가 있다고 합시다. 클러스터에 있는 노드는 특정 애플리케이션 전용 노드일 수 있습니다. 그렇다면 스케쥴러는 이러한 파드를 확인하고 최적의 노드를 찾으려고 합니다.
예를 들어 파드가 'CPU: 10'이라는 요구사항이 있을 때, 스케쥴러는 두 단계를 거칩니다.
파드에 가장 적합한 노드를 식별합니다. 여기서 스케쥴러는 이 파드 프로필에 맞지 않는 노드(ex. CPU가 충분하지 않은 노드) 등 필터링을 시도합니다.
스케쥴러는 노드의 순위를 매깁니다. 우선순위 함수를 사용하여 0에서 10까지의 점수를 할당합니다. 예를 들어, 스케쥴러는 노드의 프리한 리소스의 양을 계산하고, 파드를 프리 리소스가 더 많은 노드에 배치합니다.
이것이 스케줄러가 대략적으로 작동하는 방식입니다. 물론 커스터마이징하여 자신만의 스케쥴러를 만들 수도 있습니다. 리소스 요구사항과 제한사항, Taints 및 toleration, 노드 셀렉터, 선호도 규칙 등 다룰만한 주제가 많습니다.
앞에서 우리는 kubelet이 배의 선장과 같다고 말했는데요. kubelet은 그 배의 모든 활동을 주도합니다.
kubelet은 마스터쉽과의 유일한 접점입니다.
마스터의 스케쥴러가 지시한 대로 kubelet은 컨테이너를 배에 싣거나 내리기도 합니다.
정기적으로 선박 및 컨테이너의 상태에 대한 보고서를 보냅니다.
Kubernetes 워커 노드에 있는 kubelet은 Kubernetes 클러스터에 노드를 등록합니다.
이때 필요한 모든 서류 작업을 담당하기도 합니다.
노드의 컨테이너 또는 파드를 load하라는 명령을 받았을 때, 도커와 같은 컨테이너 런타임 엔진에게 필요한 이미지를 pull 해 달라고 요청하고, 인스턴스를 실행합니다.
kubelet은 파드와 파드 안의 컨테이너들의 상태를 계속 모니터링하고, kubeAPI 서버에 필요시 보고합니다.
Kubernetes 클러스터 내에서 모든 파드는 다른 파드와 소통할 수 있습니다. 이는 클러스터에 대한 파드 네트워킹 솔루션 배포를 통해 수행됩니다. 파드 네트워크는 클러스터의 모든 노드에 걸쳐져 있고, 모든 파드에 연결되어 있는 internal virtual network 입니다. 이 네트워크를 통해, 파드들은 서로 소통할 수 있습니다. 이런 네트워크를 배포하기 위한 많은 솔루션들이 있습니다.
예를 들어, 첫 번째 노드에 웹 어플리케이션
이 하나 있다고 합시다. 두 번째 노드에는 데이터베이스
애플리케이션이 있습니다. 웹 앱은 파드의 IP을 사용하여 간단하게 데이터베이스에 접근할 수 있습니다. 그러나 데이터베이스 파드 IP가 항상 동일하다는 보장은 되지 않습니다.
웹 어플리케이션
이 데이터베이스
접근하는 더 나은 방법은 서비스를 사용하는 것입니다. 따라서 클러스터 전체에 데이터베이스 애플리케이션을 노출하는 서비스를 만듭니다. 이제 웹 어플리케이션
은 서비스 이름인 DB를 사용하여 데이터베이스
에 접근할 수 있습니다. 서비스는 IP주소도 할당받기 때문에, 파드가 서비스 IP나 서비스 이름을 사용하여 서비스에 접근할때마다, 서비스는 트래픽을 백엔드 파드(데이터베이스)로 전달합니다.
자, 이러한 서비스는 무엇이고 어떻게 IP를 받아올까요? 서비스가 동일한 파드 네트워크에 조인될까요? 서비스는 실물이 아니기 때문에 파드 네트워크에 조인할 수 없습니다. 서비스는 파드와 같은 컨테이가 아니어서 어떠한 인터페이스도, listening(대기하는) 프로세스도 없습니다. 쿠버네티스 메모리에서 그저 살아있는 가상 컴포넌트입니다. 그러나 우리는 방금 클러스터의 모든 노드에서 서비스에 접근할 수 있어야 한다고 말했습니다. 이것이 어떻게 가능할까요? 여기서 kube-proxy가 등장합니다.
kube-proxy는 쿠버네티스 클러스터의 각 노드에서 실행되는 프로세스입니다. kube-proxy의 임무는 새로운 서비스를 찾고, 새로운 서비스가 생성될 때마다 각 노드에 해당 서비스에 대한 트래픽을 백엔드 파드로 전달하기 위한 적절한 규칙을 생성하는 것입니다.
이 규칙을 생성하는 한 가지 방법은 iptables 규칙을 사용하는 것입니다. 지금과 같은 케이스에서 kube-proxy는 클러스터의 각 노드에서 서비스 IP(10.96.0.12)로 향하는 트래픽을 전달하기 위해 iptables 규칙을 생성합니다. iptables 규칙은 실제 파드의 IP인 10.32.0.15를 서비스 IP 10.96.0.12로 변경합니다. 이것이 kube-proxy가 서비스를 구성하는 방식입니다.