k8s

k8s와 cgroup

k8s는 어떻게 linux-cgroup을 이용할까?

by HOON


1

Last updated on Jan. 26, 2025, 2:12 p.m.


random_image

오늘은 CKA를 취득하면서 궁금했던 "Kubernetes (k8s)에서 cgroup을 어떻게 이용하는가"에 대해 작성해보겠습니다.

# cgroup이란??

우선, cgroup에 대해 먼저 설명드리겠습니다.
cgroup이란 Control Group으로 주로 CPU 시간, 메모리, 네트워크 대역폭 등을 처리하는 리눅스 커널 기능입니다.
아래는 실제 리눅스 내 cgroup 경로(/sys/fs/cgroup)에 존재하는 tree 형태입니다.

controlplane $ cd /sys/fs/cgroup | tree . | head -100
.
|-- blkio
|   |-- blkio.reset_stats
|   |-- blkio.throttle.io_service_bytes
|   |-- cgroup.clone_children
|   |-- cgroup.procs
|   |-- cgroup.sane_behavior
|   |-- init.scope
|   |   |-- blkio.reset_stats
|   |   |-- blkio.throttle.io_service_bytes
|   |   |-- blkio.throttle.io_service_bytes_recursive
|   |   |-- blkio.throttle.write_iops_device
|   |   |-- cgroup.clone_children
|   |   |-- cgroup.procs
|   |   |-- notify_on_release
|   |   |-- tasks
|   |-- kubepods.slice
|   |   |-- blkio.reset_stats
|   |   |-- blkio.throttle.io_service_bytes
|   |   |-- blkio.throttle.io_service_bytes_recursive
|   |   |-- cgroup.clone_children
|   |   |-- cgroup.procs
|   |   |-- kubepods-besteffort.slice

# 작동방식

여기서 주목해야할점은, 리눅스 cgroup 관리 폴더에 존재하는 kubepods.slice 입니다.
해당 폴더에에서 k8s에서 요청한 pod 등의 spec을 변환하여 /sys/fs/cgroup에 넣습니다. 그럼 spec을 아래와같이 정의했을때, 실제로 어떤 형태로 cgroup에 들어가게 될까요?

# kubectl apply -f nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
status: {}               

해당 spec을 잠깐 살펴보면, 간단한 nginx 컨테이너이며, 컨테이너가 필요로하는 cpu는 250m, memory는 64Mi이며, 리미트는 500m, 128Mi로
여유롭게 컨테이너를 생성 할 수 있습니다. 이제 해당 pod의 spec을 kubelet에서 어떻게 생성했는지 보겠습니다.
제 환경은 현재 containerd를 사용하고있기에 crictl명령어를 사용하여 컨테이너의 세부사항을 보겠습니다.

    "reason": "",
    "resources": {
      "linux": {
        "cpuPeriod": "100000",
        "cpuQuota": "50000",
        "cpuShares": "256",
        "cpusetCpus": "",
        "cpusetMems": "",
        "hugepageLimits": [],
        "memoryLimitInBytes": "134217728",
        "memorySwapLimitInBytes": "0",
        "oomScoreAdj": "968",
        "unified": {}
      }
    },
    "startedAt": "2025-01-12T23:08:43.538173247Z",
    "state": "CONTAINER_RUNNING",
    "user": null
"cgroupsPath": "kubepods-burstable-poddcd4516f_5c26_4a1b_b7fe_8e2a233ef685.slice:cri-containerd:1c12b7bd...",

내용을 요약해보자면

  • CPU: 컨테이너는 매 0.1초(cpuPeriod) 주기당 50%(cpuQuota)만 CPU를 사용할 수 있습니다. CPU 리소스 우선순위는 낮으며(cpuShares: 256), 특정 CPU 코어에 묶여 있지 않습니다.
  • 메모리: 컨테이너의 메모리 사용량은 최대 128MB(memoryLimitInBytes)로 제한됩니다. 스왑 사용이 비활성화(memorySwapLimitInBytes: 0)되어 있으며, OOM 상황에서 먼저 종료될 가능성이 높습니다(oomScoreAdj: 968).

따라서 저희가 pod 생성시에 요청했던 내용들이 제대로 들어가있는걸 확인 할 수 있습니다. 이 내용을 토대로 systemd 드라이버를 사용하여 cgroup에 저장하게됩니다.

💡Tip 여기서 oomScoreAdj는 점수가 높을수록 컨테이너가 OOM(Out Of Memory, 메모리 부족)상황시에 우선적으로 종료 될 가능성이 높다는 뜻입니다.


