Skip to content

Latest commit

 

History

History
369 lines (306 loc) · 28.3 KB

File metadata and controls

369 lines (306 loc) · 28.3 KB

3.1 Load Average의 정의

man proc 을 통해 loadavg를 찾아보면 다음과 같이 정의하고 있다.

The first three fields in this file are load average figures giving the number of
jobs in the run queue (state R) or waiting for disk I/O(state D) averaged over
1, 5, and 15 minutes

이 파일의 처음 세 개 필드는 로드 평균(load average) 값을 나타내며,
실행 큐(run queue)에 있는 작업(R 상태) 또는
**디스크 I/O를 기다리고 있는 작업(D 상태)**의 수를
1분, 5분, 15분 동안 평균낸 값을 의미한다.

2장에서 확인한 프로세스의 상태의 상태 중 R과 D 상태에 있는 프로세스 개수의 1분, 5분, 15분 마다의 평균 값이라고 설명해 놓았다. 즉, 얼마나 많은 프로세스가 실행중 혹은 실행 대기중이냐를 의미하는 수치이다. Load Average가 높다면 많은 수의 프로세스가 실행중이거나 I/O 등을 처리하기 위한 대기 상태에 있다는 것이며, 낮다면 적은 수의 프로세스가 실행중이거나 대기중이라는 의미이다. 프로세스의 수를 세는 것이기 때문에 시스템에 있는 CPU Core 수가 몇개냐에 따라 각각의 값은 의미가 상대적이다.

CPU Core가 하나인 경우와 2개인 경우 둘 다 Load Average 값은 2의 근사값이 나오겠지만 그 의미는 좀 다르다. 첫번째 경우는 하나의 Run Queue에 두 개의 프로세스가 있으며, 이 경우 한 번에 하나만 실행이 되기 때문에 나머지 하나의 프로세스는 대기 상태에 있을 수밖에 없다. 이는 현재 시스템이 처리할 수 있는 프로세스보다 조금 더 많은 프로세스가 있다는 뜻이다. 하지만 두번째의 경우는 첫번째와 똑같이 Run Queue에 두 개의 프로세스가 있지만 서로 다른 CPU에 있기 때문에 A와 B는 동시에 실행될 수 있다. 현재 시스템에 처리 가능한 만큼의 프로세스가 있는 것이다. 이처럼 같은 Load Average라고 해도 CPU Core가 몇 개냐에 따라 전혀 다른 의미일 수 있다.

3.2 Load Average 계산 과정

이번에는 Load Average가 커널 내부에서 어떻게 계산되는지 살펴보자. 바로 uptime 명령이다.

ubuntu@ip-172-31-0-157:~$ uptime
 12:53:51 up 1 day, 21:58,  1 user,  load average: 0.00, 0.00, 0.00

우선 strace를 이용해서 uptime 명령을 분석해보자.

ubuntu@ip-172-31-0-157:~$ strace -s 65535 -f -t -o uptime_dump uptime
 12:54:52 up 1 day, 21:59,  1 user,  load average: 0.00, 0.00, 0.00
ubuntu@ip-172-31-0-157:~$ ls
malloc_test  malloc_test.c  nice_test.py  uptime_dump

생성된 덤프 파일을 편집기로 열어서 천천히 살펴볼 것이다. execve()를 통해서 bashuptime 명령을 실행시키고, 관련된 라이브러리 파일들을 읽는 과정을 확인할 수 있다. 그러다가 덤프 파일 하단부에 보면 아래와 같은 내용이 보인다.

9114  12:54:52 openat(AT_FDCWD, "/proc/loadavg", O_RDONLY) = 3
9114  12:54:52 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
9114  12:54:52 read(3, "0.00 0.00 0.00 1/143 9114\n", 1024) = 26
9114  12:54:52 close(3)                 = 0
9114  12:54:52 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}) = 0
9114  12:54:52 write(1, " 12:54:52 up 1 day, 21:59,  1 user,  load average: 0.00, 0.00, 0.00\n", 68) = 66

uptime 명령은 /proc/loadavg 파일을 열어서 그 파일의 내용을 읽고 화면에 출력해주는 명령이다. 즉, 직접 Load Average 값을 계산하는게 아니고 커널이 미리 준비해둔 /proc/loadavg 를 단순히 읽어서 보여주는 명령이다. 그럼 이번엔 /proc/loadavg를 열어보자.

