Certified Kubernetes Administrator (CKA) #2 클러스터 아키텍처 1: Control plane (apiserver/etcd/scheduler/controller-manager)

#1 시험 환경에서 셋업과 시간 관리를 잡았습니다. 이제 본론입니다. CKA는 “클러스터가 어떻게 도는가, 망가지면 어떻게 고치는가"를 묻는 시험이고, 그 답의 출발점이 control plane입니다. control plane은 클러스터의 제어 중심입니다. 어떤 Pod를 어느 노드에 띄울지 결정하고, 원하는 상태와 실제 상태의 차이를 끊임없이 메우며, 모든 변경을 한 곳에서 받아 저장합니다.

이번 글에서는 control plane을 이루는 네 개의 핵심 컴포넌트와 cloud-controller-manager가 각각 무슨 일을 하는지, 이들이 어떻게 static Pod로 떠 있는지, 그리고 하나씩 죽었을 때 클러스터에 어떤 증상이 나타나는지를 운영자 관점으로 풀겠습니다. 여기서 잡는 감각이 #24 Control plane 트러블슈팅#7 etcd 백업과 복구의 토대가 됩니다. 쿠버네티스의 기본 개념이 낯설다면 쿠버네티스 기초 1편을 먼저 읽으면 이 글이 훨씬 잘 들어옵니다.

클러스터는 control plane과 노드로 나뉜다 #

쿠버네티스 클러스터는 크게 두 종류의 머신으로 나뉩니다. 클러스터를 통제하는 control plane 노드와, 실제 워크로드(Pod)가 돌아가는 워커 노드입니다. 이번 글은 control plane만 다루고, 워커 노드의 kubelet과 kube-proxy, CRI는 #3에서 이어 가겠습니다.

control plane이 하는 일을 한 문장으로 요약하면 이렇습니다. 사용자가 선언한 **원하는 상태(desired state)**를 받아 저장하고, 그 상태를 실제 클러스터에 끊임없이 반영합니다. 이 일을 다섯 개의 컴포넌트가 나눠 맡습니다.

컴포넌트한 줄 역할죽으면
kube-apiserver모든 통신의 관문. 인증,인가,admission을 거쳐 etcd에 기록클러스터를 조작할 수 없음
etcd클러스터 상태를 담는 key-value 저장소상태를 읽지도 쓰지도 못함
kube-scheduler새 Pod를 어느 노드에 둘지 결정새 Pod가 Pending에 머묾
kube-controller-manager여러 컨트롤러를 돌려 원하는 상태로 수렴자가 치유가 멈춤
cloud-controller-manager클라우드 공급자 연동(노드,LB,라우팅)클라우드 연동이 멈춤

이 다섯이 어떻게 맞물리는지를 이해하면, 장애가 났을 때 “어느 컴포넌트의 증상인가"를 바로 짚을 수 있습니다. 시험의 Troubleshooting도메인이 30%인 이유가 여기에 있습니다.

kube-apiserver: 모든 통신의 관문 #

kube-apiserver는 control plane의 정문입니다. 클러스터에 대한 모든 요청은 예외 없이 apiserver를 거칩니다. kubectl 명령, 다른 control plane 컴포넌트, 노드의 kubelet, 컨트롤러 전부가 apiserver의 REST API로 대화합니다. 즉 apiserver는 클러스터에서 etcd에 직접 접근할 수 있는 유일한 컴포넌트이기도 합니다. 나머지는 모두 apiserver를 통해서만 상태를 읽고 씁니다.

요청이 들어오면 apiserver는 세 관문을 차례로 통과시킵니다.

  1. Authentication(인증). 요청을 보낸 주체가 누구인지 확인합니다. 인증서, 토큰, ServiceAccount 등으로 신원을 판별합니다.
  2. Authorization(인가). 그 주체가 해당 동작을 할 권한이 있는지 확인합니다. CKA에서는 주로 RBAC으로 결정되며, #9에서 깊이 다룹니다.
  3. Admission control(승인 제어). 인증,인가를 통과한 요청을 실제로 etcd에 쓰기 전에 검증하거나 변형합니다. 기본값 주입, 정책 강제 등이 여기서 일어납니다.

이 세 관문을 모두 통과한 요청만 etcd에 기록됩니다. apiserver의 동작 방식과 실행 인자는 static Pod 매니페스트로 확인할 수 있습니다.

# apiserver static Pod 매니페스트
cat /etc/kubernetes/manifests/kube-apiserver.yaml

매니페스트의 command 섹션에는 --etcd-servers, --client-ca-file, --authorization-mode 같은 핵심 플래그가 나열되어 있습니다. CKA에서 apiserver가 뜨지 않는 문제는 대부분 이 플래그나 인증서 경로의 오타에서 비롯됩니다.

etcd: 클러스터 상태의 단일 진실원 #

