Kubelet

들어가는 글

안녕하세요. 오픈소스컨설팅 Private Cloud팀에서 Kubernetes 기술 지원을 하고 있는 변진영입니다.

쿠버네티스를 한번이라도 접해보셨던 분들은 아마 kubelet이 쿠버네티스의 핵심 요소라고는 알고 있지만, 정확히 무슨 일을 하는 지는 모르실 수도 있습니다.

그래서, 이번 포스팅에서는 제가 쿠버네티스 기술 지원을 하게 되면서 kubelet도 다른 컴포넌트들에 못지 않게 많은 일들을 하고 있다는 걸 깨닫게 됐던 경험이 있어서 공유하고 알아보고자 합니다.


Kubelet 이란?

Kubelet은 쿠버네티스 노드에서 실행되는 노드 에이전트로 Pod의 생성, 실행 및 모니터링에 핵심적인 역할을 수행합니다.

또한, 클러스터의 원활한 운영을 지원하기 위해 노드 상태 및 리소스를 관리하고 ControlPlane과의 통신을 위한 징검다리 역할 등 다양한 역할을 수행합니다.

kubelet 의 구성 요소

  1. CAdvisor: CAdvisor는 Kubelet의 하위 프로세스로 컨테이너에 대한 정보를 수집, 처리 및 전달하는 데몬입니다. CAdvisor에서 수집된 정보는 Kubelet으로 전달되고 이는 클러스터 상태 모니터링에 사용됩니다.
  2. Kubelet APIserver: kubelet APIserver를 통해 Kubelet으로 들어온 요청이 처리됩니다.
  3. Kubeconfig: Kubelet이 ControlPlane과 통신하는 데 사용하는 인증 정보 파일로, 클러스터의 APIserver 주소, 클러스터 정보 등이 명시되어 있습니다.
  4. OOM Monitor: Kubelet이 pod의 OOM(Out-Of-Memory)를 처리하는데 사용합니다. OOM 발생 시 해당 Pod를 중지하거나 다시 시작하여 노드의 리소스 안정성을 보장합니다.

이번에는 Kubelet의 다양한 기능을 알아보고 테스트와 로그 확인을 통해 Kubelet의 기능을 직접 시현해보도록 하겠습니다.

Kubelet 의 기능

1. Pod 관리 🔧

Kubelet은 kube-apiserver와 통신하여, Pod 정보(PodSpecs)를 전달 받아 그 조건에 맞게 Pod를 실행하고, Pod의 상태를 주기적으로 모니터링합니다.

만약에 Pod에 문제가 발생할 경우 Pod를 다시 시작하거나 스케쥴링하여 안정성을 유지합니다. 어떤 노드에 Pod를 배치할 지는 kube-scheduler가 결정하지만, 실제로 Container Runtime에 배치를 명령하는 것은 Kubelet에서 수행합니다.

2. Container 관리 🦺

Kubelet은 Container Runtime을 사용하여 PodSpec에 따라 컨테이너 이미지 Pull, 리소스 할당, Container 시작 및 중지 등 Container의 상태를 유지/관리합니다.

3. 리소스 관리 및 모니터링 🔍

Kubelet은 노드의 리소스 사용량을 모니터링하여 클러스터의 상태를 파악하고, 이를 Control Plane에 전달합니다. 해당 정보는 Pod 스케쥴링 결정에 사용됩니다.

또한, Pod에 대한 CPU 및 메모리 공급 등의 리소스 할당 프로세스를 처리하여,공평한 분배를 보장하고 리소스 충돌을 최소화합니다.

4. 상태 보고 📢

Kubelet은 주기적으로 노드와 Pod의 상태를 Control Plane에 전달하고 이 정보를 통해 Control Plane은 클러스터 전체 상태 및 노드와 Pod 상태를 모니터링할 수 있습니다.

5. 노드 자동 복구 🩹

Kubelet은 노드가 비정상적으로 종료된 경우 자동 복구를 시도합니다. 이를 통해노드의 가용성 및 클러스터 안정성을 유지합니다.

6. 볼륨 관리 💾

Kubelet은 Pod 내 볼륨 마운트 조정자로서 Pod 재시작 및 재스케쥴링 전반에 걸쳐 액세스 연속성을 유지하여일관된 데이터 스토리지를 보장합니다.