ubuntu@ip-172-31-0-157:~$ cat /proc/loadavg
0.00 0.00 0.00 3/140 9134

예상대로 uptime에서 본 값들이 기록되어 있다. 그럼 이 값은 어떻게 만들어지는 걸까? 이제 커널 코드를 살펴볼 차례다.

여기서 중요한 두 가지 사실을 알 수 있는데, active 변수와 calc_load() 함수이다. 먼저 active 변수의 값을 살펴보자. active 변수의 값에 대해 알기 위해서는 calc_load_tasks가 어떤 값을 가지게 되는지 살펴봐야 한다.

  1. nr_active 변수에 Run Queue를 기준으로 nr_running 상태의 프로세스 개수를 입력한다. 이 프로세스들이 바로 R 상태의 프로세스다.
  2. nr_active 변수에 Run Queue를 기준으로 nr_uninterruptible 상태의 프로세스 개수를 더해준다. 이 프로세스들이 바로 D 상태의 프로세스다.
  3. nr_active 값이 기존에 계산된 값과 다르다면 그 차이 값을 구한 후 calc_load_tasks 변수에 입력한다.

이렇게 cpu_load_account_active() 함수가 매번 Tick 주기마다 깨어나서 현재 CPU의 Run Queue에 있는 nr_running 프로세스의 개수와 nr_uninterruptible 프로세스의 개수를 세어서 calc_load_tasks 변수에 넣어준다.

그 후 5초 간격으로 calc_global_load() 함수가 calc_load_tasks 변수 값을 바탕으로 1분, 5분, 15분 마다의 평균 Load Average를 계산해서 넣어준다.

그림 3-2를 보면 커널 타이머가 두 함수를 주기적으로 호출한다. 먼저 calc_load_account_active()가 더 잦은 빈도로 호출되며 그때마다 calc_load_tasks의 변수를 갱신한다. 그 후 calc_global_load() 함수가 호출되어 내부적으로 active 변수에 calc_load_tasks의 값을 저장하고 calc_load() 함수를 통해서 최종적으로 계산된 값을 avenrun[] 배열에 저장한다. 과정이 조금 복잡해 보일 수 있지만, 결국 프로세스의 개수를 센다는 점만 기억하면 된다.

3.3 CPU Bound vs I/O Bound

지금까지 Load Average가 계산되는 과정을 살펴봤다. 결국 Load Average는 상대적인 값이 아니고 계산하는 순간을 기준으로 존재하는 nr_running 상태의 프로세스 개수와 nr_uninterruptible 상태의 프로세스 개수를 합한 값을 바탕으로 계산되는 것이었다.

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

부하를 일으키는 프로세스는 크게 두 가지 종류로 나눌 수 있다. nr_running으로 표현되는, CPU 자원을 많이 필요로 하는 CPU Bound 프로세스와 nr_uninterruptible로 표현되는, 많은 I/O 자원을 필요로 하는 I/O Bound 프로세스이다.

첫번째는 무한루프를 돌면서 수치 연산을 하는 파이썬 스크립트이다. 이 스크립트를 실행시켜서 uptime 명령을 통해서 확인해보면 Load Average가 올라가는 것을 확인할 수 있다.

ubuntu@ip-172-31-0-157:~$ uptime
 14:50:37 up 30 min,  2 users,  load average: 0.30, 0.07, 0.02
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:38 up 30 min,  2 users,  load average: 0.36, 0.08, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:39 up 30 min,  2 users,  load average: 0.36, 0.08, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:40 up 30 min,  2 users,  load average: 0.36, 0.08, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:41 up 30 min,  2 users,  load average: 0.36, 0.08, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:42 up 30 min,  2 users,  load average: 0.36, 0.08, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:43 up 31 min,  2 users,  load average: 0.36, 0.08, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:43 up 31 min,  2 users,  load average: 0.41, 0.10, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:44 up 31 min,  2 users,  load average: 0.41, 0.10, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:45 up 31 min,  2 users,  load average: 0.41, 0.10, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:46 up 31 min,  2 users,  load average: 0.41, 0.10, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:46 up 31 min,  2 users,  load average: 0.41, 0.10, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:47 up 31 min,  2 users,  load average: 0.41, 0.10, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:47 up 31 min,  2 users,  load average: 0.41, 0.10, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:47 up 31 min,  2 users,  load average: 0.41, 0.10, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:48 up 31 min,  2 users,  load average: 0.41, 0.10, 0.03
ubuntu@ip-172-31-0-157:~$ uptime
 14:50:48 up 31 min,  2 users,  load average: 0.46, 0.11, 0.04

