본문으로 건너뛰기
목록으로

쿠버네티스는 무엇으로 이루어지나 — 컨트롤 플레인·워커 노드 구성 요소

Johny Cho
Software Engineer @ Kurly

쿠버네티스를 처음 보면 kubectl apply 한 줄로 컨테이너가 뜨는 게 어떻게 되는 일인지 잘 안 보입니다. 그런데 그 안에서는 여러 부품이 각자 역할을 나눠 맡아 협업하고 있습니다. 이 글은 쿠버네티스 지식이 거의 없어도 이해하도록, 클러스터가 어떤 구성 요소로 이루어지고 각 부품이 무슨 일을 하는지를 그림으로 풀어씁니다. (Pod·Deployment 같은 오브젝트를 실제로 어떻게 쓰는지와 GitOps 배포는 별도 주제이므로, 이 글은 클러스터 내부 구조에 집중합니다.)

쿠버네티스 클러스터는 크게 결정을 내리는 컨트롤 플레인컨테이너를 실제로 실행하는 워커 노드로 나뉩니다 — 이 두 층과 각 부품의 역할만 알면 나머지가 쉽게 이해됩니다.

전체 구조 — 컨트롤 플레인과 워커 노드

클러스터는 여러 대의 서버(노드)를 하나로 묶은 것입니다. 노드는 역할에 따라 두 종류입니다.

  • 컨트롤 플레인(control plane) — 클러스터의 결정·관리 담당. "무엇을 어디에 얼마나 띄울지"를 결정하고, 전체 상태를 관리합니다. 직접 컨테이너를 실행하지는 않습니다.
  • 워커 노드(worker node) — 실제 실행 담당. 컨트롤 플레인의 결정을 받아 컨테이너(파드)를 실제로 실행합니다.
PlantUML 코드
@startuml
skinparam defaultTextAlignment center
skinparam rectangle {
BackgroundColor #F4F6FB
BorderColor #556080
}
skinparam database {
BackgroundColor #E9F2E9
BorderColor #556080
}
title 쿠버네티스 클러스터 구성 — 컨트롤 플레인 + 워커 노드

package "컨트롤 플레인 (결정·관리)" {
rectangle "API 서버\n모든 요청의 관문" as api
database "etcd\n모든 상태 저장" as etcd
rectangle "스케줄러\n파드를 어느 노드에" as sched
rectangle "컨트롤러 매니저\n원하는 상태 유지 루프" as cm
}

package "워커 노드 (여러 대)" {
rectangle "kubelet\n파드 실행 감독" as k1
rectangle "컨테이너 런타임\ncontainerd 등" as r1
rectangle "kube-proxy\n서비스 네트워킹" as p1
rectangle "Pod" as pod1
}

api --> etcd
sched --> api
cm --> api
api --> k1 : 지시 / 상태 보고
k1 --> r1
r1 --> pod1
@enduml

PlantUML 쿠버네티스 클러스터 구성 요소

컨트롤 플레인 — 결정·관리 부품들

컨트롤 플레인은 네 개의 부품으로 나뉩니다. 각각 하는 일이 뚜렷합니다.

API 서버 (kube-apiserver) — 모든 요청의 관문

클러스터의 정문입니다. kubectl, 대시보드, 다른 부품까지 모든 요청이 API 서버를 거칩니다. 요청을 인증·검증하고, 결과를 저장소에 반영합니다. 다른 부품끼리 직접 대화하지 않고, 항상 API 서버를 통해서만 소통합니다.

etcd — 모든 상태를 담는 저장소

클러스터의 장부입니다. "지금 어떤 파드가 몇 개, 어느 노드에 있어야 하는지" 같은 모든 상태를 저장하는 키-값 데이터베이스입니다. 오직 API 서버만 etcd에 접근합니다. etcd가 사라지면 클러스터는 자기 상태를 잃으므로, 보통 여러 대로 이중화합니다.

