Kubernetes 개요와 아키텍처: 선언형 desired-state 모델과 Control Plane 구조
개요
Kubernetes(K8s)는 컨테이너화된 애플리케이션의 배포·확장·운영을 자동화하는 오픈소스 컨테이너 오케스트레이터다. 2014년 Google이 내부 시스템 Borg의 경험을 바탕으로 공개했고, 현재 CNCF(Cloud Native Computing Foundation)가 관리한다. 단순히 컨테이너를 실행하는 도구가 아니라, “클러스터가 어떤 상태여야 하는가”를 선언하면 시스템이 그 상태로 스스로 수렴하도록 만드는 플랫폼이라는 점이 핵심이다.
Docker가 “하나의 컨테이너를 어떻게 만들고 실행하는가”를 다룬다면, Kubernetes는 “수십~수천 개의 컨테이너를 여러 노드에 걸쳐 어떻게 안정적으로 굴리는가”를 다룬다. 노드가 죽으면 파드를 다른 노드로 옮기고, 트래픽이 늘면 복제본을 늘리며, 새 버전을 무중단으로 굴려 넣는 일을 사람이 개입하지 않아도 처리한다.
핵심 개념과 원리
선언형 desired-state 모델
Kubernetes의 모든 조작은 **명령형(imperative)**이 아니라 **선언형(declarative)**이다. “컨테이너를 3개 띄워라”라고 절차를 지시하는 대신, “이 애플리케이션의 복제본은 항상 3개여야 한다”는 원하는 상태(desired state)를 YAML로 선언한다. 사용자는 목표를 기술하고, 시스템은 그 목표를 달성·유지할 방법을 스스로 찾는다.
| 구분 | 명령형 | 선언형 |
|---|---|---|
| 표현 | ”무엇을 하라”(동작) | “무엇이어야 한다”(상태) |
| 예시 | kubectl run, kubectl scale | kubectl apply -f app.yaml |
| 재실행 | 상태 충돌 가능 | 멱등(idempotent), 반복 안전 |
| 버전 관리 | 어려움 | Git으로 관리 용이 |
수렴 루프(reconciliation loop)
선언형 모델을 실제로 구현하는 것이 컨트롤러의 수렴 루프다. 각 컨트롤러는 desired state(사용자가 선언한 상태)와 current state(클러스터의 실제 상태)를 끊임없이 비교하고, 둘의 차이(diff)를 없애는 방향으로 조치를 취한다.
관찰(observe) → 비교(diff) → 조치(act) → 다시 관찰 …
예를 들어 복제본 3개를 선언했는데 노드 장애로 2개만 살아 있다면, 컨트롤러는 차이(1개 부족)를 감지하고 새 파드를 생성한다. 이 루프는 이벤트 발생 시점뿐 아니라 주기적으로도 돌기 때문에, 일시적 장애가 지나가면 시스템이 자동으로 원래 상태를 회복한다. 이것이 Kubernetes의 자가 복구(self-healing) 원리다.
Control Plane 구성 요소
Control Plane은 클러스터의 두뇌로, 상태를 저장하고 결정을 내린다.
| 구성 요소 | 역할 |
|---|---|
kube-apiserver | 모든 요청이 통과하는 유일한 관문. REST API 제공, 인증·인가·검증 수행 |
etcd | 클러스터의 모든 상태를 저장하는 분산 key-value 저장소(Single Source of Truth) |
kube-scheduler | 새 파드를 어떤 노드에 배치할지 결정(리소스·어피니티·테인트 고려) |
kube-controller-manager | ReplicaSet·Node·Job 등 각종 컨트롤러의 수렴 루프 실행 |
cloud-controller-manager | 클라우드 공급자(LB·볼륨·노드)와의 연동 담당 |
모든 컴포넌트는 etcd를 직접 건드리지 않고 반드시 kube-apiserver를 거친다. API 서버가 중앙 허브이자 유일한 쓰기 경로이므로, 접근 제어와 감사(audit)를 한곳에 모을 수 있다.
Worker Node 구성 요소
Worker Node는 실제 컨테이너(파드)가 실행되는 곳이다.
| 구성 요소 | 역할 |
|---|---|
kubelet | 노드 에이전트. API 서버로부터 파드 스펙을 받아 컨테이너 런타임에 실행 지시, 상태 보고 |
kube-proxy | 노드의 네트워크 규칙(iptables/IPVS)을 관리해 Service의 가상 IP를 실제 파드로 라우팅 |
| 컨테이너 런타임 | 실제로 컨테이너를 실행. 현재 표준은 containerd(CRI-O도 가능, Docker Engine 직접 지원은 제거됨) |
kubelet은 API 서버가 저장한 desired state를 노드 수준에서 실현하는 또 하나의 수렴 루프다. 노드가 API 서버와 통신이 끊기면 해당 노드는 NotReady로 표시되고, 컨트롤러가 파드를 다른 노드로 재스케줄한다.
왜 Kubernetes가 필요한가
- 자가 복구: 파드·노드 장애 시 자동 재생성·재배치
- 수평 확장: 복제본 수를 선언적으로 조정하거나 HPA로 자동 스케일
- 무중단 롤아웃/롤백: 새 버전을 점진 교체하고 문제 시 이전 버전으로 즉시 복귀
- 서비스 디스커버리: 파드 IP가 바뀌어도 Service/DNS로 안정적 접근
- 선언형 IaC: 인프라 상태를 코드로 관리하고 Git으로 버전 관리 → Kubernetes - GitOps와 ArgoCD
실전
클러스터 구성 확인
kubectl cluster-info # Control Plane 엔드포인트 확인
kubectl get nodes -o wide # 노드 목록·상태·버전
kubectl get componentstatuses # 코어 컴포넌트 헬스(구버전)
kubectl api-resources # 사용 가능한 리소스 종류선언형 배포와 수렴 관찰
# deploy.yaml — 원하는 상태를 선언
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels: { app: web }
template:
metadata:
labels: { app: web }
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80kubectl apply -f deploy.yaml # desired state 선언(멱등)
kubectl get pods -w # 수렴 과정 실시간 관찰
kubectl delete pod <name> # 하나 죽여보면 → 컨트롤러가 즉시 재생성
kubectl rollout undo deploy/web # 이전 버전으로 롤백kubectl apply는 몇 번을 실행해도 동일한 결과를 보장한다. YAML을 수정한 뒤 다시 apply하면 변경분만 반영되며, 이 파일을 Git에 두면 클러스터 상태 전체가 코드로 추적된다.
함정과 베스트 프랙티스
kubectl edit/kubectl scale남용 금지: 명령형으로 직접 바꾸면 YAML(SoT)과 실제 상태가 어긋난다(drift). 변경은 항상 매니페스트를 고쳐apply한다.etcd백업은 필수:etcd가 곧 클러스터 상태 전부다. 정기 스냅샷(etcdctl snapshot save)이 없으면 Control Plane 손상 시 복구 불가.- 리소스 requests/limits 설정: 미설정 시 스케줄러가 배치를 잘못 판단하고, 한 파드가 노드 자원을 독식할 수 있다.
- API 서버는 SPOF에 유의: 단일 노드 Control Plane은 장애에 취약하다. 프로덕션은 Control Plane을 다중화(HA)한다.
- 버전 스큐 규칙 준수:
kubelet은 API 서버보다 최대 2~3 마이너 버전 낮게만 허용된다. 업그레이드는 Control Plane → Node 순서로.
정리
Kubernetes의 본질은 “선언한 desired state로 시스템이 스스로 수렴한다”는 제어 이론적 모델이다. Control Plane(kube-apiserver·etcd·scheduler·controller-manager)이 상태를 저장·결정하고, Worker Node(kubelet·kube-proxy·containerd)가 이를 실현한다. 이 수렴 루프 덕분에 자가 복구·확장·무중단 롤아웃이 자동으로 이뤄진다. 다음 단계로 워크로드 종류는 Kubernetes - Pod와 워크로드, 네트워킹은 Kubernetes - 서비스와 네트워킹, 설정·스토리지는 Kubernetes - 설정과 스토리지를 참고한다.