이번에는 무한루프를 돌면서 I/O를 발생시키는 파이썬 스크립트를 실행시켜 보자. 이 스크립트를 실행시키면 첫번째 예제와 마찬가지로 Load Average가 올라가는 것을 확인할 수 있다.

ubuntu@ip-172-31-0-157:~$ uptime
 14:53:27 up 33 min,  2 users,  load average: 0.20, 0.11, 0.04
ubuntu@ip-172-31-0-157:~$ uptime
 14:53:28 up 33 min,  2 users,  load average: 0.20, 0.11, 0.04
ubuntu@ip-172-31-0-157:~$ uptime
 14:53:28 up 33 min,  2 users,  load average: 0.26, 0.12, 0.04
ubuntu@ip-172-31-0-157:~$ uptime
 14:53:29 up 33 min,  2 users,  load average: 0.26, 0.12, 0.04
ubuntu@ip-172-31-0-157:~$ uptime
 14:53:30 up 33 min,  2 users,  load average: 0.26, 0.12, 0.04
ubuntu@ip-172-31-0-157:~$ uptime
 14:53:31 up 33 min,  2 users,  load average: 0.26, 0.12, 0.04
ubuntu@ip-172-31-0-157:~$ uptime
 14:53:32 up 33 min,  2 users,  load average: 0.26, 0.12, 0.04
ubuntu@ip-172-31-0-157:~$ uptime
 14:53:35 up 33 min,  2 users,  load average: 0.32, 0.14, 0.05
ubuntu@ip-172-31-0-157:~$ uptime
 14:53:37 up 33 min,  2 users,  load average: 0.32, 0.14, 0.05
ubuntu@ip-172-31-0-157:~$ uptime
 14:53:41 up 33 min,  2 users,  load average: 0.38, 0.15, 0.06

둘 다 비슷한 수준의 Load Average를 보여주고는 있지만 사실 일으키고 있는 부하는 전혀 다른 부하이다. 전자의 경우는 CPU 리소스를 너무 많이 사용해서 발생하는 부하이고, 후자의 경우는 I/O 리소스를 너무 많이 사용해서 발생하는 부하이다. 어떤 부하인지가 중요한 이유는, 부하의 종류에 따라서 해결 방법이 달라지기 때문이다. Load Average가 높다고 해서 단순히 CPU가 더 많은 장비를 사용하는 것으로 해결할 수 없다는 의미이다.

또한 비슷한 Load Average라 하더라도 부하를 일으키는 원인이 무엇이냐에 따라 시스템의 반응 속도가 전혀 다를 수 있다. 그렇다면 부하의 원인을 어떻게 확인할 수 있을까?

3.4 vmstat으로 부하의 정체 확인하기

Load Average 값은 시스템에 부하가 있다는 것을 알려주지만 구체적으로 어떤 부하인지는 알 수 없다. 어떤 부하가 일어나는지에 대한 정보는 vmstat을 통해서 확인할 수 있다.

앞서 사용한 CPU Bound 스크립트를 다시 실행시켜서 vmstat 명령으로 시스템의 상태를 확인해보자.

ubuntu@ip-172-31-0-157:~$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu
 2  0      0 228204  22160 549952    0    0  2571   693  666    5 11  5 77  1  7  0
 1  0      0 228204  22160 549956    0    0     0     0 2020   21 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2012   29 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2010   24 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2012   27 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2014   29 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2010   25 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2012   28 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2010   26 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2021   21 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     8 2016   30 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2010   29 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2016   33 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2014   23 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2014   40 100  0  0  0  0  0
 1  0      0 228204  22160 549956    0    0     0     0 2010   30 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0   100 2024   35 99  1  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2012   21 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0    20 2012   29 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2022   45 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2010   28 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2012   25 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2011   22 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2012   30 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2010   23 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2014   22 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2012   29 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2010   23 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2014   32 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2016   25 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2010   24 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2010   28 100  0  0  0  0  0
 1  0      0 228204  22168 549960    0    0     0     0 2012   24 100  0  0  0  0  0
 1  0      0 227712  22168 550000    0    0     8     0 2022  104 99  1  0  0  0  0
 1  0      0 227712  22168 550000    0    0     0     0 2010   26 100  0  0  0  0  0

