LabHub
배우기 러닝패스 코스

Linux障害対応

ロードアベレージはCPU使用率ではない

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

리눅스의 부하 평균은 CPU 대기만 세지 않는다. 디스크나 NFS 응답을 기다리는 D 상태 태스크도 함께 센다. 그래서 CPU 가 놀아도 로드가 24 가 될 수 있고, 그때 범인은 스토리지다.

概念マップ: CPU 대기만 세지 않는다.・둘 다 틀렸다.・개수(태스크 수)・시스템 자원 전반에 대한 수요를 재겠다

왜 이게 필요했나

새벽 알람. 8코어 서버의 부하 평균이 24.31, 22.08, 14.95 인데 top 의 CPU 유휴는 60% 가 넘는다. 여기서 두 갈래로 갈린다 - "모니터링이 잘못됐다" 또는 "CPU 를 3배 늘려야 한다". 둘 다 틀렸다.

단위부터 다르다. 사용률은 비율(0~100%)이고 로드는 개수(태스크 수) 다. 그리고 "로드 = CPU 대기열 길이"는 다른 유닉스에서는 맞지만 리눅스에서는 틀리다.

어떻게 동작하나

커널은 5초에 한 번 각 CPU 런큐를 샘플링한다.

nr_active = 실행 중이거나 실행 대기(R) + 중단 불가 대기(D)

리눅스는 1993년에 TASK_UNINTERRUPTIBLE(D 상태)을 이 계산에 더했다. 의도는 CPU 수요만이 아니라 시스템 자원 전반에 대한 수요를 재겠다는 것이었다. D 상태는 시그널로도 못 깨우는 커널 내부 대기다 - 블록 I/O 완료, 페이지 캐시 잠금, 하드 마운트 NFS 응답 대기, 일부 커널 뮤텍스. 이 목록에 CPU 는 없다.

그리고 이 값은 산술평균이 아니라 지수감쇠 평균이다. 계단처럼 부하가 오르면 1분 평균은 1분 뒤에 약 63% 만 반영되고 99% 까지 약 5분이 걸린다. 여기서 두 가지가 따라온다.

진단 순서는 이렇다.

cat /proc/loadavg          # 네 번째 필드 '실행가능/전체'를 함께 본다
ps -eo state --no-headers | sort | uniq -c   # R 과 D 를 센다
vmstat 1 5                 # b 열이 D 상태 개수, wa 가 I/O 대기 비율
iostat -x 1 3              # await 와 큐 깊이로 판단 (%util 100% 는 포화가 아닐 수 있다)

로드가 24 인데 R 이 2 라면 나머지 22 는 D 다. 그러면 답은 CPU 가 아니라 스토리지 쪽에 있다.

"코어 수로 나눠라"가 무너지는 지점들도 알아야 한다.

  1. D 상태 오염 - 위에서 본 그대로
  2. 평균의 지연 - 30초짜리 스파이크는 1분 평균에 절반도 안 드러난다
  3. SMT - nproc 16 이 물리 코어 8개일 수 있다
  4. cgroup 스로틀링 - 스로틀당한 태스크는 런큐에서 아예 빠지므로 로드에 잡히지 않는다
  5. 귀속 정보 부재 - 전역 스칼라 하나라, 알람으로 할 수 있는 일이 "들어가서 봐라" 뿐이다

현장에서 만나는 모습

컨테이너의 조용한 정지. cgroup 에 CPU 상한이 있으면 커널은 주기(기본 100ms)마다 쿼터를 배분하고, 주기 안에서 소진하면 남은 시간 동안 그룹 전체를 런큐에서 빼 버린다. limit 1코어에 스레드 8개면 100ms 쿼터를 12.5ms 만에 소진하고 나머지 87.5ms 는 완전히 멈춘다. 그런데 평균 사용률은 12.5% 근처로 보이고, 로드에도 안 잡히고, steal 에도 안 나온다. cpu.statnr_throttled / nr_periods 비율만이 이 사실을 알려 준다. 5% 를 넘으면 조사, 10% 를 넘으면 조치 대상이다.