etcd는 분산 key-value 저장소이며, 클러스터의 모든 상태가 저장되는 단 하나의 장소입니다. 어떤 Deployment가 있고, 어떤 Pod가 어느 노드에 배치되어 있으며, 어떤 ConfigMap과 Secret이 있는지가 전부 etcd에 담깁니다. apiserver를 통해 우리가 만든 모든 오브젝트의 최종 종착지가 etcd입니다.

여기서 핵심 운영 원칙이 나옵니다. etcd를 백업하면 클러스터 상태 전체를 백업하는 것입니다. 반대로 etcd가 손상되면 클러스터의 기억이 통째로 사라집니다. 그래서 etcd 스냅샷 저장과 복구가 CKA에서 비중 있게 출제되며, 이는 #7에서 전담하겠습니다.

# etcd 멤버 상태 확인 (인증서 경로는 apiserver 매니페스트에서 확인)
ETCDCTL_API=3 etcdctl member list \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

kubeadm으로 설치한 클러스터에서 etcd는 control plane 노드 위에 stacked 형태로 함께 뜨는 것이 기본입니다. 가용성을 높이려면 etcd를 별도 머신으로 분리한 외부 etcd cluster 구성을 쓰며, 이는 #5 HA 클러스터에서 다룹니다.

kube-scheduler: Pod 배치 결정 #

kube-scheduler는 아직 노드가 정해지지 않은 새 Pod를 보고, 어느 노드에 둘지 결정하는 컴포넌트입니다. 여기서 중요한 오해를 짚겠습니다. scheduler는 Pod를 직접 노드에 띄우지 않습니다. scheduler가 하는 일은 오직 결정입니다. 적합한 노드를 골라 Pod의 nodeName 필드를 채우는 것까지가 scheduler의 역할이고, 실제로 컨테이너를 실행하는 것은 해당 노드의 kubelet입니다.

배치 결정은 두 단계로 이뤄집니다.

  1. Filtering(필터링). Pod가 요구하는 자원과 제약 조건을 만족하지 못하는 노드를 후보에서 제외합니다. requests를 충족할 자원이 없거나, taint를 견디지 못하거나, nodeSelector에 맞지 않는 노드가 떨어집니다.
  2. Scoring(점수 매기기). 남은 후보 노드에 점수를 매겨 가장 적합한 노드를 고릅니다.

이 결정에 영향을 주는 nodeSelector, affinity, taints/tolerations는 #13#14에서 본격적으로 다룹니다. scheduler가 멈추면 새 Pod는 노드를 배정받지 못해 Pending 상태로 남습니다. 이미 돌아가던 Pod는 영향을 받지 않습니다.

kube-controller-manager: reconciliation loop의 집합 #

kube-controller-manager는 여러 개의 컨트롤러를 하나의 프로세스로 묶어 돌리는 컴포넌트입니다. 각 컨트롤러는 한 가지 책임을 맡아 원하는 상태와 실제 상태를 끊임없이 비교하고, 차이가 있으면 메우는 루프를 돕니다. 이 루프를 reconciliation loop라고 부릅니다.

대표적인 컨트롤러는 다음과 같습니다.

컨트롤러책임
Node controller노드가 응답하지 않으면 NotReady로 표시하고 후속 처리
ReplicaSet controller지정한 replica 수만큼 Pod가 떠 있도록 유지
Deployment controllerrolling update와 rollback을 ReplicaSet으로 조율
Job controllerJob이 지정 횟수만큼 완료되도록 Pod를 생성
ServiceAccount/Token controller새 네임스페이스에 기본 ServiceAccount 생성
Endpoints controllerService와 Pod를 연결하는 EndpointSlice 갱신

예를 들어 replica가 3인 Deployment에서 Pod 하나가 죽으면, ReplicaSet controller가 “원하는 상태 3, 실제 상태 2"라는 차이를 감지하고 새 Pod를 만들어 다시 3으로 맞춥니다. 이 자가 치유가 controller-manager의 핵심 가치입니다. controller-manager가 멈추면 이 루프가 모두 정지하므로, 죽은 Pod가 다시 생성되지 않고 Deployment 변경도 반영되지 않습니다.

cloud-controller-manager: 클라우드 연동 분리 #

cloud-controller-manager는 쿠버네티스를 특정 클라우드 공급자와 연결하는 클라우드 의존 로직을 별도 컴포넌트로 떼어 낸 것입니다. 노드가 실제 클라우드 인스턴스로 존재하는지 확인하고, LoadBalancer 타입 Service를 위해 클라우드 로드밸런서를 생성하며, 노드에 라우팅을 설정하는 일을 맡습니다.

