Kubernetes, Control Plane
Control Plane은 Desired State를 받아들이고, Actual State를 감시하며, 두 상태를 일치시키기 위한 결정을 내린다.
Architecture
flowchart TB
User["kubectl / API 클라이언트"] --> API["kube-apiserver"]
subgraph "Control Plane"
API <-->|읽기/쓰기| etcd[(etcd)]
SCH["kube-scheduler"] -->|Watch & Bind| API
CM["kube-controller-manager"] -->|Watch & Update| API
CCM["cloud-controller-manager"] -->|Watch & Update| API
end
subgraph "Worker Node"
kubelet -->|Watch & Report| API
kp["kube-proxy"] -->|Watch| API
end
모든 컴포넌트는 kube-apiserver를 통해서만 통신한다.
etcd에 직접 접근하는 컴포넌트는 kube-apiserver뿐이다.
이 중앙 집중식 설계가 Kubernetes의 보안과 일관성을 보장한다.
kube-apiserver
kube-apiserver는 Kubernetes의 유일한 진입점이다.
모든 컴포넌트(kubectl, kubelet, scheduler, controller)는 API Server의 RESTful API를 통해 클러스터와 상호작용한다.
API 요청 처리 파이프라인
모든 API 요청은 이 파이프라인을 순차적으로 통과한다.
1. Authentication (인증)
요청자가 누구인지 확인한다. 여러 인증 방식을 동시에 사용할 수 있다:
- X.509 클라이언트 인증서
- Bearer Token (ServiceAccount Token)
- OIDC (OpenID Connect)
- Webhook Token Authentication
2. Authorization (인가)
인증된 사용자가 해당 작업을 수행할 권한이 있는지 확인한다:
- RBAC(Role-Based Access Control): 가장 널리 사용되는 방식. Role/ClusterRole과 RoleBinding/ClusterRoleBinding으로 권한을 정의한다
- Node Authorization: kubelet의 API 접근을 제한한다
- Webhook Authorization: 외부 서비스에 권한 판단을 위임한다
3. Admission Control (승인 제어)
요청을 수정(Mutating)하거나 거부(Validating)할 수 있는 플러그인 체인이다.
flowchart LR
REQ["요청"] --> M1["Mutating<br/>Admission"]
M1 --> M2["Mutating<br/>Webhook"]
M2 --> V1["Validating<br/>Admission"]
V1 --> V2["Validating<br/>Webhook"]
V2 --> OK["승인"]
-
Mutating Admission: 요청을 수정한다 (기본값 주입, 사이드카 주입 등)
-
Validating Admission: 요청을 검증하고 거부할 수 있다 (보안 정책 등)
Watch 메커니즘
컴포넌트들은 API Server에 Watch 요청을 보내고, 리소스 변경 시 실시간 알림을 받는다.
sequenceDiagram
participant Client as kubelet / controller
participant API as kube-apiserver
participant WC as Watch Cache
participant etcd
Client->>API: Watch(resourceVersion=100)
API->>WC: 캐시에서 이벤트 조회
Note over WC: 슬라이딩 윈도우에<br/>최근 이벤트 보관
WC-->>Client: ADDED event (rv=101)
WC-->>Client: MODIFIED event (rv=102)
Note over etcd: 새로운 변경 발생
etcd-->>WC: 이벤트 전파
WC-->>Client: DELETED event (rv=103)
Watch Cache:
-
API Server는 etcd의 부하를 줄이기 위해 Watch Cache를 유지한다
-
리소스 타입별로 독립된 캐시 인스턴스가 존재한다
-
슬라이딩 윈도우 방식으로 최근 이벤트를 보관하여 Watch 요청에 응답한다
-
LIST 요청도 Watch Cache에서 직접 응답할 수 있어 etcd 부하를 줄여준다
etcd
etcd는 Kubernetes 클러스터의 모든 상태 데이터를 저장하는 분산 키-값 저장소이다.
Pod, Service, ConfigMap, Secret 등 모든 리소스 정보가 etcd에 저장된다.
Raft 알고리즘으로 데이터 일관성을 보장하고, 모든 읽기가 가장 최신의 쓰기를 반영한다.
Watch 를 지원하기 때문에 키의 변경 사항을 실시간으로 구독할 수 있다.
flowchart LR
Client["API Server"] --> Leader["Leader"]
Leader -->|로그 복제| F1["Follower 1"]
Leader -->|로그 복제| F2["Follower 2"]
Leader -->|로그 복제| F3["Follower 3"]
Leader -->|로그 복제| F4["Follower 4"]
etcd는 계층적 키-값 구조로 Kubernetes 리소스를 저장한다.
/registry/pods/default/nginx-abc123
/registry/pods/kube-system/coredns-xyz456
/registry/services/specs/default/my-service
/registry/deployments/default/my-app
/registry/secrets/default/my-secret
kube-scheduler
kube-scheduler는 새로 생성된 Pod를 어느 노드에 배치할지 결정하는 컴포넌트이다.
Pod가 생성되면 spec.nodeName이 비어있는 상태로 API Server에 등록되고, scheduler가 적절한 노드를 찾아 바인딩한다.
flowchart LR
Q["스케줄링 큐"] --> Filter["Filtering<br />(필터링)"]
Filter --> Score["Scoring<br />(점수화)"]
Score --> Bind["Binding<br />(바인딩)"]
Filter -.->|"부적합 노드 제거"| F1["Node A ✗"]
Filter -.->|"부적합 노드 제거"| F2["Node B ✗"]
Score -.->|"점수 비교"| S1["Node C: 80점"]
Score -.->|"점수 비교"| S2["Node D: 95점 ★"]
Node Affinity
Pod가 특정 노드에 배치되도록 요청한다.
Pod Affinity / Anti-Affinity
Pod가 다른 Pod와 같은/다른 노드에 배치되도록 요청한다.
-
Affinity: 관련 Pod끼리 같은 노드/존에 배치 (캐시 활용, 지연 시간 감소)
-
Anti-Affinity: 같은 Pod를 다른 노드/존에 분산 (고가용성)
Taints & Tolerations
노드가 특정 Pod를 거부하는 메커니즘이다.
-
Taint: 노드에 설정. “나는 이런 Pod를 거부한다”
-
Toleration: Pod에 설정. “나는 이 Taint를 허용한다”
Priority와 Preemption
-
Priority: PriorityClass로 Pod의 중요도를 정의한다
-
Preemption: 높은 우선순위 Pod가 스케줄링될 수 없을 때, 낮은 우선순위 Pod를 축출하여 자리를 만든다
kube-controller-manager
kube-controller-manager는 여러 Controller를 하나의 프로세스에서 실행하는 컴포넌트이다.
각 Controller는 클러스터의 현재 상태를 원하는 상태로 수렴시키는 Reconciliation Loop(조정 루프)를 실행한다.
Controller 패턴
flowchart TB
Observe["관찰 (Observe)<br />API Server Watch로<br />현재 상태 파악"]
Diff["비교 (Diff)<br />현재 상태 vs<br />원하는 상태"]
Act["조치 (Act)<br />차이를 해소하는<br />API 호출"]
Observe --> Diff --> Act --> Observe
사용자가 “원하는 상태”를 선언하면, Controller가 지속적으로 현재 상태를 관찰하고 원하는 상태로 수렴시킨다.
주요 빌트인 Controller
| Controller | 관찰 대상 | 하는 일 |
|---|---|---|
| ReplicaSet | Pod 수 | 원하는 Pod 수를 유지 (부족하면 생성, 초과하면 삭제) |
| Deployment | ReplicaSet | Rolling Update, Rollback 관리 |
| StatefulSet | Pod | 순서 보장 생성/삭제, 안정적 네트워크 ID |
| DaemonSet | Node, Pod | 모든 (또는 선택된) 노드에 Pod 1개씩 유지 |
| Job | Pod | 완료될 때까지 Pod 실행, 실패 시 재시도 |
| CronJob | 시간 | 스케줄에 따라 Job 생성 |
| Node | Node 상태 | 노드 하트비트 모니터링, NotReady 노드의 Pod 축출 |
| ServiceAccount | Namespace | 새 Namespace에 기본 ServiceAccount 생성 |
| EndpointSlice | Service, Pod | Service 변경 시 EndpointSlice 갱신 |
| Namespace | Namespace | Namespace 삭제 시 하위 리소스 정리 |
cloud-controller-manager
cloud-controller-manager는 클라우드 프로바이더 고유 로직을 Kubernetes 코어에서 분리하기 위한 컴포넌트이다.
베어메탈 클러스터처럼 클라우드 환경이 아닌 경우에는 cloud-controller-manager가 필요 없다.
원래 클라우드 관련 로직은 kube-controller-manager 안에 포함되어 있었다. 하지만 이 구조에서는 클라우드 프로바이더가 Kubernetes 릴리스 주기에 종속되는 문제가 있었다. cloud-controller-manager로 분리함으로써, 클라우드 프로바이더는 Kubernetes 코어와 독립적으로 기능을 개발하고 릴리스할 수 있게 되었다.
내장 Controller
-
Node Controller: 클라우드 API를 통해 노드의 존재 여부를 확인하고, 클라우드에서 삭제된 인스턴스를 클러스터에서 제거한다
-
Route Controller: 클라우드 인프라에 적절한 네트워크 라우트를 설정하여 노드 간 Pod 통신을 가능하게 한다
-
Service Controller:
type: LoadBalancerService 생성 시 클라우드 로드 밸런서를 프로비저닝하고, 삭제 시 해제한다