그리고 I/O를 일으키는 스크립트를 실행시키고 vmstat의 결과를 살펴보도록 하자.

ubuntu@ip-172-31-0-157:~$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu
 1  1      0 227712  22176 550000    0    0  1610   511  744    4 20  3 70  2  4  0
 0  1      0 227712  22176 550000    0    0     0  4528 2570 3441  4  7  0 87  2  0
 0  1      0 227712  22176 550000    0    0     0  4572 2625 3476  3  9  0 86  2  0
 0  1      0 227712  22176 550000    0    0     0  4532 2625 3442  4  8  0 86  2  0
 0  1      0 227712  22176 550000    0    0     0  4500 2602 3412  4  8  0 87  1  0
 0  1      0 227712  22176 550000    0    0     0  4632 2612 3510  3  7  0 88  2  0
 1  1      0 227712  22176 550000    0    0     0  4588 2637 3484  4  8  0 86  2  0
 0  1      0 227712  22176 550000    0    0     0  4640 2675 3518  3 10  0 85  2  0
 0  1      0 227712  22176 550000    0    0     0  4524 2588 3431  4  7  0 87  2  0
 0  1      0 227712  22176 550000    0    0     0  4536 2617 3444  4  9  0 85  2  0
 0  1      0 227712  22176 550000    0    0     0  4592 2629 3478  4  9  0 85  2  0
 0  1      0 227712  22176 550000    0    0     0  4636 2683 3536  4  8  0 87  1  0
 0  1      0 227712  22176 550000    0    0     0  4596 2636 3482  4  9  0 85  2  0
 0  1      0 227712  22176 550000    0    0     0  4640 2664 3520  4  8  0 86  2  0
 0  1      0 227712  22176 550000    0    0     0  4712 2741 3578  3 11  0 84  2  0
 0  1      0 227712  22176 550000    0    0     0  4660 2634 3535  3  8  0 87  2  0
 0  1      0 227712  22176 550000    0    0     0  4716 2754 3582  4 10  0 84  2  0
 0  1      0 227712  22176 550000    0    0     0  4716 2698 3576  3  9  0 86  2  0
 0  1      0 227712  22176 550000    0    0     0  4524 2623 3436  3  9  0 87  1  0
 0  0      0 227712  22176 549996    0    0     0  3192 1926 2485  2  7 32 57  2  0

두 출력 값 사이의 차이점은 바로 첫 번째 칼럼인 r열과 두번째 열인 b열의 값이다. r과 b가 의미하는 바는 다음과 같다.

Procs
       r: The number of runnable processes (running or waiting for run time).
       b: The number of processes blocked waiting for I/O to complete.

r은 실행되기를 기다리거나 현재 실행되고 있는 프로세스의 개수를, b는 I/O를 위해 대기열에 있는 프로세스의 개수를 말한다. 즉 각각이 nr_running, nr_uninterruptible을 의미한다고 볼 수 있다.

두 스크립트를 돌려보면 비슷한 수준의 Load Average가 나오지만, vmstat으로 확인해보면 CPU가 일으키는 Load Average인지, 아니면 I/O가 일으키는 Load Average인지 확인해볼 수 있다. 사실 I/O가 일으키는 Load Average 값이 1이나 2 정도로 낮은 편에 속한다고 해도(CPU가 1개 혹은 2개인 경우에는 높은 수준의 값이지만) 시스템에 문제를 일으킬 수 있는 소지가 있다. 지속적으로 I/O를 일으키는 프로세스가 시스템에 존재한다는 것을 의미하며, 의도하지 않은 불필요한 프로세스일 가능성이 있다.

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

그럼 같은 수준의 Load Average라면 시스템에 끼치는 영향도 같을까? 부하를 일으키는 원인이 무엇이냐에 따라서 같을 수도 다를 수도 있다. 같은 수치의 Load Average라고 해도 그 원인에 따라 영향이 다를 수 있다는 뜻이다.

