LabHub

CKA — 쿠버네티스 관리자 · 클러스터 아키텍처·설치·설정 · 이론

컨트롤 플레인은 왜 네 개로 쪼개졌나

LabHub 에서 이어서 보기

한 줄 요약

쿠버네티스 컨트롤 플레인은 etcd, kube-apiserver, kube-controller-manager, kube-scheduler 네 프로세스로 나뉘어 있고, 그 중 etcd 와 말하는 것은 apiserver 하나뿐입니다. 이 한 줄이 CKA 트러블슈팅의 절반을 설명합니다.

왜 이게 필요했나

상상해 봅시다. 스케줄러가 etcd 에 직접 붙어서 파드 오브젝트를 읽고 nodeName 을 써 넣는다고요. 그러면 이런 일이 벌어집니다.

그래서 쿠버네티스는 apiserver 를 유일한 관문으로 두었습니다. apiserver 는 상태를 만들지 않습니다. 받고, 검증하고, 인가하고, 저장하고, 지켜보는 사람들에게 알려줄 뿐입니다. 실제 판단은 전부 바깥의 컨트롤러들이 합니다.

어떻게 동작하나

컨트롤러의 reconcile 루프는 이렇게 생겼습니다.

1. watch 로 선언된 상태(spec)를 읽는다
2. 실제 상태(status)를 관측한다
3. 차이를 계산해 그만큼만 API 를 호출한다
4. 다시 1번으로

명령이 아니라 차이 줄이기라는 점이 중요합니다. 그래서 이벤트를 하나 놓쳐도 다음 리싱크에서 수렴하고, 컨트롤러를 재시작해도 처음부터 다시 맞춰 갑니다. Deployment 컨트롤러는 ReplicaSet 을 만들고, ReplicaSet 컨트롤러는 파드를 만듭니다. 각자 자기 층만 봅니다.

스케줄러는 두 단계로 동작합니다.

| 단계 | 하는 일 | 결과 |
| --- | --- | --- |
| filter (predicate) | 리소스 부족, 테인트 미허용, nodeSelector 불일치, 볼륨 존 불일치 노드를 제거 | 배치 가능한 노드 목록 |
| score (priority) | 남은 노드에 점수를 매긴다 (자원 균형, 이미지 지역성, 토폴로지 분산) | 최고점 노드 하나 |

0/3 nodes are available: 3 Insufficient cpu 같은 메시지는 filter 단계의 탈락 사유 집계표입니다. 이 문장을 읽을 줄 알면 Pending 파드의 90%는 그 자리에서 끝납니다.

현장에서 만나는 모습

사례 1 — DHCP 가 클러스터를 죽였다. 홈랩 컨트롤 플레인의 IP 가 10.0.0.111 에서 10.0.0.120 으로 바뀐 적이 있습니다. DHCP 임대 갱신 때문이었습니다. 증상은 dial tcp 10.0.0.111:6443: connect: no route to host 였고, 진짜 원인은 인증서에 있었습니다. apiserver 인증서의 SAN 에 IP Address:10.0.0.111 만 있고 .120 이 없었던 것입니다. 여기서 재미있는 것은 컴포넌트별 반응 차이였습니다. etcd 와 kube-apiserver 는 존재하지 않는 .111 에 바인드하려다 CrashLoopBackOff 에 빠졌고, kube-scheduler 와 controller-manager 는 127.0.0.1 에 바인드하므로 프로세스는 살아 있되 아무 일도 못 하는 상태였습니다. 네 프로세스가 각자 다른 주소에 붙는다는 설계를 알아야 이 그림이 읽힙니다.

사례 2 — etcd 쿼럼과 API 가용성은 별개다. 같은 홈랩을 3노드에서 7노드로 늘리면서 컨트롤 플레인을 3대로 만들었습니다. etcdctl member list 에 멤버 3개가 정확히 찍혔고, 각 노드에 apiserver, scheduler, controller-manager 가 하나씩 돌았습니다. HA 가 완성된 것처럼 보였습니다. 아닙니다.

controlPlaneEndpoint: 10.0.0.120:6443     # cp-1 의 물리 IP

이 값이 VIP 나 DNS 가 아니라 첫 노드의 실제 IP 였습니다. 그래서 cp-1 이 죽으면 etcd 쿼럼은 2/3 로 멀쩡하고 cp-2, cp-3 의 apiserver 도 정상 동작하는데, kubectl 과 7개 노드의 kubelet 이 전부 접속 불가가 됩니다. 게다가 인증서 SAN 에 다른 컨트롤 플레인의 IP 가 없어 cp-2 로 직접 붙어도 TLS 검증이 실패합니다. 데이터 가용성과 접근 가용성은 다른 문제입니다.

덧붙여, 컨트롤 플레인을 2대까지만 늘리는 것은 1대보다 더 위험합니다. 멤버 2의 과반은 2이므로 아무 한 대만 죽어도 쿼럼을 잃습니다. 홀수(1, 3, 5)가 권장되는 이유이고, 그래서 이 확장도 반드시 3까지 갔습니다.

다음 실습에서 할 것

첫 실습에서는 클러스터를 조사하고, 네임스페이스·라벨·애너테이션을 다루고, kubeconfig 컨텍스트를 직접 만들어 전환하고, kubectl explain--dry-run=client -o yaml 로 매니페스트를 뽑아 냅니다. 두 번째 실습에서는 CustomResourceDefinition 을 직접 써서 API 를 넓혀 봅니다. 스키마 검증이 실제로 요청을 거부하는 순간을 눈으로 확인하는 것이 목표입니다.