7. 네트워킹 📡

Kubelet은 Pod 내의 컨테이너에 대한 네트워크를 설정하여 동일한 노드에 있는 컨테이너 간 및 클러스터의 노드 간에 통신을 가능하게 합니다.

8. Pod 보안 정책 🚧

Kubelet은 노드에서 실행되는 워크로드의 보안을 보장하기 위해 kubernetes 관리자가 설정한 Pod 보안 정책에 따라 Pod를 생성 및 접근할 수 있도록 합니다.


Kubelet 기능 확인

1. Pod 및 Container 관리

우선 테스트를 위해 간단한 nginx Pod를 배포해보도록 하겠습니다.

# cat kubelet-test-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: kubelet
  name: kubelet-pod
spec:
  containers:
  - image: nginx:1.20.2
    name: kubelet-pod
    ports:
    - containerPort: 80
      protocol: TCP
    readinessProbe:
      httpGet:
        path: /
        port: 80
      periodSeconds: 10
  nodeName: jy-worker04


# kubectl apply -f kubelet-test-pod.yaml

Pod가 배포된 노드에서 kubelet의 로그를 확인해보면 Pod 생성을 위해 Kubelet이 수행한 일을 알 수 있습니다.

1. Control Plane의 APIserver로 부터 kubelet에 “kubelet-pod”라는 Pod가 할당됩니다.

kubelet.go:2343] "SyncLoop ADD" source="api" pods=[default/kubelet-pod]

2. Pod에 할당할 볼륨을 생성하고 Pod에 마운트합니다.

reconciler_common.go:258] "operationExecutor.VerifyControllerAttachedVolume started for volume \"kube-api-access-x5n9b\" (UniqueName: \"kubernetes.io/projected/b139c07e-7a5e-4c86-bb72-ffdf351624c9-kube-api-access-x5n9b\") pod \"kubelet-pod\" (UID: \"b139c07e-7a5e-4c86-bb72-ffdf351624c9\") " pod="default/kubelet-pod"

reconciler_common.go:231] "operationExecutor.MountVolume started for volume \"kube-api-access-x5n9b\" (UniqueName: \"kubernetes.io/projected/b139c07e-7a5e-4c86-bb72-ffdf351624c9-kube-api-access-x5n9b\") pod \"kubelet-pod\" (UID: \"b139c07e-7a5e-4c86-bb72-ffdf351624c9\") " pod="default/kubelet-pod"


operation_generator.go:718] "MountVolume.SetUp succeeded for volume \"kube-api-access-x5n9b\" (UniqueName: \"kubernetes.io/projected/b139c07e-7a5e-4c86-bb72-ffdf351624c9-kube-api-access-x5n9b\") pod \"kubelet-pod\" (UID: \"b139c07e-7a5e-4c86-bb72-ffdf351624c9\") " pod="default/kubelet-pod"

3. Pod를 위한 Sandbox를 생성합니다.

  • Sandbox란? Pod의 어플리케이션이 실행되는 격리된 환경으로, 호스트 시스템이나 운영 체제에 영향을 끼치지 않도록 컨테이너 인프라 설정을 진행합니다.
util.go:30] "No sandbox for pod can be found. Need to start a new one" pod="default/kubelet-pod"

4. ControlPlane에 생성된 Pod의 정보 및 상태를 업데이트합니다.

kubelet.go:2350] "SyncLoop UPDATE" source="api" pods=[default/kubelet-pod]

1.1 파드 복구 테스트

만약에 애플리케이션의 상태가 비정상적일 경우 kubelet에서 어떤 조치를 취하는 지 확인하기 위해, nginx Pod 생성 시 ReadinessProbe 설정을 통해 컨테이너 내의 애플리케이션의 상태를 체크하도록 설정했습니다.

1. 컨테이너에서 의도적으로 애플리케이션을 중단해보도록 하겠습니다.

# kubectl exec -it -n default kubelet-pod -- bash
root@kubelet-pod:/# service nginx stop

command terminated with exit code 137

2. 애플리케이션과의 연결이 refused되면서 ReadinessProbe fail이 발생합니다.