steal time. topst 는 "내가 쓰고 싶었는데 하이퍼바이저가 다른 게스트에게 준 시간"이다. 애플리케이션을 아무리 최적화해도 이 수치는 내려가지 않는다. 1% 미만은 정상, 5~10% 지속이면 조치, 10% 초과면 그 호스트에서는 어떤 튜닝도 무효다. 그리고 게스트 안에서 reboot 해도 물리 호스트는 바뀌지 않는다 - 클라우드 API 의 stop/start 라야 재배치된다.

PSI 가 답하는 질문. 로드는 "몇 개가 기다리는가"만 말하고 "얼마나 오래 멈춰 있었는가"는 말하지 않는다. /proc/pressure/{cpu,io,memory}some 은 최소 한 개가 멈춘 시간 비율, full 은 전부가 동시에 멈춘 비율이다. iofull avg10=61 은 최근 10초 중 6초 이상 시스템 전체가 I/O 때문에 정지했다는 뜻이고, 이건 손실된 처리량으로 곧장 번역된다.

부하가 높은데 CPU 는 한가할 때

load average 가 40인데 top 의 CPU 사용률은 5%인 상황은 고장이 아니다. 리눅스의 부하는 실행 대기만 세는 것이 아니라 D 상태(디스크 대기)도 함께 센다. 그래서 부하가 높다는 것은 "바쁘다" 가 아니라 "무언가를 기다리는 것이 많다" 는 뜻이다.

먼저 어느 종류인지 가른다. vmstatr 열은 실행 대기, b 열은 블록 대기다.

vmstat 1 5
# r  b   swpd  free  buff cache  si so  bi bo  in cs  us sy id wa st

r 이 크면 CPU 가 모자란 것이고, bwa 가 크면 저장소가 병목이다. st(steal)가 0이 아니면 가상 머신이 물리 CPU 를 못 받고 있는 것이라 호스트 쪽 문제다.

CPU 가 모자랄 때는 코어 수와 비교한다. 부하 4는 1코어에서는 심각하고 8코어에서는 한가하다. nproc 으로 나눠 본 값이 1을 넘는지가 기준이다. 다만 컨테이너 안의 nproc호스트의 코어 수를 말하는 경우가 많으므로, cgroup 의 cpu.max 를 함께 본다.

한 프로세스가 한 코어를 다 쓰는지, 여러 스레드가 나눠 쓰는지 본다.

top -H -p <pid>          # 스레드 단위
pidstat -t -p <pid> 1

CPU 사용률이 정확히 100%에서 멈춰 있으면 단일 스레드가 한 코어를 다 쓰는 것이고, 그때는 코어를 늘려도 아무 소용이 없다.

스로틀링을 놓치지 않는다. 컨테이너에서 CPU 한도가 걸려 있으면 사용률은 낮아 보이는데 응답은 느리다. cgroup 의 통계에 그 횟수가 남는다.

cat /sys/fs/cgroup/cpu.stat     # nr_throttled, throttled_usec

nr_throttled 가 계속 늘고 있다면 한도를 올리거나, 요청 처리 방식을 바꿔 짧은 순간의 폭증을 줄인다.

iowait 는 "CPU 가 놀고 있고 그동안 디스크를 기다린다" 는 뜻일 뿐이다. CPU 가 바쁘면 같은 디스크 지연이 있어도 wa 는 낮게 나온다. 저장소가 느린지는 iostat -x 1await%util 로 판단한다.

다음 실습에서 할 것

CPU 를 태우는 프로세스를 띄워 부하 평균이 실제로 오르는 것을 관찰하고, R 상태와 누적 CPU 시간을 /proc 에서 직접 읽는다. PSI 와 cgroup 스로틀링 카운터를 확인하되, 없는 환경이면 "없다"고 정확히 기록한다. 마지막에는 코어 수로 정규화해 부하를 판정하는 스크립트를 만든다.