crictl inspect를 통해 가져온 cpu, memory 정보와 cgroupsPath를 통해 실제 cgroup에 어떻게 적용 됐는지 확인해보겠습니다.

controlplane $ systemd-cgls --unit kubepods.slice --no-pager
Unit kubepods.slice (/kubepods.slice):
├─kubepods-burstable.slice  ├─kubepods-burstable-podd2808c86_a6e7_432f_a917_c5eeb4ef8e4f.slice   ├─cri-containerd-c32e952e6084e55c89e25e8786ed3719398cfd63bda4888a537998b269097c6e.scope …
│   └─2773 /coredns -conf /etc/coredns/Corefile
│  └─cri-containerd-c295d25466812e309067cc983a41ecaea01bb83d68275925b98956e75ba8de1a.scope …
│    └─2691 /pause
│ └─kubepods-burstable-poddcd4516f_5c26_4a1b_b7fe_8e2a233ef685.slice    ├─cri-containerd-1c12b7bdb2ae56352dd116e2d0da5320bf81505f1502057191da865b1a3ba19a.scope …
│    ├─24147 nginx: master process nginx -g daemon off;    └─24185 nginx: worker process
| |  . . . 
| |  . . . 


node01 $ systemctl show --no-pager cri-containerd-1c12b7bdb2ae...
MemoryCurrent=2850816
CPUUsageNSec=20500316
CPUQuotaPerSecUSec=500ms
CPUQuotaPeriodUSec=100ms
MemoryMin=0
MemoryLow=0
MemoryHigh=infinity
MemoryMax=infinity
MemorySwapMax=infinity
MemoryLimit=134217728

위에서 가져온 cgroupPath를 참고해서 어떤 kubepods인지 확인합니다. 여기서는 kubepods-burstable-poddcd4516f*이기 때문에 일치하는 내용을 찾은결과
cri-containerd-1c12b7bdb2ae56352dd116e2d0da5320bf81505f1502057191da865b1a3ba19a.scope … 으로 확인 할 수 있습니다.
그 이후 유닛을 살펴보면, 저희가 요청한 pod의 spec대로 설정되어있으며, 이 내용을 cgroup의 파일시스템에서 수집하게 됩니다.

또한 cgroup(v1 기준) 에서 /sys/fs/cgroup/cpu/kubepods.slice경로로 이동해보면 아래와같은 파일리스트가 보이며,
세부내용을 하나씩 보면 실제 요청대로 적용된 파일을 확인 할 수 있습니다.

node01 $ ls -al
total 0
drwxr-xr-x 4 root root 0 Jan 12 22:45 .
dr-xr-xr-x 5 root root 0 Jan 12 22:45 ..
-rw-r--r-- 1 root root 0 Jan 13 00:43 cgroup.clone_children
-rw-r--r-- 1 root root 0 Jan 13 00:43 cgroup.procs
-rw-r--r-- 1 root root 0 Jan 13 00:43 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Jan 13 00:43 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Jan 13 00:43 cpu.shares
-r--r--r-- 1 root root 0 Jan 13 00:43 cpu.stat
-rw-r--r-- 1 root root 0 Jan 13 00:43 cpu.uclamp.max
-rw-r--r-- 1 root root 0 Jan 13 00:43 cpu.uclamp.min
-r--r--r-- 1 root root 0 Jan 13 00:43 cpuacct.stat
-rw-r--r-- 1 root root 0 Jan 13 00:43 cpuacct.usage
-r--r--r-- 1 root root 0 Jan 13 00:43 cpuacct.usage_all
-r--r--r-- 1 root root 0 Jan 13 00:43 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Jan 13 00:43 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Jan 13 00:43 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Jan 13 00:43 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Jan 13 00:43 cpuacct.usage_user
drwxr-xr-x 3 root root 0 Jan 12 22:45 kubepods-besteffort.slice
drwxr-xr-x 6 root root 0 Jan 12 22:45 kubepods-burstable.slice
-rw-r--r-- 1 root root 0 Jan 13 00:43 notify_on_release
-rw-r--r-- 1 root root 0 Jan 13 00:43 tasks

# 정리

쿠버네티스에 관심이 갖게 된 계기는 수많은 pod들을 자동으로 생성하고, 종료하고, autoscailing 할 수 있다는 점이였습니다.
오늘은 리눅스 기능인 cgroup을 기반으로 쿠버네티스의 연관성을 알아보았는데, 실제로 k8s에만 그치지않고 리눅스 기반의 기능을 사용하기때문에
리눅스까지 같이 공부 할 수 있는 기회라고 생각합니다.

×
k8s linux


Leave a Comment: