Kubernetes, Node Components

· 6분 읽기

kubelet은 Pod를 실행하고, Container Runtime은 컨테이너를 관리하며, kube-proxy는 네트워크 트래픽을 라우팅한다.

kubelet

kubelet은 모든 Worker Node에서 실행되는 에이전트이다.

API Server로부터 PodSpec을 받아 해당 노드에서 컨테이너가 정상적으로 실행되고 있는지 보장하는 역할을 한다.

flowchart LR
    API["kube-apiserver"] -->|Watch PodSpec| kubelet
    kubelet -->|CRI 호출| CR["Container Runtime"]
    kubelet -->|상태 보고| API
    CR --> Container1["Container"]
    CR --> Container2["Container"]
  1. 노드 등록: kubelet이 시작되면 API Server에 자신(노드)을 등록한다

  2. Pod 라이프사이클 관리: API Server를 Watch하여 해당 노드에 스케줄된 Pod를 감지하고, Container Runtime에 컨테이너 생성/삭제를 지시한다

  3. 상태 보고: 노드 상태(CPU, 메모리, 디스크 등)와 Pod 상태를 주기적으로 API Server에 보고한다

  4. 헬스체크 실행: 컨테이너 Probe(Liveness, Readiness, Startup)를 실행하여 컨테이너 상태를 모니터링한다

PLEG (Pod Lifecycle Event Generator)

kubelet은 PLEG라는 컴포넌트를 통해 컨테이너 상태 변화를 감지한다.

  • PLEG는 주기적으로(기본 1초) Container Runtime에 질의하여 Pod/컨테이너의 현재 상태를 확인한다

  • 이전 상태와 비교하여 변화가 있으면 이벤트를 생성한다 (ContainerStarted, ContainerDied 등)

  • 생성된 이벤트를 kubelet의 메인 루프에 전달하여 적절한 조치를 취하게 한다

PLEG가 응답하지 않으면 노드가 NotReady 상태에 빠진다. PLEG is not healthy 에러는 Container Runtime이 느리거나 노드에 과부하가 걸렸을 때 흔히 발생한다.

kubelet은 세 가지 Probe를 통해 컨테이너의 상태를 확인한다.

Probe목적실패 시 동작사용 시점
Liveness컨테이너가 살아있는지 확인컨테이너 재시작데드락, 무한 루프 탐지
Readiness트래픽을 받을 준비가 되었는지 확인Service 엔드포인트에서 제거초기화 완료 전, 일시적 과부하
Startup앱이 시작되었는지 확인컨테이너 재시작시작 시간이 긴 레거시 앱

Probe 방식:

  • httpGet: HTTP GET 요청을 보내고 2xx~3xx 응답을 확인한다

  • tcpSocket: TCP 연결을 시도한다

  • exec: 컨테이너 내부에서 명령을 실행하고 exit code 0을 확인한다

  • grpc: gRPC Health Check Protocol을 사용한다

Static Pod

kubelet은 API Server 없이도 로컬 파일 시스템의 매니페스트를 읽어 Pod를 직접 실행할 수 있다. 이를 Static Pod라 한다.

  • 기본 경로: /etc/kubernetes/manifests/

  • Control Plane 컴포넌트(kube-apiserver, etcd, kube-scheduler 등)가 Static Pod로 실행되는 대표적 사례이다

  • kubelet이 API Server에 Mirror Pod를 생성하여 kubectl get pods로 조회할 수 있지만, 삭제/수정은 매니페스트 파일을 통해서만 가능하다

리소스 관리

kubelet은 노드의 리소스를 관리하고 보호하는 여러 메커니즘을 제공한다.

cgroups v2

  • Linux 커널의 cgroups(Control Groups)를 사용하여 컨테이너별 CPU/메모리 할당을 제어한다

Eviction (축출)

kubelet은 노드 리소스가 부족할 때 Pod를 축출한다:

  1. Soft Eviction: 유예 기간(grace period) 후 축출

  2. Hard Eviction: 즉시 축출 (기본값: 메모리 100Mi, 디스크 10%, 노드 PID 등)

Device Plugin

  • GPU, FPGA 등 특수 하드웨어를 Kubernetes에서 관리하기 위한 프레임워크이다

  • kubelet과 gRPC로 통신하며, 디바이스 등록/할당/해제를 처리한다

Container Runtime

