CPU 를 나눠 쓰는 규칙을 손으로 바꿔 본다
이 실습은 VM 에서 돕니다
우분투 24.04 VM 한 대입니다(vCPU 2개). cgroup 파일에 직접 쓰고 프로세스를 옮기는 일은 커널 권한이 필요해서 실습 파드에서는 할 수 없습니다.
목표
같은 CPU 를 두 프로세스가 다툴 때 스케줄러가 무엇을 기록하는지 읽습니다.
nice 로 비율을 바꾸고, cgroup v2 의 cpu.weight 로 그룹 단위 비율을 바꾸고,
cpu.max 로 상한을 걸어 스로틀링이 어떻게 숫자로 남는지 봅니다.
마지막으로 그 숫자가 쿠버네티스의 requests·limits 와 어떻게 이어지는지 씁니다.
왜 중요한가
"CPU 사용률은 낮은데 응답이 느리다" 는 신고의 상당수가 스케줄러 이야기입니다.
프로세스가 실행되지 못하고 런큐에서 기다린 시간은 사용률 그래프에 나오지
않습니다. 그리고 컨테이너에 CPU limit 을 걸면 커널은 cpu.max 로 그 그룹을 주기마다
멈추는데, 이 멈춤도 사용률이 아니라 nr_throttled 라는 다른 숫자에 남습니다.
무엇을 봐야 하는지 알면 "CPU 를 더 주세요" 와 "limit 을 풀어 주세요" 를 구별할 수
있습니다.
단계
kx-spin 3을 CPU 0 에 묶어 혼자 돌리고 결과를/root/kx/sched/01-alone.txt에 저장하세요.- 같은 CPU 에
kx-spin 3둘을 동시에 돌리고 두 줄을/root/kx/sched/02-contend.txt에 저장하세요. - 하나는
nice -n 10, 하나는 그대로 같은 CPU 에서 동시에 돌리고/root/kx/sched/03-nice.txt에 저장하세요. - cgroup
/sys/fs/cgroup/kx-sched를 만들고 그 아래gold(cpu.weight 800)와bronze(100)를 만드세요. gold와bronze에kx-spin 6을 하나씩 넣어 CPU 0 에서 동시에 돌리세요.kx-sched/capped에cpu.max를20000 100000으로 걸고kx-spin 5를 돌려 스로틀링을 일으키세요.capped그룹의 CPU 압력(PSI)을/root/kx/sched/07-psi.txt에 저장하세요.- 쿠버네티스와의 연결을
/root/kx/sched/08-k8s.md에 정리하세요.
참고
taskset -c 0 <명령>은 그 명령을 CPU 0 에서만 돌게 합니다. 두 명령을 동시에 돌리려면 하나를&로 띄우고wait로 기다립니다.- cgroup v2 에서 자식 그룹이
cpu.weight를 가지려면 부모의cgroup.subtree_control에+cpu를 써야 합니다. - 프로세스를 그룹에 넣는 것은 그 그룹의
cgroup.procs에 PID 를 쓰는 것입니다.( echo $BASHPID > …/cgroup.procs; exec 명령 )꼴이 편합니다. - 흔한 실수: 그룹과 값만 만들고 프로세스를 넣지 않는 것. 그 그룹의
cpu.stat은 0 에 머뭅니다.
혼자 돌 때의 기록
taskset -c 0 kx-spin 3 을 실행해 나오는 한 줄을 /root/kx/sched/01-alone.txt 에 저장하세요. run_ns 는 CPU 에서 실제로 돈 시간, wait_ns 는 런큐에서 기다린 시간입니다.
kx-spin 은 끝날 때 /proc/<pid>/schedstat 의 세 숫자를 풀어서 찍습니다. 혼자 돌면 기다릴 일이 거의 없어야 합니다. 다른 일을 함께 돌리고 있지 않은지 확인하세요.
같은 CPU 를 둘이 다툰다
taskset -c 0 kx-spin 3 두 개를 동시에 돌리고, 나온 두 줄을 /root/kx/sched/02-contend.txt 에 저장하세요. 두 줄 모두 wait_ns 가 run_ns 의 절반 이상이어야 합니다.
하나를 & 로 띄우고 바로 다른 하나를 실행한 뒤 wait 하면 됩니다. 둘 다 같은 CPU 에 묶어야 합니다. 서로 다른 CPU 에 놓으면 다툼이 없어 기다림도 없습니다.
nice 가 바꾸는 것은 비율이다
같은 CPU 0 에서 nice -n 10 kx-spin 3 과 kx-spin 3 을 동시에 돌리고 두 줄을 /root/kx/sched/03-nice.txt 에 저장하세요. nice=0 쪽의 run_ns 가 nice=10 쪽의 네 배 이상이어야 합니다.
nice 값은 CFS(EEVDF) 가중치로 바뀝니다. 0 은 1024, 10 은 110 이라 같은 시간 동안 대략 9 대 1 로 나눕니다. taskset 과 nice 는 앞뒤로 겹쳐 쓸 수 있습니다.
그룹 단위로 비율을 정한다
/sys/fs/cgroup/kx-sched 를 만들고 자식 그룹에서 CPU 컨트롤러를 쓸 수 있게 한 뒤, 그 아래 gold(cpu.weight 800)와 bronze(cpu.weight 100)를 만드세요.
cgroup v2 는 디렉터리를 만들면 그룹이 생깁니다. 그런데 자식 그룹에 cpu.weight 파일이 생기려면 부모의 cgroup.subtree_control 에 컨트롤러를 켜야 합니다. 켜기 전에 만든 자식에도 켜는 순간 파일이 나타납니다.
가중치대로 나뉘는지 본다
gold 와 bronze 에 kx-spin 6 을 하나씩 넣어 CPU 0 에서 동시에 돌리세요. 끝난 뒤 두 그룹의 cpu.stat 에서 usage_usec 가 gold 쪽이 1초 이상이고 bronze 의 네 배 이상이어야 합니다.
그룹에 넣는 것은 그 그룹의 cgroup.procs 에 PID 를 쓰는 것입니다. 서브셸에서 $BASHPID 를 쓰고 exec 로 명령을 이어 받으면 그 명령이 처음부터 그룹 안에서 돕니다. 따로따로 돌리면 다툼이 없어 비율이 1 대 1 로 나옵니다.
상한을 걸면 멈춘다
/sys/fs/cgroup/kx-sched/capped 를 만들고 cpu.max 를 20000 100000 으로 건 뒤, 그 그룹 안에서 kx-spin 5 를 돌리세요. 끝난 뒤 그 그룹의 cpu.stat 에 nr_throttled 가 10 이상이어야 합니다.
cpu.max 의 두 숫자는 '한 주기(마이크로초) 동안 쓸 수 있는 시간' 과 '주기' 입니다. 20000/100000 은 CPU 0.2 개입니다. 다 쓰면 그 주기가 끝날 때까지 그룹 전체가 멈추고, 멈춘 횟수가 nr_throttled 에 쌓입니다.
멈춘 시간은 압력으로 남는다
/sys/fs/cgroup/kx-sched/capped/cpu.pressure 를 /root/kx/sched/07-psi.txt 에 저장하세요. total= 값이 0 보다 커야 합니다.
PSI(Pressure Stall Information)는 '할 일이 있는데 CPU 를 못 받은 시간' 을 마이크로초로 쌓습니다. 시스템 전체 값은 /proc/pressure/cpu 에 있지만 여기서는 그룹 하나의 값을 봅니다. 그룹마다 파일이 따로 있습니다.
쿠버네티스의 말로 옮긴다
/root/kx/sched/08-k8s.md 에 ## requests 와 cpu.weight·## limits 와 cpu.max·## 무엇을 관측할까 세 절을 쓰세요. 본문에 cpu.weight·cpu.max·nr_throttled 가 나오고, 6단계 capped 그룹의 nr_throttled 값을 그대로 인용해야 합니다.
kubelet 은 파드의 CPU requests 를 cgroup 의 가중치로, limits 를 cpu.max 의 할당량으로 바꿉니다. 그래서 limit 이 걸린 파드는 노드가 한가해도 멈출 수 있습니다. 6·7단계의 숫자가 그 장면입니다.