간단하게 테스트를 해보자. 테스트 서버에는 nginx와 java를 통해서 간단하게 GET 요청을 처리할 수 있도록 세팅한다. 그리고 앞에서 사용한 두 가지 스크립트를 이용해서 서로 다른 부하를 일으키도록 한다. 첫번째 스크립트를 사용해서 CPU 기반의 부하를 일으키는 총 10개의 프로세스를 생성한다. 그리고 클라이언트의 역할을 하는 서버에서 다음과 같이 명령을 입력해서 응답 시간을 측정한다.

./siege -c 100 -r 10 -q http://server/hello.jsp

top 명령을 통해 이때의 프로세스 상태를 살펴보면 재미있는 사실을 발견할 수 있다. 이미 돌고 있는 10개의 파이썬 스크립트들이 CPU를 차지하고 있고, 요청을 처리하기 위한 nginx와 java 프로세스들이 중간중간에 끼어들어서 실행되고 있는 것을 볼 수 있다. nginx와 java를 방해하는 프로세스가 없는 상태보다는 응답 속도가 느려질 수밖에 없는 상황이다.

이번엔 I/O 기반의 부하를 일으켜보자. 두 경우 모두 10개의 프로세스를 띄우기 때문에 uptime을 통해서 보는 Load Average 값은 비슷하다. 하지만 siege 툴을 통해 확인한 응답 속도는 차이가 있다.

I/O 부하를 일으킬 경우의 top 결과는 다음과 같다. I/O를 일으키는 파이썬 스크립트들은 D 상태에 빠져있는 것을 볼 수 있다. I/O 대기 상태이기 때문에 당연한 상태다. 하지만 CPU 기반의 부하일때와는 다르게 파이썬 스크립트보다 nginx와 java의 CPU Usage가 더 많다. 이는 CPU에 대한 경합이 전자의 경우보다 덜하기 때문에 빠른 응답 속도를 보여줄 수 있다는 의미이다. 즉, 우리가 돌리고 있는 프로세스가 어떤 시스템 자원을 많이 쓰느냐에 따라서 부하가 시스템에 미치는 영향이 다르다는 뜻이다.

위 결과는 CPU 기반의 부하가 많을 때보다 I/O 기반의 부하가 많을 때 더 빠르다는 이야기를 의미하는 것이 아니다. 예제에서 사용한 테스트 프로그램은 CPU 만을 사용하는 프로그램이었기 때문에 위와 같은 결과가 나왔으며, CPU와 I/O를 함께 사용하는 애플리케이션의 경우에는 CPU 기반의 부하가 많을 때가 I/O 기반의 부하가 많을 때보다 더 빠를 수도 있다. 위 예제에서의 테스트 결과는 서로 다른 형태의 부하는 시스템의 성능에 다른 영향을 끼친다는 것을 의미한다.

3.6 Case Study - OS 버전과 Load Average

이번에는 Load Average와 관련해서 실제 발생한 이슈를 살펴보려 한다. 서비스중인 서버들의 OS를 업그레이드 하는 과정에서 서버마다 서로 상이한 버전의 OS로 운영중이던 시기가 있었다. 그런데 동일한 애플리케이션을 운영하고 있음에도 불구하고, 두 서버간의 Load Average 차이가 상당히 벌어졌다.

앞에서도 살펴본 것처럼 Load Average는 단순히 프로세스의 개수를 바탕으로 한 값이다. 차이가 있다는 것은 프로세스의 개수가 다르다는 의미인데, 동일한 애플리케이션이기 때문에 그럴 가능성은 매우 적다.

그래서 두 가지 방식으로 간단한 테스트를 해보기로 했다. 첫번째 테스트는 앞에서 사용한 CPU Bound의 파이썬 프로세스를 다시 사용해서 진행했다. 두 서버간의 Load Average 차이는 거의 없었다.

두번째 테스트는 조금 다르게 아래의 파이썬 스크립트를 사용해 진행했다. 첫번째와는 다르게 실제 일은 10개의 스레드를 만들어서 진행하고 부모 스레드는 아무 작업을 하지 않도록 한다. 결과는 다음과 같았다.

ubuntu@ip-172-31-0-131:~$ uptime
 09:52:57 up 10 min,  1 user,  load average: 0.70, 0.25, 0.12
ubuntu@ip-172-31-0-131:~$ uptime
 09:52:58 up 10 min,  1 user,  load average: 0.80, 0.28, 0.13
ubuntu@ip-172-31-0-131:~$ uptime
 09:52:59 up 10 min,  1 user,  load average: 0.80, 0.28, 0.13