온프레미스나 베어메탈, 그리고 CKA 연습용 로컬 클러스터에는 cloud-controller-manager가 없는 경우가 많습니다. 클라우드 연동이 필요 없으면 이 컴포넌트도 필요 없기 때문입니다. 시험에서는 이런 분리 개념을 이해하는 수준이면 충분합니다.

static Pod로 뜨는 control plane #

kubeadm으로 설치한 클러스터에서 control plane 컴포넌트는 static Pod로 실행됩니다. static Pod는 apiserver나 scheduler가 관리하지 않습니다. 노드의 kubelet이 특정 디렉터리의 매니페스트 파일을 직접 읽어 그 Pod를 실행하고 관리합니다. 그 디렉터리가 /etc/kubernetes/manifests/입니다.

# control plane static Pod 매니페스트 목록
ls /etc/kubernetes/manifests/
# etcd.yaml
# kube-apiserver.yaml
# kube-controller-manager.yaml
# kube-scheduler.yaml

여기에 닭과 달걀의 문제가 있습니다. apiserver가 떠 있어야 Pod를 만들 수 있는데, apiserver 자체가 Pod라면 누가 그것을 띄울까요. 답은 kubelet입니다. kubelet은 apiserver 없이도 이 디렉터리의 파일만 보고 control plane Pod를 부트스트랩합니다. 그래서 control plane을 static Pod로 두는 것입니다.

운영 관점에서 가장 중요한 성질은 이것입니다. kubelet은 이 디렉터리를 감시하다가, 파일이 바뀌면 해당 Pod를 자동으로 재기동합니다. 즉 apiserver의 플래그 하나를 고치려면 kubectl edit이 아니라 이 매니페스트 파일을 직접 수정합니다. 저장하는 순간 kubelet이 변경을 감지해 Pod를 다시 띄웁니다.

# 예: apiserver 플래그 수정 후 kubelet이 자동 재기동
vim /etc/kubernetes/manifests/kube-apiserver.yaml
# 저장 직후 kubelet이 변경을 감지해 apiserver Pod를 재생성

여기서 흔한 실수가 나옵니다. 매니페스트의 YAML 들여쓰기를 잘못 만들거나 플래그를 오타 내면, kubelet이 잘못된 매니페스트로 Pod를 띄우려다 실패하고 apiserver 자체가 올라오지 못합니다. 이때는 kubectl이 통하지 않으므로, 노드에 들어가 journalctl과 컨테이너 런타임 로그로 원인을 추적해야 합니다. 이 시나리오가 바로 #24의 핵심입니다.

static Pod에는 한 가지 표시가 더 있습니다. static Pod는 apiserver에도 “미러 Pod"로 보이는데, 이름 끝에 노드 이름이 붙습니다. 예를 들어 kube-apiserver-controlplane처럼 표시됩니다.

선언형 모델과 reconciliation #

지금까지의 컴포넌트가 어떻게 협력하는지를 하나의 원리로 묶으면 쿠버네티스의 핵심 철학이 보입니다. 쿠버네티스는 선언형(declarative) 모델로 동작합니다. 사용자는 “Pod를 만들어라, 그다음 노드에 배치해라” 같은 절차를 지시하지 않습니다. 대신 “replica 3개가 있는 이 Deployment가 존재하기를 원한다"는 원하는 상태만 선언합니다. 그 선언은 apiserver를 거쳐 etcd에 저장됩니다. 그러면 scheduler와 여러 컨트롤러가 각자의 reconciliation loop를 돌며 실제 상태를 그 선언에 끊임없이 수렴시킵니다. 노드가 죽어 Pod가 사라지면 컨트롤러가 다시 만들고, scheduler가 새 노드에 배치합니다. 사용자가 다시 개입할 필요가 없습니다. control plane을 운영한다는 것은 결국 이 수렴 루프가 건강하게 돌도록 지키는 일입니다.

control plane 컴포넌트 장애 영향 #

각 컴포넌트가 죽었을 때 클러스터에 나타나는 증상을 정확히 알아야 트러블슈팅이 빨라집니다. 핵심은 이미 돌아가던 워크로드와 새로운 조작을 구분하는 것입니다.

죽은 컴포넌트증상이미 뜬 Pod는
kube-apiserverkubectl이 통하지 않음. 클러스터를 읽지도 바꾸지도 못함계속 동작
etcdapiserver가 상태를 읽고 쓰지 못해 사실상 클러스터 조작 불가계속 동작
kube-scheduler새 Pod가 노드를 배정받지 못해 Pending에 머묾계속 동작
kube-controller-manager자가 치유 정지. 죽은 Pod 미복구, replica,Deployment 변경 미반영계속 동작
cloud-controller-manager새 LoadBalancer Service 미생성, 노드 라우팅,정리 지연계속 동작