prober.go:107] "Probe failed" probeType="Readiness" pod="default/kubelet-pod" podUID=407a0aff-81a9-480f-8a3f-7c33fc897e7e containerName="kubelet-pod" probeResult=failure output="Get \"http://10.233.96.164:80/\": dial tcp 10.233.96.164:80: connect: connection refused"

3. Kubelet에서 문제가 되는 컨테이너를 삭제합니다.

scope.go:115] "RemoveContainer" containerID="605abbb3e434bb15034b4006dd756d004ddf58c6900bdb8a91c0c295783564ae"

4. 신규 컨테이너가 시작됩니다.

kubelet.go:2375] "SyncLoop (PLEG): event for pod" pod="default/kubelet-pod" event=&{ID:407a0aff-81a9-480f-8a3f-7c33fc897e7e Type:ContainerStarted Data:439608a706674ef951f4f61b258a8f9fb89099c491a3c551c5a880743d17f51b}

5. 신규 컨테이너에 대해 애플리케이션 연결을 확인하고, APIserver에 Pod 상태를 업데이트합니다.

kubelet.go:2447] "SyncLoop (probe)" probe="readiness" status="ready" pod="default/kubelet-pod"

kubelet.go:2447] "SyncLoop (probe)" probe="readiness" status="" pod="default/kubelet-pod"

2. 리소스 관리 및 노드 자동 복구

위에서 Kubelet은 노드의 리소스 사용량을 모니터링 및 관리한다고 말씀드렸는데요. 만약 노드의 리소스가 부족해지거나 한다면 Kubelet에서 어떤 조치를 할지 테스트해보겠습니다.

이번 테스트를 위해서 노드의 “/” 디스크 사용률을 90% 이상 올려보도록 하겠습니다.

# fallocate -l 40G test.txt
# df -h | grep vda1

/dev/vda1        49G   45G  3.7G  93% /

1. “/” 디스크의 사용률이 85%를 넘어가는 경우 Kubelet에서는 노드 복구 차원에서 ephemeral-storage, 즉 임시 스토리지를 회수하기 위해 시도합니다.

eviction_manager.go:346] "Eviction manager: attempting to reclaim" resourceName="ephemeral-storage"

2. 사용하고 있지 않은 컨테이너(Exited 상태의 컨테이너)를 삭제합니다.

container_gc.go:86] "Attempting to delete unused containers"

3. 현재 해당 노드에서 사용하고 있지 않는 컨테이너 이미지를 삭제합니다.

image_gc_manager.go:340] "Attempting to delete unused images"

image_gc_manager.go:400] "Removing image to free bytes" imageID="sha256:a51c89f2d5c84294a0cf86c91b2d04f5ab8ce66ad15f8236ff4ff8f794caa22d" size=1411409975

4. 노드 복구를 위해 해당 노드의 Pod들을 evict 상태로 만들어 임시 스토리지를 회수하기 위해 시도합니다.

  • evict Pod: 노드의 리소스 부족으로 강제 종료된 Pod
eviction_manager.go:357] "Eviction manager: must evict pod(s) to reclaim" resourceName="ephemeral-storage"

eviction_manager.go:375] "Eviction manager: pods ranked for eviction" pods=[kube-system/nginx-proxy-jy-worker04 playcekube/csi-nfs-node-8zjp8 kube-system/calico-node-96bhj kube-system/kube-pro>
eviction_manager.go:578] "Eviction manager: cannot evict a critical pod" pod="kube-system/nginx-proxy-jy-worker04"
eviction_manager.go:578] "Eviction manager: cannot evict a critical pod" pod="playcekube/csi-nfs-node-8zjp8"
eviction_manager.go:578] "Eviction manager: cannot evict a critical pod" pod="kube-system/calico-node-96bhj"
eviction_manager.go:578] "Eviction manager: cannot evict a critical pod" pod="kube-system/kube-proxy-6hwcg"


eviction_manager.go:407] "Eviction manager: unable to evict any pods from the node"

5. 노드의 상태를 Control Plane의 APIserver에 전달하여, 해당 노드에 Pod가 배치되지 않도록 조치합니다.

kubelet_node_status.go:669] "Recording event message for node" node="jy-worker04" event="NodeHasDiskPressure"

3. 네트워킹

