新人エンジニアが知っておきたいサーバ負荷の考え方

10年ほど前、エンジニア駆け出しの頃のブログを再認識兼ねてリバイバルします。
最近はサーバにsshで繋いで確認すると言う機会も減りましたが、覚えておいて損はないです。
https://itinao.hatenadiary.org/

### ある日の出来事

エンジニア成り立ての僕。
出勤して早々、キラキラな営業さんや企画の方からこんなこと言われます。

営業「ちょっとなんかサーバが重いんだけど」
企画「なんとかして！仕事にならないよ」

僕「やべ、調査の仕方わからへん。」
僕「すみません。先輩にすぐ連絡します。」

プログラマーの僕はインフラ側の知識、知見が足りてません。
なんとかしたいけど、どうすることもできず。。

自社サービス運用していると良くある光景ですね。

### やっぱりなんとかしたい！

報連相は大切だけどやっぱりエンジニア。
自分でなんとか出来るようになりたいと思いますよね。
そんな方へ。まずはボトルネックの考え方を学びましょう。

### ボトルネックの考え方は大きく2つ

```
1. CPU負荷
2. I/O負荷
```

# 1. CPU負荷ってなんぞ？
プロセスがCPUを占有している(CPUが計算している)状態
![スクリーンショット 2020-03-18 0.58.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272635/df4f4f37-b1cd-1b4b-b6fd-18948de81f75.png)

あるプロセス(プログラム)がCPUを使い使用率100%の状態が長く続いてしまった場合、他のプロセスの実行を妨害してしまいます。

一つ語弊があると困りますが、CPU使用率100％自体は悪ではなく、ディスクやメモリ容量などの他にボトルネックがなければ理想的な状態です。

### CPU100%が急に続くようであれば
```
プログラムが暴走（無限ループ等）していないか確認する。
直近のリリースバージョン内の処理を見直す。
```

## 2. じゃCPU負荷とI/O負荷の違いは？

I/Oとは入出力(Input/Output)の意味です。
頻繁にデータを出し入れすることにより、ハードウェアやネットワークに負荷がかかることで、CPU負荷とI/O負荷は別物です。CPU負荷が高いからと言って必ずしもI/Oが遅くなるわけではなく、あくまでもディスクへ大量の読み書きが発生している状態です。


### I/O負荷が続くようであれば
```
ファイルに入出力ようなプログラムが多くないか？
メモリ不足でスワップが発生してディスクアクセスが発生していないか？
```

メモリが足らないとシステムはスワップを使います。
逆に言うとスワップに対してのアクセスが多い場合はメモリ不足の可能性があります。


## ボトルネック調査手順

ここまでCPU負荷とI/O負荷の概念を見てきました。
次に早速、CPU負荷とI/O負荷についてのボトルネックの調査方法に入っていきます。

```
1. まずは心を落ち着かせます。これ大事。
2. topでロードアベレージを確認します。
3. sarでCPUとI/O負荷どちらが高いか確認します。
4. psで各プロセスの情報を見ます。
5. 実行プログラムの見直しやバージョンを戻すなどの対応をします
6. 途中でとめて問題ない場合は悪さをしているプロセスをkillしたり再起動します。
```
### 1. まずは心を落ちつかせる

これはどんな時も大切です。
周りに早くなんとかしろと言う視線が痛いですが、パニックになってはいけません。

太々しいほど落ちつき対応をしましょう。


### 2. すぐにTOPコマンド

とにかくまずはTOPコマンドでロードアベレージを見ましょう。

#### ロードアベレージとは
1CPUにおける単位時間あたりの実行待ちとディスクI/O待ちのプロセスの数。
単位時間当たりにどの程度のタスクが待ち状態にあったかを報告する数値です。
これが高いとシステムへの負荷が高いということです。

#### ロードアベレージが高いとは

コア数よりロードアベレージが高ければ負荷の原因になる可能性があります。

```
$top
top - 00:41:49 up 6 days,  2:24,  1 user,  load average: 2.15, 3.02, 3.20
Tasks:  93 total,   1 running,  45 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.2 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3977928 total,  3324844 free,   121568 used,   531516 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  3630656 avail Mem 
```

load average: 2.15, 3.02, 3.20という表示がロードアベレージです。
左から直近1分, 5分, 15分間の値です。

見るべきものは以下２つ

```
load averageの状態がコア数を超えてないか。
Swapは発生していないか。
```

次にコアごとの負荷状況を見て行きましょう。

###  2. sarコマンドでCPU使用率とI/O待ち率を見る

マルチコアの場合ロードアベレージだけでは判断がつかないことがあります。
そんなときは合わせてsar -P ALLで各CPUの状態を個別に把握しましょう。
マルチCPUが搭載されていてもディスクは１つしかない場合、CPU負荷は他のCPUに分散できてもI/Oは分散できないため負荷原因となります。

```
$ sar -P ALL
Linux 3.10.0-862.2.3.el7.x86_64 (118-27-1-88)   10/01/2018  _x86_64_    (2 CPU)

01:17:35 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle
01:17:36 AM     all      0.00      0.00      0.00      0.00      0.00    100.00
01:17:36 AM       0      0.00      0.00      0.00      0.00      0.00    100.00
01:17:36 AM       1      0.00      0.00      0.00      0.00      0.00    100.00
```

それぞれの意味はこちらになります。

|表示 |説明  |
|---|---|
| %user | CPUがユーザモードにあった時間の割合 |
| %system | CPUがカーネルモードにあった時間の割合 |
| %iowait | CPUがIO待ちをしていた時間の割合 |　　　　　　　　
| %idle | CPUがアイドル状態にあった時間の割合 |

見るべきものは以下

```
%idleが小さいとCPUの使用率が高いのでCPUがボトルネックになっている可能性があります。
```