표에서 읽어야 할 핵심은 control plane이 죽어도 이미 떠 있는 Pod는 계속 동작한다는 점입니다. 노드의 kubelet과 컨테이너 런타임은 control plane과 무관하게 돌아가기 때문입니다. control plane 장애는 “새로운 변경을 받아 반영하는 능력"을 잃는 것이지, 현재 트래픽을 즉시 끊는 것이 아닙니다. 단, apiserver나 etcd가 길게 죽어 있으면 결국 자가 치유와 모든 조작이 멈추므로 빠른 복구가 필요합니다.

control plane 상태 확인 명령 #

CKA 시험과 실무에서 control plane의 건강 상태를 확인하는 명령은 손에 익혀 두어야 합니다. kubeadm 클러스터의 control plane은 kube-system 네임스페이스에 static Pod로 떠 있으므로 다음으로 한눈에 봅니다.

# control plane 컴포넌트 Pod 상태
k get pods -n kube-system

# control plane 컴포넌트만 추려서 보기
k get pods -n kube-system -l tier=control-plane

컴포넌트의 헬스를 직접 묻는 명령도 있습니다. componentstatuses는 오래된 API이지만 시험에서 여전히 마주칩니다.

# scheduler/controller-manager/etcd 헬스 (구 API, cs로 축약)
k get componentstatuses
k get cs

특정 컴포넌트가 왜 안 뜨는지 추적할 때는 Pod 로그와 매니페스트를 함께 봅니다.

# apiserver Pod 로그
k logs -n kube-system kube-apiserver-controlplane

# apiserver가 죽어 kubectl이 안 통하면 노드에서 직접
sudo crictl ps -a | grep apiserver
sudo journalctl -u kubelet -f

# 문제의 static Pod 매니페스트 직접 열람
cat /etc/kubernetes/manifests/kube-apiserver.yaml

마지막 패턴이 중요합니다. apiserver가 죽으면 k logs조차 통하지 않으므로, 노드에 들어가 crictl로 컨테이너를 보고 journalctl로 kubelet 로그를 추적한 뒤 매니페스트를 직접 고치는 흐름으로 갑니다.

시험 포인트 #

  • apiserver는 모든 통신의 유일한 관문입니다. 모든 요청은 인증,인가,admission을 거쳐 etcd에 기록되며, etcd에 직접 접근하는 컴포넌트는 apiserver뿐입니다.
  • etcd는 클러스터 상태의 단일 진실원입니다. etcd 백업이 곧 클러스터 상태 백업이며, etcd 복구는 #7의 핵심 출제 영역입니다.
  • scheduler는 결정만, kubelet이 실행합니다. scheduler가 죽으면 새 Pod가 Pending에 머뭅니다.
  • controller-manager는 reconciliation loop의 집합입니다. 죽으면 자가 치유가 멈춰 죽은 Pod가 복구되지 않습니다.
  • control plane은 /etc/kubernetes/manifests/의 static Pod입니다. 파일을 수정하면 kubelet이 자동으로 Pod를 재기동하므로, 플래그 수정은 이 매니페스트를 직접 편집합니다.
  • 컴포넌트가 죽어도 이미 뜬 Pod는 계속 동작합니다. 증상으로 어느 컴포넌트의 문제인지 역추적하는 감각이 트러블슈팅의 출발점입니다.
  • k get pods -n kube-system, k get cs, crictl ps -a, journalctl -u kubelet을 손에 익혀 둡니다.

정리 #

이번 글에서 잡은 것:

  • control plane은 클러스터의 제어 중심입니다. apiserver,etcd,scheduler,controller-manager,cloud-controller-manager가 역할을 나눠 맡습니다.
  • apiserver는 관문, etcd는 저장소, scheduler는 배치 결정, controller-manager는 수렴 루프, cloud-controller-manager는 클라우드 연동입니다.
  • kubeadm 클러스터의 control plane은 /etc/kubernetes/manifests/static Pod로 뜨며, kubelet이 직접 관리하고 파일 변경 시 자동 재기동합니다.
  • 컴포넌트별 장애 증상을 알면 “어디가 망가졌는가"를 빠르게 짚을 수 있습니다. 이미 뜬 Pod는 control plane 장애에도 계속 동작합니다.
  • 쿠버네티스는 선언형 모델로 동작하며, 원하는 상태를 실제 상태로 수렴시키는 reconciliation이 운영의 본질입니다.

다음: 클러스터 아키텍처 2 #

control plane이라는 두뇌를 봤으니, 이제 실제로 손발을 움직이는 쪽으로 내려갑니다. #3 클러스터 아키텍처 2에서는 워커 노드의 kubelet(노드 위에서 Pod를 실제로 실행하는 에이전트), kube-proxy(Service 트래픽을 라우팅), CRI(컨테이너 런타임 인터페이스)가 각각 무슨 일을 하는지, 그리고 Pod가 어떻게 서로 통신하는 Pod 네트워킹 모델을 직접 들여다보며 정리하겠습니다.

X