마지막으로 Kubelet이 컨테이너의 네트워크를 어떻게 관리하고 관여하는 지 테스트를 진행해보도록 하겠습니다.

테스트를 위해 jy-worker04 노드에 위치한 Pod의 네트워크 인터페이스를 제거해보도록 하겠습니다.

1. ControlPlane에서 jy-worker04 노드의 Pod가 갖는 네트워크 인터페이스를 확인합니다.

# calicoctl get workloadEndpoints -A | grep worker04

default                hpa-statefulset-1      jy-worker04    10.233.96.157/32   cali5fcdea79e99

2. jy-worker04 노드에서 hpa-statefulset-1 Pod의 네트워크 인터페이스를 조회합니다.

root@jy-worker04:~# ip address show cali5fcdea79e99
16: cali5fcdea79e99@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UP group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netns cni-1b6a69c6-45b7-d9ad-17e6-a6264d5c1523
    inet6 fe80::ecee:eeff:feee:eeee/64 scope link
       valid_lft forever preferred_lft forever

root@jy-worker04:~# ip netns list

cni-1b6a69c6-45b7-d9ad-17e6-a6264d5c1523 (id: 0)

3. hpa-statefulset-1 Pod의 CNI(Container Network Interface)를 해제합니다.

root@jy-worker04:~# cd /var/run/netns/

root@jy-worker04:/var/run/netns# ls
cni-1b6a69c6-45b7-d9ad-17e6-a6264d5c1523

root@jy-worker04:/var/run/netns# umount /var/run/netns/cni-1b6a69c6-45b7-d9ad-17e6-a6264d5c1523


root@jy-worker04:/var/run/netns# rm -rf cni-1b6a69c6-45b7-d9ad-17e6-a6264d5c1523

4. kubelet을 재기동합니다.

root@jy-worker04:~# systemctl restart kubelet.service

5. hpa-statefulset-1 Pod의 sandbox의 IP 주소가 유실된 것을 확인 한 Kubelet이 신규 네트워크 인터페이스를 발급합니다.

util.go:60] "Sandbox for pod has no IP address. Need to start a new one" pod="default/hpa-statefulset-1"
jy-worker04 kernel: [71719.798466] IPv6: ADDRCONF(NETDEV_CHANGE): cali5fcdea79e99: link becomes ready

6. hpa-statefulset-1 Pod에 신규 IP 및 CNI 인터페이스가 할당 된 것을 확인할 수 있습니다.

root@jy-worker04:~# ip address show cali5fcdea79e99
17: cali5fcdea79e99@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UP group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netns cni-dcfd3a5c-0fec-d11a-0509-3d77feb4f70e
    inet6 fe80::ecee:eeff:feee:eeee/64 scope link
       valid_lft forever preferred_lft forever

# calicoctl get workloadEndpoints -A | grep worker04

default                hpa-statefulset-1      jy-worker04    10.233.96.158/32    cali5fcdea79e99

마치며..

지금까지 Kubelet의 기능을 알아보고 테스트를 통해 직접 기능을 확인해보는 시간을 가졌습니다.

Kubelet은 Kubernetes 노드에서 실행되는 핵심 노드 에이전트로, Pod의 실행과 상태 관리, 노드 복구 및 리소스 관리 등 클러스터의 원활한 운영과 안정성을 보장하기 위해 다양한 기능을 제공합니다.

이번 포스팅을 통해 Kubelet에 대해 알아보면서 저도 기존에 알지 못했던 기능을 알고 테스트해볼 수 있었습니다. 다음에는 Kubernetes의 다른 주요 컴포넌트들도 알아보면서 Kubernetes를 넓고 깊게 볼 수 있는 시간을 준비해보겠습니다.

긴 글 읽어주셔서 감사합니다!


참고 문서

[1] https://www.armosec.io/glossary/kubelet/

[2] https://velog.io/@gun_123/Kubernetes-Kubelet

[3]https://www.scaler.com/topics/kubelet-in-kubernetes/

Playce Cloud팀의 카페인같은 엔지니어 변진영입니다. 클라우드 세상에서 언제나 깨어 있고, 깨우치기 위해 노력합니다.

Leave a Reply

Your email address will not be published. Required fields are marked *