ubuntu@ip-172-31-0-131:~$ uptime
 09:53:00 up 10 min,  1 user,  load average: 0.80, 0.28, 0.13
ubuntu@ip-172-31-0-131:~$ uptime
 09:53:00 up 10 min,  1 user,  load average: 0.80, 0.28, 0.13
ubuntu@ip-172-31-0-131:~$ uptime
 09:53:01 up 10 min,  1 user,  load average: 0.80, 0.28, 0.13
ubuntu@ip-172-31-0-131:~$ uptime
 09:53:01 up 10 min,  1 user,  load average: 0.80, 0.28, 0.13
ubuntu@ip-172-31-0-131:~$ uptime
 09:53:13 up 10 min,  1 user,  load average: 0.84, 0.31, 0.14
ubuntu@ip-172-31-0-131:~$ uptime
 09:53:13 up 10 min,  1 user,  load average: 0.84, 0.31, 0.14
ubuntu@ip-172-31-0-131:~$ uptime
 09:53:14 up 10 min,  1 user,  load average: 0.84, 0.31, 0.14
ubuntu@ip-172-31-0-131:~$ uptime
 09:53:15 up 10 min,  1 user,  load average: 0.84, 0.31, 0.14
ubuntu@ip-172-31-0-131:~$ uptime
 09:53:16 up 10 min,  1 user,  load average: 0.84, 0.31, 0.1
ubuntu@ip-172-31-0-119:~$ uptime
 09:52:49 up 2 min,  1 user,  load average: 0.71, 0.35, 0.14
ubuntu@ip-172-31-0-119:~$ uptime
 09:52:50 up 2 min,  1 user,  load average: 0.71, 0.35, 0.14
ubuntu@ip-172-31-0-119:~$ uptime
 09:52:51 up 2 min,  1 user,  load average: 0.71, 0.35, 0.14
ubuntu@ip-172-31-0-119:~$ uptime
 09:52:51 up 2 min,  1 user,  load average: 0.73, 0.36, 0.14
ubuntu@ip-172-31-0-119:~$ uptime
 09:52:53 up 2 min,  1 user,  load average: 0.73, 0.36, 0.14
ubuntu@ip-172-31-0-119:~$ uptime
 09:52:53 up 2 min,  1 user,  load average: 0.73, 0.36, 0.14
ubuntu@ip-172-31-0-119:~$ uptime
 09:53:05 up 2 min,  1 user,  load average: 0.85, 0.40, 0.16
ubuntu@ip-172-31-0-119:~$ uptime
 09:53:05 up 2 min,  1 user,  load average: 0.85, 0.40, 0.16
ubuntu@ip-172-31-0-119:~$ uptime
 09:53:06 up 2 min,  1 user,  load average: 0.85, 0.40, 0.16
ubuntu@ip-172-31-0-119:~$ uptime\
> 
 09:53:08 up 2 min,  1 user,  load average: 1.02, 0.44, 0.17
ubuntu@ip-172-31-0-119:~$ uptime
 09:53:09 up 2 min,  1 user,  load average: 1.02, 0.44, 0.17

놀랍게도 Load Average 차이가 실제로 확인되었다. 위 결과에서 볼 수 있는 것처럼 CentOS 7.2에서의 Load Average가 더 정확하게 측정되었으며 CentOS 6.5에서는 Load Average가 전혀 상승하지 않았다.

Load Average가 서로 다른 값을 보이고 있지만, 두 서버가 동일한 작업을 진행중이라는 것을 어떻게 확인할 수 있을까? 이 경우에는 vmstat 명령을 통해 확인할 수 있다. vmstat은 r과 b열을 통해서 현재 시스템에서 동작중인 프로세스의 개수를 출력해주기 때문에, 이 값이 같다면 두 서버에서 돌아가고 있는 프로세스(스레드)의 개수가 다르지 않다는 것을 확인할 수 있다.

ubuntu@ip-172-31-0-119:~$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu
 6  0      0 516888  17272 259076    0    0   997    54  352    4  6  2 89  1  3  0
 6  0      0 516888  17272 259084    0    0     0     0 4742 10941 98  2  0  0  0  0
 5  0      0 516888  17272 259084    0    0     0     0 4726 10933 98  2  0  0  0  0
 5  0      0 516888  17272 259084    0    0     0     0 4868 10967 98  2  0  0  0  0
 5  0      0 516888  17272 259084    0    0     0     0 4776 10940 98  2  0  0  0  0
 5  0      0 516888  17272 259084    0    0     0     0 4807 10893 99  1  0  0  0  0
