Cluster Architecture
모든 통신은 API Server를 경유한다 — K8s 내부 동작의 핵심
📌 이 글의 목적
K8s 클러스터의 내부 구조를 컴포넌트 단위로 이해하는 것이 목표. 각 컴포넌트가 무슨 일을 하고, 어떻게 협력하는지를 알면 트러블슈팅, HA 설계, CKA 시험의 기반이 됨.
1. Control Plane 컴포넌트
1.1 API Server (kube-apiserver)
K8s의 중앙 허브. 모든 내부/외부 통신이 API Server를 경유함.
| 역할 | 설명 |
|---|---|
| REST API | 모든 오브젝트의 CRUD를 REST API로 제공 |
| 인증/인가 | 요청자가 누구인지(인증), 무엇을 할 수 있는지(인가, RBAC) 확인 |
| Admission Control | 요청을 최종 검증/변환 (Webhook, ResourceQuota 등) |
| etcd 접근 | 유일하게 etcd와 직접 통신하는 컴포넌트 |
flowchart TB
kubectl["kubectl"] --> Auth["1. 인증<br/>(X.509, Token, OIDC)"]
Auth --> Authz["2. 인가<br/>(RBAC)"]
Authz --> Admission["3. Admission Control<br/>(Webhook, Validation)"]
Admission --> etcd_write["4. etcd에 저장"]
요청 처리 순서:
- 인증(Authentication) — 요청자가 누구인가 (인증서, 토큰)
- 인가(Authorization) — 이 사용자가 이 작업을 할 수 있는가 (RBAC)
- Admission Control — 정책 검증 (ResourceQuota 초과?, 필수 레이블 있는가?)
- etcd에 저장 — 검증 통과 후 상태 저장
1.2 etcd
분산 Key-Value 저장소. K8s의 모든 클러스터 상태가 저장됨.
| 항목 | 설명 |
|---|---|
| 합의 알고리즘 | Raft — 리더 선출, 로그 복제, 과반수 합의 |
| 데이터 | 모든 K8s 오브젝트 (Pod, Service, ConfigMap, Secret...) |
| 접근 | API Server만 접근 (다른 컴포넌트는 API Server 경유) |
| 권장 노드 수 | 3, 5, 7 (홀수 — 과반수 합의를 위해) |
etcd와 Proxmox Corosync 비교:
| etcd (K8s) | Corosync (Proxmox) | |
|---|---|---|
| 역할 | 클러스터 상태 저장 | 멤버십, 메시징, 쿼럼 |
| 합의 | Raft | Totem (링 기반) |
| 데이터 | Key-Value (모든 오브젝트) | 설정 파일 (pmxcfs) |
| 쿼럼 | 과반수 (3노드 → 2) | 과반수 (3노드 → 2) |
etcd 토폴로지:
| 토폴로지 | 설명 | 추천 |
|---|---|---|
| Stacked | etcd가 Control Plane 노드에 같이 동작 | ✅ 간편, 소~중규모 |
| External | etcd가 별도 서버에서 동작 | 대규모, 더 안정적 |
flowchart TB
subgraph Stacked["Stacked etcd"]
M1["Master 1<br/>API Server + etcd"]
M2["Master 2<br/>API Server + etcd"]
M3["Master 3<br/>API Server + etcd"]
end
subgraph External_etcd["External etcd"]
MA["Master 1<br/>API Server"]
MB["Master 2<br/>API Server"]
MC["Master 3<br/>API Server"]
E1["etcd 1"]
E2["etcd 2"]
E3["etcd 3"]
MA & MB & MC --> E1 & E2 & E3
end
etcd 백업/복원 — CKA 시험 필출:
# 스냅샷 백업
ETCDCTL_API=3 etcdctl snapshot save /tmp/etcd-backup.db \
--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
# 스냅샷 복원
ETCDCTL_API=3 etcdctl snapshot restore /tmp/etcd-backup.db \
--data-dir=/var/lib/etcd-restored
# 복원 후 etcd의 data-dir을 변경하고 재시작
1.3 kube-scheduler
Pending 상태의 Pod를 어떤 노드에 배치할지 결정.
flowchart LR
Pending["Pending Pod"] --> Filter["필터링<br/>(부적합 노드 제거)"]
Filter --> Score["스코어링<br/>(적합 노드 순위)"]
Score --> Bind["바인딩<br/>(노드 배정)"]
| 단계 | 역할 | 예시 |
|---|---|---|
| 필터링 | 조건을 못 맞추는 노드 제거 | CPU/메모리 부족, Taint 불일치, nodeSelector 불일치 |
| 스코어링 | 남은 노드에 점수 부여 | 리소스 여유, Affinity 일치도, 균등 분산 |
| 바인딩 | 최고 점수 노드에 Pod 배정 | API Server에 업데이트 |
💡 VMware DRS와 유사한 역할이지만, DRS는 이미 실행 중인 VM을 재배치(밸런싱)하고, K8s Scheduler는 새 Pod의 최초 배치만 담당함. 실행 중인 Pod를 자동으로 옮기지는 않음(Descheduler 별도 설치 필요).
1.4 kube-controller-manager
선언된 상태와 현재 상태를 일치시키는 컨트롤 루프 모음.
flowchart LR
Observe["현재 상태 관찰"] --> Compare["선언된 상태와 비교"]
Compare -->|"불일치"| Act["조정 (Pod 생성/삭제 등)"]
Act --> Observe
Compare -->|"일치"| Observe
| 컨트롤러 | 역할 |
|---|---|
| Deployment Controller | Deployment → ReplicaSet 관리 |
| ReplicaSet Controller | 원하는 수만큼 Pod 유지 |
| Node Controller | 노드 상태 감시, NotReady 노드의 Pod 축출 |
| Job Controller | Job/CronJob 관리 |
| Endpoint Controller | Service ↔ Pod 매핑 |
| ServiceAccount Controller | 네임스페이스별 기본 SA 생성 |
1.5 cloud-controller-manager
클라우드 프로바이더(AWS, Azure, GCP)와 연동하는 컨트롤러. 온프레미스에서는 사용하지 않음.
| 역할 | 예시 |
|---|---|
| 노드 관리 | 클라우드 VM 상태와 K8s 노드 동기화 |
| 로드밸런서 | Service type=LoadBalancer → 클라우드 LB 생성 |
| 라우팅 | 클라우드 네트워크 라우팅 테이블 관리 |
2. Worker Node 컴포넌트
2.1 kubelet
노드의 에이전트. API Server로부터 Pod 스펙을 받아 컨테이너를 실행/관리함.
| 역할 | 설명 |
|---|---|
| Pod 관리 | API Server에서 Pod 스펙 수신 → CRI를 통해 컨테이너 생성 |
| 상태 보고 | 노드/Pod 상태를 API Server에 주기적으로 보고 |
| Probe 실행 | Liveness/Readiness/Startup Probe 실행 |
| 볼륨 마운트 | PVC에 따라 볼륨을 Pod에 마운트 |
| 리소스 관리 | cgroups로 CPU/메모리 제한 적용 |
flowchart TB
API["API Server"] -->|"Pod 스펙"| Kubelet["kubelet"]
Kubelet -->|"CRI"| Runtime["Container Runtime<br/>(containerd)"]
Runtime --> Container["컨테이너"]
Kubelet -->|"CSI"| Storage["스토리지"]
Kubelet -->|"상태 보고"| API
2.2 kube-proxy
Service의 네트워크 규칙을 노드에 적용.
| 모드 | 방식 | 특징 |
|---|---|---|
| iptables (기본) | iptables 규칙으로 트래픽 라우팅 | 대부분의 환경에서 충분 |
| IPVS | Linux IPVS(IP Virtual Server) 사용 | 대규모(Service 1000+)에서 성능 우수 |
Service IP(ClusterIP)로 들어오는 트래픽을 실제 Pod IP로 분산하는 역할.
2.3 Container Runtime
실제 컨테이너를 실행하는 엔진.
| 런타임 | 특징 | 현재 위치 |
|---|---|---|
| containerd | Docker에서 분리된 경량 런타임 | ✅ 가장 많이 사용 |
| CRI-O | Red Hat 주도, OCI 표준 전용 | OpenShift |
| K8s 1.24에서 제거 (Dockershim) | ❌ 사용 불가 |
⚠️ "K8s에서 Docker를 못 쓴다"는 것은 Docker 데몬을 런타임으로 쓸 수 없다는 뜻. Docker로 빌드한 이미지는 OCI 표준이므로 containerd에서 정상 실행됨.
3. 통신 흐름 상세
3.1 Deployment 생성 전체 흐름
sequenceDiagram
participant User as kubectl
participant API as API Server
participant etcd as etcd
participant DC as Deployment<br/>Controller
participant RSC as ReplicaSet<br/>Controller
participant Sched as Scheduler
participant KL as kubelet
participant CR as containerd
User->>API: 1. kubectl apply -f deploy.yaml
API->>API: 2. 인증 → 인가 → Admission
API->>etcd: 3. Deployment 저장
DC->>API: 4. Deployment 변경 감지 (Watch)
DC->>API: 5. ReplicaSet 생성 요청
API->>etcd: 6. ReplicaSet 저장
RSC->>API: 7. ReplicaSet 변경 감지
RSC->>API: 8. Pod 생성 요청 (Pending)
API->>etcd: 9. Pod 저장 (nodeName 없음)
Sched->>API: 10. Pending Pod 감지
Sched->>Sched: 11. 필터링 → 스코어링
Sched->>API: 12. Pod에 nodeName 배정
API->>etcd: 13. Pod 업데이트 (nodeName 있음)
KL->>API: 14. 자기 노드의 Pod 변경 감지
KL->>CR: 15. CRI로 컨테이너 생성
CR->>KL: 16. 컨테이너 Running
KL->>API: 17. Pod 상태 → Running
API->>etcd: 18. 상태 업데이트
핵심 패턴:
- 모든 컴포넌트는 API Server를 Watch하여 변경을 감지함
- 각 컨트롤러는 자기 관심사만 처리 (Deployment Controller는 ReplicaSet만, Scheduler는 Pending Pod만)
- 이벤트 드리븐 — 폴링이 아니라 Watch 기반
3.2 Watch 메커니즘
flowchart LR
Controller["컨트롤러"] -->|"Watch 등록"| API["API Server"]
API -->|"변경 이벤트 스트리밍"| Controller
Controller -->|"조정 액션"| API
- 컨트롤러가 API Server에 Watch를 등록하면, 관련 오브젝트가 변경될 때마다 실시간으로 이벤트를 받음
- 내부적으로 Informer 패턴 사용 — 로컬 캐시 + Watch 조합으로 API Server 부하 최소화
4. HA 구성
4.1 Control Plane HA
flowchart TB
LB["로드밸런서<br/>(HAProxy/Nginx/클라우드 LB)"]
subgraph CP["Control Plane (×3)"]
M1["Master 1<br/>API Server + etcd"]
M2["Master 2<br/>API Server + etcd"]
M3["Master 3<br/>API Server + etcd"]
end
subgraph Workers["Worker Nodes"]
W1["Worker 1"]
W2["Worker 2"]
W3["Worker 3"]
end
LB --> M1 & M2 & M3
W1 & W2 & W3 --> LB
| 컴포넌트 | HA 방식 |
|---|---|
| API Server | 여러 인스턴스 + 앞단 로드밸런서. Stateless라 단순 |
| etcd | 3/5/7 노드 Raft 클러스터. 과반수 유지 필수 |
| Scheduler | Leader Election — 하나만 Active, 나머지 Standby |
| Controller Manager | Leader Election — 하나만 Active, 나머지 Standby |
4.2 Worker Node 장애 처리
flowchart LR
NodeFail["Worker 노드 장애"] --> NodeCtrl["Node Controller<br/>NotReady 감지"]
NodeCtrl -->|"5분 대기<br/>(node-monitor-grace-period)"| Evict["Pod 축출<br/>(Eviction)"]
Evict --> Reschedule["Scheduler가<br/>다른 노드에<br/>Pod 재배치"]
| 단계 | 시간 | 설명 |
|---|---|---|
| kubelet 상태 보고 중단 | 즉시 | 노드가 NotReady로 전환 |
| 대기 | ~5분 | 일시적 네트워크 장애일 수 있으므로 대기 |
| Pod 축출 | 5분 후 | Taint(unreachable) 적용 → Pod 삭제 |
| 재스케줄링 | 즉시 | ReplicaSet Controller가 부족한 Pod를 감지 → Scheduler가 새 노드에 배치 |
5. Proxmox/VMware 위에 K8s 배포
5.1 VM 설계 가이드
| 역할 | 권장 스펙 | 수량 |
|---|---|---|
| Master | 2 vCPU, 4GB RAM, 50GB 디스크 | 3 (HA) |
| Worker | 4+ vCPU, 8+ GB RAM, 100GB 디스크 | 3+ |
| LB (선택) | 1 vCPU, 1GB RAM | 1~2 |
5.2 kubeadm 기반 설치 흐름
flowchart TD
Prereq["1. 사전 준비<br/>(swap off, 커널 모듈, sysctl)"]
Runtime["2. Container Runtime 설치<br/>(containerd)"]
KubeTools["3. kubeadm, kubelet, kubectl 설치"]
Init["4. kubeadm init<br/>(첫 번째 Master)"]
CNI["5. CNI 설치<br/>(Calico/Cilium)"]
JoinCP["6. 나머지 Master join"]
JoinWorker["7. Worker join"]
Verify["8. 클러스터 검증"]
Prereq --> Runtime --> KubeTools --> Init --> CNI --> JoinCP --> JoinWorker --> Verify
# 사전 준비 (모든 노드)
swapoff -a
modprobe br_netfilter overlay
cat <<EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system
# kubeadm init (첫 번째 Master)
kubeadm init \
--control-plane-endpoint "lb.k8s.local:6443" \
--upload-certs \
--pod-network-cidr=10.244.0.0/16
# kubeconfig 설정
mkdir -p $HOME/.kube
cp /etc/kubernetes/admin.conf $HOME/.kube/config
# CNI 설치 (Calico 예시)
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27/manifests/calico.yaml
# Worker join
kubeadm join lb.k8s.local:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
5.3 Proxmox + K8s 연동 포인트
| 연동 | 방법 |
|---|---|
| VM 생성 자동화 | Terraform bpg/proxmox + Cloud-Init |
| K8s 설치 자동화 | Ansible (kubespray) |
| 스토리지 | Ceph CSI → Proxmox Ceph 연동, NFS CSI |
| 네트워크 | Proxmox Bridge/VLAN + Calico/Cilium CNI |
| GPU Passthrough | Proxmox IOMMU → K8s GPU Operator |
정리
| 컴포넌트 | 위치 | 핵심 역할 |
|---|---|---|
| API Server | Control Plane | 모든 통신의 관문. 유일한 etcd 접근자 |
| etcd | Control Plane | 클러스터 상태 저장. Raft 합의 |
| Scheduler | Control Plane | Pending Pod → 적합 노드에 배치 |
| Controller Manager | Control Plane | 선언 상태와 현재 상태를 맞추는 컨트롤 루프 |
| kubelet | Worker | Pod 생명주기 관리. CRI로 런타임 호출 |
| kube-proxy | Worker | Service 네트워크 규칙 적용 (iptables/IPVS) |
| Container Runtime | Worker | 실제 컨테이너 실행 (containerd) |
다음 글
→ #3 Pod & Workload Resources — Pod 생명주기, Deployment, StatefulSet, DaemonSet
🔗 관련 문서
- K8s Overview — 전체 그림
- Kubernetes Series Index — 시리즈 목차
- Proxmox Cluster & HA — etcd ↔ Corosync 비교