Study/Linux

[Linux]Load Average와 시스템 부하

seomj 2024. 4. 9. 02:38

Load가 높다/낮다고 표현하는 이 값의 의미는 무엇이고 시스템에 어떤 영향을 미치는지, 그리고 이 값을 바탕으로 시스템의 부하를 어떻게 결정하면 좋을지를 살펴보자.


Load Average

프로세스의 상태 중 R과 D 상태에 있는 프로세스 개수의 1분, 5분, 15분마다의 평균 값

얼마나 많은 프로세스가 실행 혹은 실행 대기 중이냐를 의미하는 수치이다.

프로세스의 수를 세는 것이기 때문에 시스템에 있는 CPU Core 수가 몇 개냐에 따라 각각의 값은 의미가 상대적이다.

ex. 하나의 Run Queue에 두 개의 프로세스가 있는 경우(CPU Core가 1개)와 서로 다른 CPU의 Run Queue에 두 개의 프로세스가 있는 경우(CPU Core가 2개) 모두 Load Average 값은 2의 근사값이 나올 것이다.

전자의 경우 한 번에 하나만 실행되기 때문에 나머지는 대기 상태 → 현재 시스템이 처리할 수 있는 프로세스보다 조금 더 많은 프로세스가 있다.

후자의 경우 동시에 실행 → 현재 시스템에서 처리할 수 있는 만큼의 프로세스가 있다.

 

계산 과정

root@worker1:~# uptime
 06:17:52 up  6:46,  1 user,  load average: 1.31, 0.50, 0.23

 

uptime 명령 분석

root@worker1:~# strace -s 65535 -f -t -o uptime_dump uptime
 06:18:21 up  6:46,  1 user,  load average: 1.11, 0.53, 0.25
root@worker1:~# ls
uptime_dump

strace 명령어는 시스템 콜들과 시그널들을 추적하기 위해 사용된다.

  • -f 는 자식 프로세스를 동일한 파일에 캡처하게하고, 추적 출력에서 별도의 프로세스들의 앞에는 PID 번호가 붙는다
  • -t 는 각 라인의 시작 시간을 출력한다.
  • -o 는 출력이 길어서 실시간 분석이 어려울 수 있기 때문에 파일에 추적을 출력한다.
root@worker1:~# tail uptime_dump 
224519 06:18:21 close(4)                = 0
224519 06:18:21 openat(AT_FDCWD, "/proc/loadavg", O_RDONLY) = 4
224519 06:18:21 lseek(4, 0, SEEK_SET)   = 0
224519 06:18:21 read(4, "1.11 0.53 0.25 1/563 224519\\n", 8191) = 28
224519 06:18:21 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
224519 06:18:21 write(1, " 06:18:21 up  6:46,  1 user,  load average: 1.11, 0.53, 0.25\\n", 61) = 61
224519 06:18:21 close(1)                = 0
224519 06:18:21 close(2)                = 0
224519 06:18:21 exit_group(0)           = ?
224519 06:18:21 +++ exited with 0 +++

 

파일의 마지막 부분을 보면 uptime 명령은 /proc/loadavg 파일을 열어서 그 파일의 내용을 읽어 출력해주는 명령인 것을 알 수 있다.

root@worker1:~# cat /proc/loadavg 
0.34 0.30 0.25 1/547 228319

 

그렇다면 이 값들은 어떻게 만들어지는가?

커널 코드 확인해보자. /proc 파일 시스템과 관련된 커널 소스는 fs/proc/ 에 위치해 있다.

직접 확인이 불가능하여 kernel.org에서 커널 버전을 찾아 진행했다. 본인의 경우 5.15.0-88-generic으로 5.15.153을 참고했다.

/fs/proc/loadavg.c

15번째 줄: avnrun이라는 배열

17번째 줄: get_avnrun() 함수를 통해 avnrun 배열에 값을 입력

19번째 줄: avnrun 배열 값을 토대로 Load Average 출력

결국 계산하는 함수는 get_avnrun()으로 이를 찾아야 한다. 다음과 같은 명령으로 찾을 수 있다.

