전공영역 공부 기록

리눅스 컨테이너는 cpu 자원이 어떻게 제어될까?

악분 2024. 9. 22. 01:24
반응형

글은 리눅스가 어떻게 컨테이너 cpu 제어하는지 설명합니다.

 

 

1. 컨테이너가 실행된다는 의미

컨테이너가 실행된다는 것은 무슨 의미일까요? 컨테이너는 운영체제가 관리하는 프로세스이기 때문에 프로세스가 실행된다는 의미와 같습니다. 결국 컨테이너가 실행된다는 것은 프로세스가 실행된다는 의미와 같습니다.

 

프로세스가 실행된다는 의미는 CPU 시간을 할애하여 프로세스의 연산을 수행하는 것입니다. 운영체제는 CPU 여러 프로세스를 효율적으로 연산할 있도록 프로세스를 스케쥴링 합니다. 스케쥴링은 알고리즘에 따라 동작이 다릅니다.

 

2. 리눅스가 컨테이너 CPU자원 할당을 제어하는 원리

리눅스에서는 모든 프로세스가 최대한 공정하게 CPU 할당받을 있도록 CFS(Completely Fair Scheduler) 알고리즘을 사용합니다.

 

CFS 알고리즘은 모든 프로세스에게 CPU 시간을 공정하게 분배하려고 노력합니다. 그러나 현실적인 시나리오에서는 특정 프로세스가 CPU 자원을 필요로 하거나, 반대로 제한되어야 하는 경우가 있습니다. 사용자가 시나리오에 맞게 프로세스에 CPU자원할당을 설정할 있어야 하는데, 역할을 cgroup(Control group) 합니다.

 

cgroup 프로세스를 그룹으로 묶어 cpu, 메모리 등의 자원을 제어합니다. cgroup 통해 설정된 자원은 CFS 알고리즘 실행에 영항을 줍니다. 예를 들어 A이름을 갖는 cgroup cpu 30% 사용하도록 제한하면, A cgroup 속한 프로세스는 cpu 30% 사용하게 됩니다.

 

컨테이너는 프로세스이기 때문에, 컨테이너 또한 cgroup 사용하여 cpu 제한합니다.

 

3. cgroup 확인 방법

cgroup 설정은 파일로 관리되고 컨테이너는 cgroup설정을 마운트해서 사용합니다. 따라서 마운트 정보를 확인하면 cgroup파일이 있는 경로가 보입니다.

mount | grep cgroup

 

cgroup v1 v2 있는데 2024년을 기준으로 v2 변경되는 추세입니다. 아래 예제는 cgroup v2 사용합니다. 마운트 경로는 /sys/fs/cgroup으로 고정되어 있습니다.

 

cgroup 설정파일은 cpu, memory 매우 많습니다.

 

4. 쿠버네티스 pod cgroup 경로 확인방법

컨테이너가 마운트 cgroup파일들은 컨테이너를 실행하는 호스트에서도 찾을 있습니다. 원리를 사용하여 호스트에서 쿠버네티스 pod 사용하는 cgroup 찾을 있습니다.

 

pod cgroup을 찾는 과정은 아래와 같습니다. pod uuid를 찾고 호스트에서 uuid로 cgroup 경로를 찾으면 됩니다.

kubectl get pod <pod_name> -o jsonpath='{.metadata.uid}'
POD_UID_UNDERSCORE=$(kubectl get pod nginx-pod -o jsonpath='{.metadata.uid}' | tr '-' '_')
find /sys/fs/cgroup/ -name "*${POD_UID_UNDERSCORE}*"

 

아래 예제는 busybox-cpu1 pod cgroup 찾는 예제입니다.

 

pod 1 이상 컨테이너를 실행할 있으므로, pod cgroup하위에 컨테이너 갯수만큼 cgroup 있습니다.

 