### CPUに割り当てられるプロセスの状態遷移をpsコマンドで確認

CPUが負荷の原因とわかった場合は
次にどのプロセスが悪さをしているのかを把握しましょう。

```
$ ps auwx | head
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  19232  1516 ?        Ss   Feb09   0:00 /sbin/init
root         2  0.0  0.0      0     0 ?        S    Feb09   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S    Feb09   0:00 [migration/0]
root         4  0.0  0.0      0     0 ?        S    Feb09   0:00 [ksoftirqd/0]
root         5  0.0  0.0      0     0 ?        S    Feb09   0:00 [stopper/0]
root         6  0.0  0.0      0     0 ?        S    Feb09   0:06 [watchdog/0]
root         7  0.0  0.0      0     0 ?        S    Feb09   0:00 [migration/1]
root         8  0.0  0.0      0     0 ?        S    Feb09   0:00 [stopper/1]
root         9  0.0  0.0      0     0 ?        S    Feb09   0:00 [ksoftirqd/1]
```
それぞれの意味は下記を参照ください。

|表示 |説明  |
|---|---|
| %CPU | プロセスのCPU使用率 |
| %MEM | プロセスの物理メモリ |
| VSZ(RSS) | プロセスが確保している仮想(物理)メモリ領域 |　　　　　　　　
| STAT | プロセスの状態 |
| TIME | プロセスがCPUを占有した時間 |

##### STAT(プロセスステータス)について 

CPU上で実行可能なプロセスはTASK_RUNNING状態になっています。
複数あるTASK_RUNNING状態のプロセスのうち最も高いプライオリティを持つタスクにCPUが与えられます。

|表記| 状態 | 説明 |
|:--|:--|:--|
| R |TASK_RUNNING | 実行可能状態 |
| S|TASK_INTERRUPTIBLE | 待ち状態。シグナル受信可能 |
| D|TASK_UNINTERRUPTIBLE | 待ち状態。シグナル受信不可 |
| Z|TASK_ZOMBIE | ゾンビ状態。exit後の状態 |
| T|TASK_STOPPED | サスペンド状態 |

見るべきものは以下２つ

```
RSSのサイズを見て極端に大きなプロセスがないか確認する。
TIMEの状態をみます。無限ループ(TASK_RUNNING)の場合、TIMEが増加し続けます。
```


### スワップが発生している場合
TOPコマンドでスワップが発生している場合には物理メモリ不足が原因となる可能性があります。
sarコマンドでさらに細かくみていきます。

```
$ sar -S

00時00分01秒 kbswpfree kbswpused  %swpused  kbswpcad   %swpcad
00時10分01秒   2097148         0      0.00         0      0.00
00時20分01秒   2097148         0      0.00         0      0.00
00時30分01秒   2097148         0      0.00         0      0.00
00時40分01秒   2097148         0      0.00         0      0.00
```

| 状態 | 説明 |
|:--|:--|
|kbswpfree|スワップ領域の空き容量|
|kbswpused|スワップ領域の使用容量|
|%swpused|スワップ領域の使用量割合|
|kbswpcad|スワップ領域のキャッシュ容量|

こちらでどの程度Swapが発生しているのか確認した上、
vmstatでvmstat 1 100 のようにインターバルを指定して見るとわかりやすい

```
$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 3  1 244208  10312   1552  62636    4   23    98   249   44  304 28  3 68  1  0
 0  2 244920   6852   1844  67284    0  544  5248   544  236 1655  4  6  0 90  0
 1  2 256556   7468   1892  69356    0 3404  6048  3448  290 2604  5 12  0 83  0
 0  2 263832   8416   1952  71028    0 3788  2792  3788  140 2926 12 14  0 74  0
 0  3 274492   7704   1964  73064    0 4444  2812  5840  295 4201  8 22  0 69  0
```

| 表示 | 説明 |
|:--|:--|
| r | 実行待ちプロセス数 |
| b | スリープ（割り込み可能）のプロセス数、実行できてないプロセス数 |
| swpd | スワップサイズ（KB） |
| free | 空きメモリ（KB） |
| buff | バッファメモリサイズ（KB） |
| cache | キャッシュメモリサイズ（KB） |
| si | ディスクからスワップインされているメモリサイズ（KB/秒） |
| so | ディスクへスワップアウトされているメモリサイズ（KB/秒） |
| bi | ブロックデバイスから受け取ったブロック数（ブロック/秒） |
| bo | ブロックデバイスに送られたブロック数（ブロック/秒） |
| in | 割り込み回数/秒 |
| cs | コンテキストスイッチ回数/秒 |
| us | ユーザプロセスのCPU使用時間割合 |
| sy | カーネルコードの実行に使用した時間  |
| id | CPUがアイドル状態の時間割合 |
| wa | CPUがI/O待ち |
| st | ゲストOSがCPUを割り当てられなかった時間の割合 |

```
r、bは通常は0~2程度。
この数値が大きい場合サーバ重いと感じる可能性があります。

si、soは常時ゼロが基本。
ここに常時数値が現れることはメモリ不足か、メモリを食ってしまうプログラムがあるのどちらかです。
```

## 負荷対策まとめ

まずはCPUかI/Oかを以下のコマンドで判断する

```
top
sar
ps
vmstat
```

### 対処法としては

CPU負荷が高い場合

```
サーバ増設やプログラムのロジック、アルゴリズムの改善
```

I/O負荷が高い場合

```
メモリ増設でキャッシュ領域を拡大する
メモリ増設不可なら、データの分散やキャッシュサーバの導入を検討
プログラムの改善でI/O頻度を軽減する
```

ふう、まとめるの疲れました。
これで負荷の原因を説明できるようになったら良いな。