스케줄러 (kube-scheduler) — 파드를 어느 노드에

새로 만들어졌지만 아직 노드가 정해지지 않은 파드를 발견하면, 어느 워커 노드에 놓을지 결정합니다. 노드의 남은 CPU·메모리, 배치 규칙(어피니티) 등을 따져 가장 알맞은 노드를 고른 뒤, "이 파드는 노드 N에"라고 API 서버에 기록합니다. 결정만 하고, 실제 실행은 하지 않습니다.

컨트롤러 매니저 (kube-controller-manager) — 원하는 상태를 유지

여러 개의 컨트롤러(제어 루프) 를 돌립니다. 각 컨트롤러는 "원하는 상태와 현재 상태를 비교해, 다르면 맞추는" 일을 끊임없이 반복합니다. 예를 들어 레플리카를 3개로 선언했는데 하나가 죽으면, 컨트롤러가 그걸 감지해 새 파드를 만들도록 API 서버에 요청합니다. 클라우드에서는 로드밸런서·볼륨을 다루는 클라우드 컨트롤러 매니저가 따로 붙기도 합니다.

컨트롤 플레인의 규칙은 단순합니다 — 모든 소통은 API 서버를 통하고, 모든 상태는 etcd에 있고, 스케줄러는 배치를 결정하고, 컨트롤러는 원하는 상태를 계속 맞춥니다.

워커 노드 — 실행 부품들

워커 노드에는 실제로 컨테이너를 실행하기 위한 세 부품이 있습니다.

kubelet — 노드의 현장 관리자

각 워커 노드에 하나씩 있는 현장 감독입니다. API 서버에게 "내 노드에 배정된 파드가 뭐냐"를 물어보고, 그 파드의 컨테이너가 실제로 떠서 정상 동작하는지 책임집니다. 컨테이너가 죽으면 다시 띄우고, 상태를 API 서버에 보고합니다. kubelet은 컨테이너를 직접 만들지 않고, 컨테이너 런타임에게 시킵니다.

컨테이너 런타임 — 실제로 컨테이너를 실행

이미지를 내려받아 컨테이너를 실제로 실행하는 소프트웨어입니다. 요즘은 containerd나 CRI-O를 씁니다(예전의 도커 엔진 역할). kubelet이 표준 인터페이스(CRI)로 런타임에게 "이 이미지로 컨테이너 띄워"라고 지시합니다.

kube-proxy — 서비스 네트워킹

파드는 죽고 살아나며 IP가 계속 바뀝니다. 그래서 앞에 고정 진입점인 Service를 두는데, kube-proxy가 각 노드에서 "이 서비스로 온 트래픽을 실제 파드들에게 어떻게 나눠 보낼지" 네트워크 규칙을 관리합니다. Service·Ingress의 자세한 사용법은 별도 주제이니, 여기서는 kube-proxy의 역할만 짚습니다.

워커 노드는 kubelet이 지휘하고, 컨테이너 런타임이 실제 실행을 맡고, kube-proxy가 서비스 트래픽을 라우팅합니다 — 세 부품이 파드를 "실제로 실행되게" 만듭니다.

오브젝트는 무엇인가 — 부품이 다루는 대상

지금까지가 클러스터를 이루는 부품(프로세스) 이라면, 우리가 kubectl로 만드는 Pod·Deployment·Service 등은 그 부품들이 다루는 대상(리소스) 입니다. 아주 짧게만 정리합니다.

오브젝트한 줄
Pod컨테이너를 감싼 가장 작은 실행 단위(일회성)
ReplicaSet파드를 정해진 개수만큼 유지
Deployment원하는 레플리카·버전을 선언(내부적으로 ReplicaSet 관리)
Service / Ingress파드 앞의 고정 진입점·부하 분산 / 외부 트래픽 경로
Namespace리소스를 환경·팀별로 나눠 담는 격리 단위