Container Runtime은 실제로 컨테이너를 생성하고 실행하는 소프트웨어이다.
kubelet은 CRI(Container Runtime Interface)를 통해 Container Runtime과 통신한다.

CRI (Container Runtime Interface)

flowchart LR
    kubelet -->|CRI gRPC| Runtime["Container Runtime"]
    Runtime -->|OCI 표준| Container["Container"]

    subgraph "CRI 서비스"
        RS["RuntimeService<br />(컨테이너 생성/삭제/실행)"]
        IS["ImageService<br />(이미지 Pull/삭제/조회)"]
    end

CRI는 kubelet과 Container Runtime 사이의 표준 gRPC 인터페이스이다.

  • RuntimeService: 컨테이너와 Pod Sandbox의 라이프사이클을 관리한다

  • ImageService: 컨테이너 이미지의 Pull, 삭제, 조회를 관리한다

CRI 덕분에 kubelet은 특정 Container Runtime에 종속되지 않으며, CRI를 구현한 어떤 런타임이든 사용할 수 있다.

kube-proxy

kube-proxy는 모든 노드에서 실행되며 Service의 네트워크 규칙을 관리하는 컴포넌트이다.
Pod 간 통신과 외부 트래픽의 Pod 라우팅을 담당한다.
Calico 같은 일부 CNI(Container Network Interface) 플러그인은 kube-proxy 없이 Service 라우팅을 직접 처리할 수 있다.

아래는 간단한 예시이다.

sequenceDiagram
    participant Client as Client Pod
    participant KP as kube-proxy<br/>(네트워크 규칙)
    participant EP as EndpointSlice
    participant Pod as Backend Pod

    Note over KP: API Server를 Watch하여<br/>Service/EndpointSlice 변경 감지
    Client->>KP: Service ClusterIP로 요청
    KP->>KP: DNAT 수행<br/>(Service IP → Pod IP)
    KP->>Pod: 실제 Pod로 전달

kube-proxy는 API Server를 감시하여 Service와 EndpointSlice의 변경을 감지하고, 노드의 네트워크 규칙을 업데이트한다.

실제 트래픽이 kube-proxy 프로세스를 경유하지는 않으며, 커널 레벨의 규칙(iptables/IPVS/nftables)으로 패킷을 전달한다.

Service 유형별 라우팅

ClusterIP

  1. Service 생성 시 가상 IP(ClusterIP)가 할당된다

  2. kube-proxy가 모든 노드에 네트워크 규칙을 설정한다

  3. ClusterIP로 향하는 패킷에 DNAT(Destination NAT)를 수행하여 백엔드 Pod 중 하나로 전달한다

NodePort

  1. ClusterIP의 모든 기능을 포함한다

  2. 추가로 모든 노드의 특정 포트(30000-32767)를 개방한다

  3. <NodeIP>:<NodePort>로 들어온 트래픽을 ClusterIP를 거쳐 Pod로 전달한다

LoadBalancer

  1. NodePort의 모든 기능을 포함한다

  2. 클라우드 프로바이더의 로드 밸런서가 모든 노드의 NodePort로 트래픽을 분산한다

EndpointSlice

Kubernetes v1.33부터 기존 Endpoints API는 공식 deprecated 되었으며, EndpointSlice로 대체된다.

  • 기존 Endpoints는 한 Service의 모든 백엔드를 하나의 객체에 저장했다

  • EndpointSlice는 최대 100개씩 분할하여 저장한다

flowchart TB
    subgraph "Worker Node"
        kubelet["kubelet<br/>(노드 에이전트)"]
        kp["kube-proxy<br/>(네트워크 규칙)"]
        cr["Container Runtime<br/>(containerd)"]

        kubelet -->|CRI gRPC| cr
        cr --> C1["Container"]
        cr --> C2["Container"]
        cr --> C3["Container"]

        kp -->|iptables/IPVS/nftables| NET["네트워크 규칙"]
    end

    API["kube-apiserver"] -->|PodSpec Watch| kubelet
    API -->|Service/EndpointSlice Watch| kp
    kubelet -->|노드/Pod 상태 보고| API

각 컴포넌트의 역할을 한 줄로 정리하면:

  • kubelet: “이 노드에서 어떤 Pod가 실행되어야 하는가”를 관리한다

  • Container Runtime: “컨테이너를 어떻게 생성/실행/삭제하는가”를 처리한다

  • kube-proxy: “Service로 들어온 트래픽을 어떤 Pod로 보내는가”를 결정한다

References