컨트롤러 (Controllers)

컨트롤러 (Controllers)

쿠버네티스에서 컨트롤러(controller)는 클러스터의 상태를 지켜보는 제어 루프(control loop)예요. 상태를 관찰한 뒤, 필요하면 변경을 만들거나 요청합니다. 각 컨트롤러는 현재 클러스터 상태를 원하는 상태(desired state)에 더 가깝게 옮기려고 하죠.

컨트롤러 패턴

컨트롤러는 쿠버네티스 리소스 타입을 최소한 하나 이상 추적합니다. 이 객체들은 원하는 상태를 나타내는 spec 필드를 갖고 있어요. 해당 리소스를 담당하는 컨트롤러는 현재 상태가 그 원하는 상태에 가까워지도록 만드는 책임을 집니다.

컨트롤러가 그 행동을 스스로 수행할 수도 있지만, 쿠버네티스에서 더 흔한 방식은 다릅니다. 컨트롤러가 API 서버에 유용한 부수 효과(side effect)를 가지는 메시지를 보내는 방식이에요. 아래에서 그 예시를 볼게요.

API 서버를 통한 제어

Job 컨트롤러는 쿠버네티스 내장 컨트롤러의 한 예시입니다. 내장 컨트롤러는 클러스터 API 서버와 상호작용하며 상태를 관리합니다.

Job은 태스크를 수행하기 위해 Pod 하나 또는 여러 Pod를 실행한 다음 멈추는 쿠버네티스 리소스입니다.

...

Job 컨트롤러가 새 태스크를 보면, 클러스터 어딘가의 노드들에 있는 kubelet들이 작업을 처리할 만큼 알맞은 수의 Pod를 실행하고 있는지 확인합니다. Job 컨트롤러는 Pod나 컨테이너를 직접 실행하지 않아요. 대신 Job 컨트롤러는 API 서버에게 Pod를 만들거나 제거하라고 지시합니다.

컨트롤 플레인(control plane)의 다른 구성 요소들은 이 새로운 정보(스케줄되고 실행할 새 Pod가 생겼다는 정보)에 따라 행동합니다.

...

새 Job을 만들면 원하는 상태는 그 Job이 완료되는 것입니다. Job 컨트롤러는 해당 Job의 현재 상태를 원하는 상태에 가깝게 만드는데, 그 Job을 위해 원했던 작업을 수행할 Pod를 만들어 Job이 완료에 더 가까워지게 하죠.

컨트롤러는 자신을 설정하는 객체도 업데이트합니다. 예를 들어 Job의 작업이 끝나면 Job 컨트롤러는 그 Job 객체를 업데이트해 Finished로 표시합니다. 이는 어떤 온도조절기(thermostat)가 설정한 온도에 방이 도달했음을 표시하려고 불을 끄는 것과 비슷해요.

직접 제어

Job과는 대조적으로, 어떤 컨트롤러는 클러스터 밖의 것들을 변경해야 합니다.

예를 들어 클러스터에 충분한 노드가 있는지 확인하는 제어 루프를 쓴다면, 그 컨트롤러는 필요할 때 새 노드를 구성하기 위해 현재 클러스터 밖의 무언가가 필요합니다. 외부 상태와 상호작용하는 컨트롤러는 API 서버에서 원하는 상태를 찾아낸 다음, 외부 시스템과 직접 통신해 현재 상태를 그에 가깝게 만듭니다. (실제로 클러스터의 노드를 수평 확장하는 컨트롤러가 있어요.)

여기서 중요한 점은, 컨트롤러가 원하는 상태를 이루기 위해 어떤 변경을 수행하고, 그 다음 현재 상태를 다시 클러스터의 API 서버에 보고한다는 것입니다. 다른 제어 루프들이 그 보고된 데이터를 관찰해 각자의 행동을 취할 수 있죠.

온도조절기 예시로 보면, 방이 아주 추울 때 다른 컨트롤러가 동파 방지 히터를 켤 수도 있어요. 쿠버네티스 클러스터에서는 컨트롤 플레인이 IP 주소 관리 도구, 스토리지 서비스, 클라우드 제공자 API 등의 서비스와 간접적으로 함께 동작합니다 — 쿠버네티스를 확장해서 그런 것들을 구현하는 방식이죠.

원하는 상태와 현재 상태

쿠버네티스는 시스템에 대해 클라우드 네이티브 관점을 취하며, 끊임없는 변화를 처리할 수 있습니다.

작업이 진행됨에 따라 클러스터는 언제든 바뀔 수 있고, 제어 루프가 실패를 자동으로 고칩니다. 이 말은, 잠재적으로 클러스터가 안정적인 상태에 도달하지 못할 수도 있다는 뜻입니다.

...

설계

설계 원칙으로서, 쿠버네티스는 클러스터 상태의 특정 측면을 각각 관리하는 많은 컨트롤러를 사용합니다. 대부분의 경우 특정 제어 루프(컨트롤러)는 원하는 상태로 사용하는 리소스 종류가 하나이고, 그 원하는 상태가 실현되도록 관리하는 리소스 종류가 또 따로 있습니다. 예를 들어 Job용 컨트롤러는 Job 객체를 추적해(새 작업을 발견하려고) Pod 객체를 관리합니다(Job을 실행하고, 작업이 끝났는지 보려고). 이 경우 Job을 만드는 것은 다른 무언가이고, Job 컨트롤러는 Pod를 만듭니다.

서로 얽힌 하나의 거대한 제어 루프 집합보다 단순한 컨트롤러가 유용합니다. 컨트롤러는 실패할 수 있으므로, 쿠버네티스는 그런 상황을 허용하도록 설계되었습니다.

참고:

같은 종류의 객체를 만들거나 업데이트하는 컨트롤러가 여러 개일 수 있습니다. 뒤에서 쿠버네티스 컨트롤러들은 자신이 제어하는 리소스에 연결된 리소스에만 주의를 기울이도록 합니다.

예를 들어 Deployment와 Job을 둘 다 가질 수 있는데, 둘 다 Pod를 만듭니다. Job 컨트롤러는 Deployment가 만든 Pod를 삭제하지 않아요. 컨트롤러들이 그 Pod들을 구분하는 데 쓸 수 있는 정보(라벨)가 있기 때문입니다.

컨트롤러 실행 방식

쿠버네티스는 kube-controller-manager 안에서 실행되는 내장 컨트롤러 집합을 함께 제공합니다. 이 내장 컨트롤러들은 중요한 핵심 동작을 제공합니다. Deployment 컨트롤러와 Job 컨트롤러는 쿠버네티스 자체의 일부로 제공되는("내장" 컨트롤러) 예시입니다.

쿠버네티스는 탄력적인 컨트롤 플레인을 실행할 수 있게 해 주는데, 내장 컨트롤러 중 하나가 실패하더라도 컨트롤 플레인의 다른 부분이 그 작업을 이어받습니다.

컨트롤 플레인 밖에서 실행되어 쿠버네티스를 확장하는 컨트롤러도 찾을 수 있고, 원한다면 스스로 새 컨트롤러를 직접 작성할 수도 있습니다. 자신만의 컨트롤러를 Pod 집합으로 실행하거나 쿠버네티스 외부에서 실행할 수 있어요. 어떤 방식이 가장 적합한지는 그 컨트롤러가 무엇을 하는지에 따라 달라집니다.

...