11  0      0 516888  17272 259084    0    0     0     0 4786 10986 98  2  0  0  0  0
 5  0      0 516888  17272 259084    0    0     0     0 4805 10916 98  2  0  0  0  0
 6  0      0 516888  17272 259084    0    0     0     0 4801 10894 98  2  0  0  0  0
 5  0      0 516888  17272 259084    0    0     0     0 4751 10899 99  1  0  0  0  0
 6  0      0 516888  17272 259084    0    0     0     0 4771 10923 98  2  0  0  0  0
 5  0      0 516888  17272 259084    0    0     0     0 4802 10942 98  2  0  0  0  0
 6  0      0 516888  17272 259084    0    0     0     0 4836 10960 99  1  0  0  0  0
 8  0      0 516888  17272 259084    0    0     0     0 4760 10893 98  2  0  0  0  0
12  0      0 516888  17272 259084    0    0     0     0 4770 10896 99  1  0  0  0  0
 7  0      0 516888  17272 259084    0    0     0     0 4656 10858 98  2  0  0  0  0
12  0      0 516888  17272 259084    0    0     0     0 4763 10913 98  2  0  0  0  0
ubuntu@ip-172-31-0-131:~$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu
 5  0      0 532984  17448 245080    0    0   876    67  571   10 10  2 77  1 10  0
 6  0      0 532984  17448 245088    0    0     0     0 3978 10486 96  3  0  0  1  0
 5  0      0 532984  17448 245088    0    0     0     0 3973 10529 97  3  0  0  0  0
 5  0      0 532984  17448 245088    0    0     0     0 4143 10553 96  3  0  0  1  0
 5  0      0 532984  17448 245088    0    0     0     0 4010 10433 95  4  0  0  1  0
12  0      0 532984  17448 245088    0    0     0     0 3946 10413 96  3  0  0  1  0
14  0      0 532984  17448 245088    0    0     0     0 3972 10468 95  4  0  0  1  0
 6  0      0 532984  17448 245088    0    0     0     0 4064 10527 96  4  0  0  0  0
 5  0      0 532984  17448 245088    0    0     0     0 4093 10437 94  4  0  0  2  0
 5  0      0 532984  17448 245088    0    0     0     0 4038 10473 96  3  0  0  1  0
13  0      0 532984  17448 245088    0    0     0     0 3950 10462 96  4  0  0  0  0
 5  0      0 532984  17448 245088    0    0     0     0 4095 10578 96  3  0  0  1  0
 6  0      0 532984  17448 245088    0    0     0     0 4059 10534 95  4  0  0  1  0
 7  0      0 532984  17448 245088    0    0     0     0 3997 10510 98  2  0  0  0  0
 6  0      0 532984  17448 245088    0    0     0     0 3927 10460 97  2  0  0  1  0
 5  0      0 532984  17448 245088    0    0     0     0 4058 10585 96  4  0  0  0  0

위 결과를 보면 두 서버 모두 r 열의 값이 5로 동일한 것을 볼 수 있다. 즉, 측정되는 running 프로세스의 개수는 Load Average 자체가 잘못 계산된 것으로 볼 수도 있다.

조금 더 상세하게 살펴볼 수 있는 방법은 /proc/sched_debug를 보는 것이다. 이 파일은 proc 파일 시스템에 있는 파일로, 각 CPU의 Run Queue 상태와 스케줄링 정보도 살펴볼 수 있다. Load Average 값은 제대로 찍히지 않았지만 /proc/sched_debug에 보면 실제 파이썬 스크립트들이 큐에 들어가 있다.

이 이슈는 커널의 버그로 인해 발생한 이슈였지만, 실제 운영중에도 비슷한 일들이 일어날 수 있다. 커널은 완벽하지 않기 때문에 버그가 있을 수 있으며, 커널 버전이 달라지면 잘 알고 있는 모니터링용 지표가 제대로 수집되지 않을 가능성도 있다. 그렇기 때문에 하나의 지표로만 모니터링하거나 시스템의 상태를 확인하지 말고 다양한 툴들과 지표를 조합해서 운영해야 한다.