아래 예시는 busybox-cpu1 pod에서 pause컨테이너의 cgroup 찾는 과정입니다. cgroup 설정파일   cgroup.procs파일이 cgroup 적용할 프로세스 id입니다. 프로세스 id 정보를 조회하면 pause컨테이너라는 것을 확인 있습니다.

 

$ cat cgroup.procs
972

$ ps -fp 972
UID          PID    PPID  C STIME TTY          TIME CMD
65535        972     940  0 00:32 ?        00:00:00 /pause

 

5. pod 컨테이너의 request cgroup 연관관계

cgroup pod 컨테이너의 request 어떻게 제어할까요? 아래 예제처럼 request.cpu 0.3으로 설정하는 것은 어떤 뜻일까요? cpu 0.3 core 사용한다는 의미일까요?

 

정답은 cpu 시간을 상대적으로 0.3만큼 사용하겠다는 의미입니다. 0.3코어가 아니라 상대적으로 0.3시간만큼 cpu 쓰겠다는 의미입니다. 쿠버네티스는 request.cpu cgroup 이해하는 값으로 변환하고 cgroup 변환된 값을 읽어 프로세스에게 cpu 시간을 상대적으로 할당합니다. cgroup v1에서는 cpu share 변환되고 cgroup v2에서는 cpu weight 변환됩니다.

 

상대적으로 cpu 시간이 할당된다는 의미가 매우 중요합니다. request는 코어가 아니라 시간이 할당된다는 의미입니다. 따라서 다른 프로세스가 cpu 시간을 사용하게 되면, pod 정의된 request보다 cpu 시간을 있습니다.

 

6. request.cpu 예제

예제에서는 정말 request.cpu 코어가 아니라 cpu시간을 상대적으로 쓰겠다는 것을 보여줍니다. 테스트 결과를 쉽게 분석하기 위해 쿠버네티스 노드는 1core 설정했습니다.

 

노드에 request 1.5 cpu 설정한 pod 있으면 pod cpu메트릭은 1.5 아니라 1 나옵니다. 1core 아니라 cpu 100% 시간을 pod 사용했다는 의미로 해석됩니다.

 

request.cpu 0.5 갖는 pod 생성하면 pod cpu 어떻게 실행될까요? pod 3:1비율로 cpu시간을 사용합니다. 1.5:0.5 3:1이므로 pod 3:1비율만큼 pod cpu시간을 사용합니다.

 

request.cpu cgroup값으로 변환됩니다. cgroup v2에서는 cpu.weight입니다. cpu.weight kubectl top결과와 마찬가지로 3:1비율로 설정됩니다.

 

7. pod 컨테이너의 limit cgroup 연관관계

limit cpu pod 할애하는 시간을 절대시간으로 제한합니다. request.cpu 연관지으면 리눅스 운영체제는 cpu시간을 request 참조하여 상대적으로 pod cpu시간을 할애하고, 할애한 시간이 limit 못넘게 합니다.

 

limit.cpu cgroup v1에서 cpu.cfs_quota_us 변환되고 v2에서는 cpu.max값으로 변환됩니다.

 

limit.cpu request 다르게 계산식이 있습니다. quota / period 계산식입니다. 예를 들어 아래 예제에서는 limit.cpu 1.5 어떻게 quota, period값으로 전환되는지 보여줍니다. quota 150000ms, period 100000ms 변환되고 계산식에 의해 1.5 됩니다.

 

period 리눅스 CFS스케쥴러의 주기입니다. 디폴트로 100ms 설정되어 있고 100ms마다 프로세스에게 얼마나 cpu시간을 할애할지 결정합니다. 그리고 quota 프로세스에게 cpu시간을 할애하는 절대값입니다.

 

부록. systemd 디폴트로 cgroup 사용

systemd 실행되는 프로세스는 디폴트로 cgroup 사용합니다. docker, kubelet 프로세스는 보통 systemd로 실행되기 때문에  cgroup 사용합니다. 아래 예제는 init프로세스를 systemd 실행했을 cgroup 적용된 결과입니다.

systemctl status

반응형