[☸️K8s] 쿠버네티스를 공부하는 이유와 아키텍처 한눈에 보기
OCI/CRI/CNI/CSI 인터페이스로서의 쿠버네티스부터 Control Plane·Node 컴포넌트, kubectl apply 이후 Pod가 실행되기까지의 흐름을 정리합니다.
쿠버네티스는 단순한 컨테이너 관리 도구가 아니라 컨테이너 인프라가 따라야 할 표준 인터페이스의 집합이다. 이 글은 쿠버네티스를 공부하는 이유, 핵심 아키텍처(Control Plane / Node), 그리고
kubectl apply한 번에 어떤 컴포넌트가 어떤 순서로 동작하는지를 한 호흡에 정리한 1주차 학습 노트다.
이 글에서 다루는 내용
- 컨테이너 오케스트레이션이 등장하게 된 배경
- 쿠버네티스가 기술이 아니라 인터페이스인 이유 (OCI / CRI / CNI / CSI)
- Control Plane 컴포넌트(API Server, etcd, Scheduler, Controller Manager)의 역할
- Node 컴포넌트(kubelet, kube-proxy, Container Runtime)의 역할
kubectl apply -f deployment.yaml이 실제 Pod 실행으로 이어지는 전체 흐름- Control Loop와 운영체제의 이벤트 루프 사이의 구조적 유사성
이 글을 읽기 전 필요한 배경
이 글은 컨테이너의 기본 개념(이미지, 컨테이너 런타임, Docker)을 알고 있다는 전제에서 시작한다. 핵심 용어 몇 가지만 미리 정리한다.
📚 컨테이너: OS 커널을 공유하면서 프로세스 수준의 격리를 제공하는 실행 단위.
Linux 커널의 namespace(PID, Network, Mount, UTS, IPC, User 등 시스템 뷰 분리)와 cgroup(CPU, 메모리, I/O 등 자원 제한)이 격리의 기반이다.
📚 선언형(Declarative) 모델: “어떻게(how)”가 아니라 “무엇(what)”을 선언하는 방식.
시스템이 스스로 현재 상태와 선언된 상태의 차이를 감지하고 수렴한다. 멱등성이 보장된다는 점이 명령형과의 핵심 차이다.
📚 Control Plane / Worker Node: 쿠버네티스 클러스터의 두 축.
Control Plane은 “무엇이 어디서 실행되어야 하는가”를 결정하는 두뇌, Worker Node는 실제로 컨테이너를 실행하는 손과 발이다.
1. 쿠버네티스를 공부하는 이유
1-1. 컨테이너 오케스트레이션의 등장 배경
소프트웨어 배포 방식은 물리 서버 → 가상 머신(VM) → 컨테이너 순서로 추상화 수준이 높아져 왔다. 각 단계는 이전 단계의 한계를 해결하기 위해 등장했다. 물리 서버 시대에는 하나의 애플리케이션이 서버 자원을 독점하거나, 반대로 자원을 낭비하는 문제가 있었다. 가상 머신은 하나의 물리 서버 위에서 여러 OS를 격리된 환경으로 실행함으로써 자원 활용률을 높였지만, OS 전체를 포함하는 무거운 이미지와 느린 부팅 속도라는 한계가 있었다.
컨테이너는 OS 커널을 공유하면서 프로세스 수준의 격리를 제공함으로써 VM보다 훨씬 가볍고 빠른 실행 환경을 만들었다. Linux 커널의 namespace 와 cgroup 이 이 격리의 기반 기술이다. namespace는 PID, Network, Mount, UTS, IPC, User 등 프로세스가 바라보는 시스템 뷰를 분리하고, cgroup은 CPU, 메모리, 디스크 I/O 등 자원 사용량을 제한하고 측정한다. 컨테이너는 결국 커널 기능으로 격리된 프로세스이며, VM처럼 별도의 하이퍼바이저나 게스트 OS가 필요하지 않다.
그러나 컨테이너가 확산되면서 단일 호스트에서 Docker만으로 관리할 수 없는 문제들이 나타났다. 수십, 수백 개의 컨테이너를 여러 호스트에 걸쳐 실행할 때 다음과 같은 문제가 발생한다.
- 어떤 호스트에 컨테이너를 배치할 것인가 (스케줄링)
- 컨테이너가 죽으면 자동으로 재시작할 수 있는가 (자가 치유)
- 트래픽이 증가하면 컨테이너를 자동으로 늘릴 수 있는가 (스케일링)
- 여러 호스트에 분산된 컨테이너들이 서로 어떻게 통신하는가 (네트워킹)
- 컨테이너가 교체되어도 데이터를 유지할 수 있는가 (스토리지)
이 문제들을 해결하기 위해 Docker Swarm, Apache Mesos, Google Kubernetes 등이 경쟁했고, 결국 쿠버네티스가 사실상의 표준으로 자리잡았다.
1-2. 쿠버네티스는 기술이 아닌 표준 인터페이스다
쿠버네티스를 단순히 컨테이너를 자동으로 관리해주는 도구라고 이해하면 본질을 놓친다. 쿠버네티스의 진정한 가치는 컨테이너 인프라 전반에 걸친 표준 인터페이스를 정의했다 는 점에 있다.
이를 이해하려면 먼저 OCI와의 관계를 살펴봐야 한다.
📚 OCI (Open Container Initiative): 2015년 Docker와 CoreOS가 주도하여 설립한 Linux Foundation 산하 프로젝트.
컨테이너 이미지 형식(Image Spec)과 컨테이너 실행 방식(Runtime Spec)을 표준화했다. OCI 덕분에 Docker가 만든 이미지를 containerd, CRI-O 등 다른 런타임에서도 실행할 수 있게 되었다.
쿠버네티스는 여기서 한 단계 더 나아가, 컨테이너 런타임·네트워크·스토리지 각각에 대한 플러그인 인터페이스를 정의했다.
📚 CRI (Container Runtime Interface): 쿠버네티스 ↔ 컨테이너 런타임 사이의 gRPC 인터페이스. 구현체에 containerd, CRI-O 등이 있다.
📚 CNI (Container Network Interface): Pod에 네트워크를 붙이고 제거하는 방식을 표준화. 구현체에 Calico, Cilium, Flannel 등이 있다.
📚 CSI (Container Storage Interface): 스토리지 시스템을 플러그인 방식으로 연결. AWS EBS, Ceph, NFS 등이 CSI를 통해 연결된다.
1
2
3
4
5
6
7
8
┌────────────────────────────┐
│ Kubernetes │
└──────┬────────┬────────┬───┘
│ CRI │ CNI │ CSI
▼ ▼ ▼
containerd Calico EBS
CRI-O Cilium Ceph
Flannel NFS
결과적으로 쿠버네티스는 특정 구현체가 아니라, 컨테이너 인프라가 따라야 할 인터페이스의 집합이 되었다. 이 구조는 벤더 종속성을 제거하고, 각 레이어에서 최적의 구현체를 선택할 수 있게 만든다.
1-3. 쿠버네티스 생태계의 방향성
쿠버네티스에는 명확한 문제가 있다. 컨트롤 플레인 자체가 너무 무겁다 는 것이다. etcd, API Server, Scheduler, Controller Manager 등 핵심 컴포넌트만 실행해도 상당한 메모리와 CPU를 소비한다. 소규모 환경이나 엣지 컴퓨팅 환경에서는 이 오버헤드가 큰 부담이 된다.
이에 대한 생태계의 대응 방식이 중요하다. 업계는 쿠버네티스를 대체하는 새로운 오케스트레이터를 만드는 방향이 아니라, 쿠버네티스 인터페이스를 유지하면서 내부 구현을 경량화하는 방향 을 선택했다.
- k3s: Rancher Labs(현 SUSE)가 만든 경량 쿠버네티스. 단일 바이너리로 배포되며 etcd 대신 SQLite를 기본 데이터스토어로 사용한다.
- k0s: Mirantis가 만든 배포판. 추가적인 OS 의존성 없이 실행 가능하다.
- MicroK8s: Canonical이 만든 배포판. snap 패키지 형태로 배포된다.
이 모든 경량 배포판은 쿠버네티스 API와 완전히 호환된다. kubectl이 동작하고, CRI·CNI·CSI 플러그인을 그대로 사용할 수 있으며, Helm 차트와 쿠버네티스 매니페스트를 그대로 적용할 수 있다.
이것이 쿠버네티스 인터페이스를 공부해야 하는 핵심 이유다 — 구현체는 바뀌어도 인터페이스는 유지된다.
2. 쿠버네티스 아키텍처
2-1. 전체 구조 개요: 선언형 모델
쿠버네티스를 이해하는 데 있어 가장 먼저 파악해야 할 개념은 선언형(Declarative) 모델 이다. 일반적인 명령형(Imperative) 방식은 “컨테이너를 시작해라”, “포트 80을 열어라”처럼 수행할 행동을 직접 지시한다. 반면 선언형 방식은 “이 시스템은 이런 상태여야 한다” 고 원하는 상태(Desired State)를 선언하고, 시스템이 스스로 현재 상태(Actual State)와의 차이를 감지하여 원하는 상태로 수렴하게 만든다.
1
2
명령형: "컨테이너 3개를 띄워라"
선언형: "이 클러스터에는 항상 컨테이너 3개가 떠 있어야 한다"
kubectl apply -f deployment.yaml 명령을 실행할 때, 우리는 쿠버네티스에게 특정 행동을 명령하는 것이 아니다. YAML 파일에 기술된 상태가 클러스터에 반영되어야 한다는 의도를 API Server에 전달하는 것이다. 이 상태 정보는 etcd에 저장되고, 쿠버네티스의 각 컨트롤러가 지속적으로 etcd의 선언과 실제 클러스터 상태를 비교하며 차이를 해소한다.
이 모델은 운영 측면에서 강력한 장점을 제공한다. 노드가 죽거나 컨테이너가 비정상 종료되는 상황에서도 시스템이 선언된 상태로 자동 복구하려 시도한다. 또한 동일한 YAML 파일을 반복 적용해도 멱등성(idempotency) 이 보장된다. 이미 원하는 상태이면 아무것도 변경하지 않는다.
2-2. Control Plane 컴포넌트
쿠버네티스 클러스터는 크게 Control Plane과 Worker Node로 나뉜다. Control Plane은 클러스터 전체의 상태를 관리하고 결정을 내리는 두뇌 역할을 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──────────────────── Control Plane ────────────────────┐
│ │
│ ┌────────────┐ ┌──────┐ │
│ │ API Server │◄─►│ etcd │ │
│ └─────┬──────┘ └──────┘ │
│ │ │
│ ┌─────┴─────┬──────────────────┐ │
│ ▼ ▼ ▼ │
│ Scheduler Controller (cloud-controller │
│ Manager -manager) │
└───────────────────────────────────────────────────────┘
│ ▲
│ (Watch / Update) │
▼ │
┌──────────────────── Worker Node ──────────────────────┐
│ │
│ kubelet ──CRI──► containerd / CRI-O ──► runc │
│ kube-proxy │
└───────────────────────────────────────────────────────┘
API Server
API Server(kube-apiserver)는 쿠버네티스의 모든 통신이 통과하는 단일 진입점 이다. etcd와 직접 통신하는 유일한 컴포넌트이며, 클러스터 내 다른 모든 컴포넌트는 API Server를 통해서만 상태를 읽고 쓴다.
요청이 API Server에 도달하면 세 단계를 거친다.
- 인증(Authentication): 이 요청이 누구에게서 왔는지 확인.
- 인가(Authorization): 해당 주체가 이 작업을 수행할 권한이 있는지 RBAC 정책 기반 검사.
- 어드미션 컨트롤(Admission Control): 요청 내용의 유효성을 검증하고 필요시 변환.
이 세 단계를 통과한 요청만 etcd에 반영된다.
etcd
📚 etcd: 쿠버네티스 클러스터의 모든 상태를 저장하는 분산 key-value 스토어.
단순한 데이터베이스가 아니라 Raft 합의 알고리즘을 기반으로 동작하는 분산 시스템이다.
Raft는 다수의 노드 중 리더를 선출하고, 쓰기 작업이 과반수 노드에 복제되어야 커밋되도록 보장한다. 따라서 일부 etcd 노드에 장애가 발생해도 데이터의 일관성이 유지된다.
etcd가 쿠버네티스에서 핵심적인 이유는 Watch 메커니즘 에 있다. API Server는 etcd에 단순히 데이터를 저장하는 것이 아니라, 다른 컴포넌트들이 특정 키의 변경 사항을 실시간으로 감지할 수 있도록 Watch API를 제공한다. Scheduler, Controller Manager, kubelet 모두 이 Watch 메커니즘을 통해 자신이 관심 있는 상태 변화를 통보받는다.
Scheduler
Scheduler(kube-scheduler)는 아직 어떤 Node에도 할당되지 않은 Pod(nodeName 필드가 비어있는 Pod)를 감지하고, 해당 Pod를 실행할 최적의 Node를 결정한다. Scheduler는 두 단계로 동작한다.
- 필터링(Filtering): Pod의 resource request,
nodeSelector, affinity/anti-affinity, taint/toleration 등을 기준으로 실행 불가능한 Node를 걸러낸다. - 스코어링(Scoring): 남은 Node들에 점수를 매겨 가장 높은 점수의 Node를 선택한다.
중요한 점은 Scheduler가 직접 컨테이너를 실행하지 않는다 는 것이다. Scheduler는 Pod 오브젝트의
nodeName필드를 업데이트하는 것이 전부다. 실제 컨테이너 실행은 해당 Node의 kubelet이 담당한다.
Controller Manager
Controller Manager(kube-controller-manager)는 다수의 컨트롤러를 단일 프로세스로 실행하는 컴포넌트다. 각 컨트롤러는 특정 리소스 유형을 담당하며, Desired State와 Actual State의 차이를 감지하고 해소하는 Control Loop 를 반복적으로 실행한다.
- ReplicaSet Controller: 현재 실행 중인 Pod 수가 선언된
replicas와 일치하는지 확인. 부족하면 생성, 초과하면 삭제. - Deployment Controller: ReplicaSet을 통해 롤링 업데이트와 롤백을 관리.
- Node Controller: Node의 상태를 모니터링하고, 응답이 없는 Node의 Pod를 다른 Node로 이동.
2-3. Node 컴포넌트
kubelet
📚 kubelet: 각 Worker Node에서 실행되는 에이전트.
Control Plane과 해당 Node를 연결하는 역할을 한다.
kubelet은 API Server를 Watch하다가 자신이 실행된 Node에 할당된 Pod를 감지하면, Pod 스펙에 따라 컨테이너를 실행하도록 CRI를 통해 컨테이너 런타임에 지시한다.
kubelet은 컨테이너 실행 외에도 liveness probe 와 readiness probe 를 주기적으로 실행하여 컨테이너의 건강 상태를 확인한다.
liveness probe실패 → 컨테이너 재시작readiness probe실패 → Service의 엔드포인트에서 해당 Pod 제거
또한 Node의 CPU, 메모리, 디스크 사용량 등 리소스 상태를 API Server에 주기적으로 보고한다.
kube-proxy
kube-proxy는 각 Node에서 실행되며 쿠버네티스 Service 추상화를 네트워크 수준에서 구현 한다. Service는 동적으로 변하는 Pod의 IP를 안정적인 가상 IP(ClusterIP)로 추상화하는 리소스인데, kube-proxy는 이 가상 IP로 들어오는 트래픽을 실제 Pod IP로 포워딩하는 규칙을 각 Node의 iptables 또는 IPVS에 설정한다. Service의 Endpoints가 변경될 때마다(Pod가 추가되거나 삭제될 때) kube-proxy는 이 규칙을 갱신한다.
Container Runtime
Container Runtime은 실제로 컨테이너를 실행하는 소프트웨어다. 쿠버네티스 1.24 버전부터 Docker에 대한 직접 지원(dockershim)이 제거되었으며, 현재는 CRI를 구현한 컨테이너 런타임만 사용 가능 하다.
| Runtime | 특징 |
|---|---|
| containerd | 원래 Docker 내부 구성요소였으나 독립 프로젝트로 분리 |
| CRI-O | Red Hat이 주도하여 쿠버네티스 전용으로 설계한 경량 런타임 |
두 런타임 모두 내부적으로 OCI Runtime Spec을 구현한 runc 를 통해 실제 컨테이너를 실행한다.
2-4. 컴포넌트 간 통신 흐름: kubectl apply 이후 Pod가 실행되기까지
kubectl apply -f deployment.yaml 명령 실행 이후 실제로 Pod가 Node 위에서 실행되기까지의 전체 과정을 순서대로 추적해보면 쿠버네티스 아키텍처가 어떻게 맞물려 동작하는지 이해할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[1] kubectl ───► API Server (HTTPS + token/cert)
│ Auth → RBAC → Admission
▼
[2] etcd (Deployment 저장 = Desired State 변경)
│
(Watch) │
[3] Deployment Controller ─► ReplicaSet 생성
│
(Watch) │
[4] ReplicaSet Controller ─► Pod 생성 (nodeName = ∅)
│
(Watch) │
[5] Scheduler ─► nodeName 필드 채움
│
(Watch) │
[6] kubelet ─► CRI 호출
│
[7] containerd / CRI-O ─► runc ─► 컨테이너 실행
│
▼
Pod status = Running
kubectl은 YAML 파일을 파싱하여 API Server의 REST 엔드포인트로 HTTP 요청을 전송한다. 요청은kubeconfig파일에 저장된 인증 정보(클라이언트 인증서 또는 토큰)와 함께 전송된다.API Server는 인증 → 인가(RBAC) → 어드미션 컨트롤 순서로 요청을 검증한 뒤, Deployment 오브젝트를 etcd에 저장한다. 이 시점에서 클러스터의 선언된 상태 가 변경된다.
Deployment Controller(Controller Manager 내부)는 API Server Watch를 통해 새 Deployment가 생성된 것을 감지한다. 선언된
replicas수만큼 Pod를 가진 ReplicaSet을 생성한다.ReplicaSet Controller는 새 ReplicaSet이 생성된 것을 감지하고, 필요한 수만큼 Pod 오브젝트를 생성하여 API Server를 통해 etcd에 저장한다. 이 Pod들은 아직
nodeName이 비어있는 상태다.Scheduler는
nodeName이 없는 Pod를 감지하고, 필터링과 스코어링을 거쳐 적합한 Node를 선택한 뒤 Pod의nodeName필드를 해당 Node 이름으로 업데이트한다.선택된 Node의 kubelet은 자신의 Node에 바인딩된 새 Pod를 감지한다. Pod 스펙에서 컨테이너 이미지, 환경변수, 볼륨 마운트 등 정보를 읽고, CRI를 통해 컨테이너 런타임에 컨테이너 생성을 요청한다.
컨테이너 런타임(containerd/CRI-O)은 이미지가 로컬에 없으면 레지스트리에서 pull하고, namespace와 cgroup을 설정한 뒤
runc를 통해 컨테이너를 실행한다. kubelet은 컨테이너 상태를 API Server에 보고하고, Pod 상태가Running으로 갱신된다.
2-5. Control Loop: 운영체제 개념과의 연결
Controller Manager의 각 컨트롤러가 실행하는 Control Loop 는 다음 구조를 가진다.
1
2
3
4
5
6
loop:
desired = read from etcd
actual = read from cluster
diff = desired - actual
reconcile(diff)
goto loop
etcd에서 Desired State를 읽고, 현재 클러스터의 Actual State를 조회하여, 두 상태 사이의 차이(diff)를 계산하고, 차이를 해소하기 위한 작업을 수행한 뒤, 다시 처음으로 돌아가 반복한다. 이 루프는 상태가 수렴될 때까지 지속적으로 실행된다.
이 구조는 운영체제의 이벤트 루프 및 인터럽트 처리 방식과 유사한 패턴 이다. OS의 이벤트 루프는 외부 이벤트(I/O 완료, 타이머 만료, 시그널)를 감지하고 적절한 핸들러를 실행한 뒤 다시 대기 상태로 돌아간다. 쿠버네티스의 컨트롤러는 etcd의 상태 변화 이벤트를 Watch를 통해 감지하고 적절한 조정 작업을 수행한다는 점에서 구조적으로 동일한 패턴이다.
즉, 쿠버네티스의 Control Loop는 분산 시스템 수준에서 동작하는 이벤트 기반 상태 머신이다.
이 설계 덕분에 쿠버네티스는 노드 장애, 네트워크 단절, 컨테이너 크래시 등 예상치 못한 상황에서도 선언된 상태로 수렴하려는 특성을 갖는다. 개별 컴포넌트가 일시적으로 실패하더라도, 재시작 후 etcd의 상태를 다시 읽어 작업을 재개할 수 있다.
3. 학습 목표 점검
1주차 학습을 마친 후 다음 질문에 스스로 답할 수 있어야 한다.
- 쿠버네티스를 대체하는 기술이 나오기 어려운 이유는 무엇인가? CRI, CNI, CSI 인터페이스와 생태계의 관계를 중심으로 설명하라.
kubectl apply이후 Pod가 실행되기까지 어떤 컴포넌트가 어떤 순서로 동작하는가? 각 단계에서 어떤 데이터가 어디에 쓰이는지 추적하라.- etcd가 단순한 데이터베이스와 다른 이유는 무엇인가? Raft 합의와 Watch 메커니즘의 역할을 설명하라.
- Controller Manager의 Control Loop가 운영체제의 어떤 개념과 구조적으로 유사한가?
- Scheduler는 실제로 무엇을 하는가? 컨테이너를 직접 실행하지 않는다면 무엇을 변경하는가?
핵심 정리
- 쿠버네티스의 본질은 도구가 아니라 인터페이스 다. OCI는 이미지/런타임을, CRI/CNI/CSI는 쿠버네티스가 외부 구현체와 만나는 경계를 표준화한다. k3s, k0s, MicroK8s 같은 경량 배포판이 가능한 이유도 이 인터페이스 덕분이다.
- 쿠버네티스는 선언형 모델 위에서 동작한다. 우리가 보내는 것은 명령이 아니라 의도이며, 각 컨트롤러의 Control Loop가 Desired ↔ Actual 차이를 끝없이 좁힌다.
- API Server 는 모든 통신의 단일 진입점이며, etcd와 직접 통신하는 유일한 컴포넌트다. 다른 컴포넌트는 API Server의 Watch를 통해 상태 변화를 받는다.
- Scheduler는 컨테이너를 실행하지 않는다. Pod의
nodeName을 채우는 것까지가 Scheduler의 일이고, 실제 실행은 kubelet → CRI → containerd/CRI-O →runc경로로 흐른다. - Control Loop는 분산 시스템 수준의 이벤트 루프다. 일시적 실패에도 시스템이 선언된 상태로 수렴하려는 자가 치유 특성이 여기서 나온다.
Reading flow
- Previous:
... - Next:
... - Series:
/series/