grep -R get_avnrun ./*

실제로 계속 따라가보면 확인하고자 하는 코드는 calc_global_load() 함수에 있다.

/kernel/sched/loadavg.c

366번째 줄: calc_load_tasks 값을 atomic_long_read()라는 매크로를 통해서 읽어온 후 active 값에 넣는다.

369번째 줄: active 값을 바탕으로 avenrun[] 배열에 있는 값들을 calc_load() 함수를 이용해서 계산한다.

 

그렇다면 active 값인 calc_load_tasks를 찾아보자.

395번째 줄: delta가 0이 아니면 해당 값을 calc_load_tasks 변수에 입력한다.

 

83번째 줄: nr_active 변수에 Run Queue를 기준으로 nr_running 상태의 프로세스 개수를 입력한다. 이 프로세스들이 바로 R 상태의 프로세스이다.

84번째 줄: nr_active 변수에 Run Queue를 기준으로 nr_uninterruptible 상태의 프로세스 개수를 더해준다. 이 프로세스들이 바로 D 상태의 프로세스이다.

86 ~ 87번째 줄: delta는 nr_active 값이 기존에 계산된 값과 다르다면 그 차이의 값에 해당한다.

 

정리하면 다음과 같은 흐름이 된다.

 

계산하는 순간을 기준으로 nr_running 상태의 프로세스 개수와 nr_uninterruptible 상태의 프로세스 개수를 합한 값을 바탕으로 계산되는 것이다.

 

CPU Bound vs I/O Bound

Load Average가 높다는 것은 I/O에 병목이 생겨서 I/O 작업을 대기하는 프로세스가 많을 수도 있다는 의미이다. Load Average 값만으로는 시스템에 어떤 상태의 부하가 일어나는지 확인하기 어렵다는 뜻이기도 하다.

 

부하를 일으키는 프로세스는 크게 2가지로 나눌 수 있다.

  • CPU 자원을 많이 필요로 하는 CPU Bound (nr_running)
  • 많은 I/O 자원을 필요로 하는 I/O Bound (nr_uninterrunptible)

어떤 부하인지는 중요하다. 부하의 종류에 따라 해결 방법이 달라지기 때문이다.

 

부하의 정체 확인하기

그렇다면 부하의 원인은 어떻게 확인할 수 있을까?

vmstat

root@worker1:~# vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0      0 103484  31912 757456    0    0   103   192  352  600  6  3 91  0  0

r: 실행되기를 기다리거나 현재 실행되고 있는 프로세스의 개수, nr_running

b: I/O를 위해 대기열에 있는 프로세스의 개수, nr_uninterruptible

 

추가로

  • memory에서 free 항목은 해당 OS의 실제 남은 메모리를 의미한다.
  • system 항목은 시스템 콜 및 인터럽트에 관련된 정보를 보여준다.
  • cpu 항목은 CPU 사용률을 나타낸다.

 

Load Average가 시스템에 끼치는 영향

같은 수치의 Load Average라고 해도 그 원인에 따라 영향이 다를 수 있다.

 

간단한 테스트

nginx와 java를 통해서 간단한 GET 요청 처리 세팅을 한다.

그리고 각각 CPU 기반의 부하와 I/O 기반의 부하를 일으키도록 한다.

테스트 결과, CPU 기반의 부하는 응답 속도가 9~10초, I/O 기반의 부하는 응답 속도가 8초 초반대가 나왔다.

*top 명령을 통해 nginx, java, 스크립트 파일 각각이 얼마만큼의 CPU를 사용하고 있는지 확인이 가능하다.

 

이처럼 프로세스가 어떤 시스템 자원을 많이 쓰느냐에 따라서 부하가 시스템에 미치는 영향이 다르다.

 

정리

  • Load Average는 실행 중 혹은 실행 대기 중이거나 I/O 작업 등을 위해 대기 큐에 있는 프로세스들의 수를 기반으로 만들어진 값이다.
  • Load Average 자체의 절대적인 높음과 낮음은 없으며 현재 시스템에 장착되어 있는 CPU 코어를 기반으로 한 상대적인 값으로 해석해야 한다.
  • 커널에도 버그가 있으며 Load Average 값을 절대적으로 신뢰해서는 안된다.
  • vmstat 툴 역시 시스템의 부하를 측정하는 데 사용될 수 있으며 r과 b를 눈여겨볼 필요가 있다.
  • /proc/sched_debuf는 vmstat 툴을 통해 확인할 수 있는 것보다 더 자세한 정보들을 제공해주며 특히 nr_running과 runnable tasks 항목에서는 각 CPU에 할당된 프로세스 수와 프로세스의 PID 등의 정보를 확인할 수 있다.

 

 

 

<참고>

DevOps와 SE를 위한 리눅스 커널 이야기

uptime: https://access.redhat.com/ko/articles/3118571

vmstat: https://waspro.tistory.com/155