부품(API 서버·스케줄러·kubelet…)이 일을 하고, 오브젝트(Deployment·Pod…)는 그 일의 대상입니다.

배포 요청 한 번이 흐르는 과정

부품들이 어떻게 협업하는지, kubectl apply로 파드 하나를 띄우는 과정으로 따라가 봅니다.

PlantUML 코드
@startuml
skinparam defaultTextAlignment center
autonumber
actor "사용자 (kubectl)" as u
participant "API 서버" as api
database "etcd" as etcd
participant "스케줄러" as sched
participant "kubelet (워커 노드)" as kubelet
participant "컨테이너 런타임" as rt

u -> api : 배포 요청 (kubectl apply)
api -> etcd : 원하는 상태 저장
sched -> api : 노드 미배정 파드 감지 → 노드 선택
api -> etcd : "파드를 노드 N에" 기록
kubelet -> api : 내 노드에 배정된 파드 확인
kubelet -> rt : 컨테이너 실행
kubelet -> api : 상태 보고 (Running)
@enduml

PlantUML 쿠버네티스 배포 요청 흐름

  1. 사용자kubectl apply로 "이런 파드를 원한다"를 API 서버에 보냅니다.
  2. API 서버가 그 원하는 상태를 etcd에 저장합니다. (이 시점엔 아직 어느 노드에 뜰지 미정)
  3. 스케줄러가 노드 미배정 파드를 발견해, 가장 알맞은 워커 노드를 골라 API 서버에 기록합니다.
  4. 배정된 노드의 kubelet이 "내 노드에 새 파드가 있다"를 확인하고, 컨테이너 런타임에게 실행을 지시합니다.
  5. 컨테이너가 뜨면 kubelet이 상태를 API 서버에 보고하고, 그 결과가 etcd에 반영됩니다.
모든 단계가 API 서버를 중심으로 돌아갑니다 — 각 부품은 서로 직접 명령하지 않고, "API 서버에 기록된 원하는 상태"를 각자 보고 자기 일을 합니다. 이 느슨한 협업이 쿠버네티스가 견고한 이유입니다.

정리

  • 클러스터는 컨트롤 플레인(결정·관리) + 워커 노드(실행) 로 나뉜다.
  • 컨트롤 플레인: API 서버(모든 요청의 관문) · etcd(모든 상태 저장) · 스케줄러(파드를 어느 노드에) · 컨트롤러 매니저(원하는 상태 유지 루프).
  • 워커 노드: kubelet(파드 실행 감독) · 컨테이너 런타임(실제 실행, containerd 등) · kube-proxy(서비스 네트워킹).
  • 오브젝트(Pod·Deployment·Service)는 부품들이 다루는 대상이다.
  • 배포는 API 서버 → etcd → 스케줄러 → kubelet → 런타임 으로 흐르고, 모든 소통은 API 서버를 통한다.
쿠버네티스는 저절로 되는 게 아니라 "역할을 나눈 부품들이 API 서버를 통해 원하는 상태를 맞추는" 구조입니다 — 각 부품이 무슨 일을 하는지 알면, 장애를 진단할 때 어디를 봐야 할지도 보입니다.

용어 한 줄 정리

용어쉬운 뜻
클러스터여러 노드(서버)를 하나로 묶은 것
컨트롤 플레인클러스터의 결정·상태 관리 담당
워커 노드실제로 컨테이너(파드)를 실행하는 노드
API 서버모든 요청이 거치는 관문
etcd클러스터의 모든 상태를 담는 저장소
스케줄러파드를 어느 노드에 놓을지 결정
컨트롤러 매니저원하는 상태와 현재 상태를 계속 맞추는 루프
kubelet노드에서 파드 실행을 감독하는 현장 관리자
컨테이너 런타임이미지를 받아 컨테이너를 실제 실행(containerd 등)
kube-proxy서비스 트래픽을 파드로 라우팅하는 네트워크 규칙 관리