Linux系OSでHWリソース使用率を取得してサーバ負荷の原因を調査する

この記事は2015年10月15日に公開したものです。情報が古い可能性がありますのでご注意ください。

はじめに

サーバが高負荷となる原因には様々なものがあると思いますが、高負荷が確認された場合に何がボトルネックとなっているのか、負荷の原因を特定したり、対応策を考えるのはシステムの運用者にとっては重要です。

しかし、サーバが高負荷となっているときには、システムに障害が起こっている場合も多く、そのような状況下で、適切に状況を確認するのは難しいことも多いと思います。
ここではサーバ負荷の特定のためにLinux OSのサーバリソース使用状況を取得するコマンドについて、簡単に記載します。

サーバの負荷とは

サーバの負荷とは、大きく以下の2つに分類されます。

  • CPU負荷
  • I/O負荷

負荷の状況を確認するポイントとしては、大きく以下の4点があります。

  • ロードアベレージ
  • CPU使用率
  • I/O負荷
  • メモリ

こういったサーバの負荷状態を確認するコマンドは様々にありますが、ここではOSに標準で搭載されているtopコマンドやfreeコマンド、システム内の情報を表示vmstatコマンドを利用して確認する方法を記載します。
※これらのコマンドはRHEL6系やUbuntuなら procps (RHEL7系だと procps-ng?)というパッケージに含まれており、 procps パッケージは標準でインストールされてるはずです。

実行環境

本記事記載に伴い、各コマンドを実行した環境は以下の通りです。

OS/カーネル 3.13.0-76-generic #120-Ubuntu SMP

サーバ負荷の確認

ロードアベレージの確認

サーバが重い状態、つまり高負荷な状態の場合、まずロードアベレージを確認します。ロードアベレージの確認には uptimetopwコマンドを利用します。

  • uptimeコマンド実行例
    $ uptime
     11:38:58 up 288 days, 18:58,  3 users,  load average: 4.86, 5.90, 7.29
    
  • w コマンド実行例
    $ w
     11:38:39 up 288 days, 18:57,  3 users,  load average: 5.00, 6.00, 7.35
    …
    
  • topコマンド実行例
    $ top
    top - 11:39:16 up 288 days, 18:58,  3 users,  load average: 4.45, 5.74, 7.21
    …
    

    いずれも見方は同じで、出力結果のload average:行に続く3つの数値がロードアベレージを示し、左から1分間、5分間、15分間の平均値を示します。

ロードアベレージとして表示されている数値は、”処理を実行したいが、何らかの原因で実行できずに待ち状態となっているプロセス数(厳密には実行中のプロセス数+実行可能状態のプロセス数)の1分、5分、15分の平均値”になります。

プロセスの状態には以下があります。

状態 説明
TASK_RUNNING 実行可能状態
TASK_INTERRUPTIBLE 待ち状態。シグナル受信可能
TASK_UNINTERRUPTIBLE 待ち状態。シグナル受信不可
TASK_ZOMBIE ゾンビ状態。exit後の状態
TASK_STOPPED サスペンド状態

このうち「実行待ちプロセス」とは以下の状態のプロセスを指します。

状態 説明
TASK_RUNNING 実行可能状態
TASK_UNINTERRUPTIBLE 割り込み不可能な待ち状態。シグナル受信不可

なお、プロセスの状態はtopコマンドのS列のほかに、psコマンド(BSD系)のSTAT列で確認できます。

  • psコマンド実行例
    $ ps auxf
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         2  0.0  0.0      0     0 ?        S     4月21   0:11 [kthreadd]
    root         3  0.0  0.0      0     0 ?        S     4月21   0:34  \_ [ksoftirqd/0]
    root         5  0.0  0.0      0     0 ?        S<    4月21   0:00  \_ [kworker/0:0H]
    root         6  0.0  0.0      0     0 ?        S     4月21   0:00  \_ [kworker/u4:0]
    root         7  0.0  0.0      0     0 ?        S     4月21   0:05  \_ [migration/0]
    :
    

    STAT列に表示されている内容は以下を示している。

    D    割り込み不可能なスリープ状態 (通常 IO 中)
    R    実行中または実行可能状態 (実行キューにある)
    S    割り込み可能なスリープ状態 (イベントの完了を待っている)
    T    ジョブ制御シグナルまたはトレースされているために停止中の状態
    W    ページング状態 (2.6.xx カーネルからは無効)
    X    死んだ状態 (見えるべきではない)
    Z    終了したが、親プロセスによって回収されなかった、消滅した (ゾンビ) プロセス
    (man psより)
    
    BSD 形式で stat キーワードが用いられたときは、以下の添付文字が表示されることがある。
    <    優先度の高いプロセス (他のユーザーに対して良くない)
    N    優先度の低いプロセス (他のユーザーに対して良い)
    L    実メモリのページをロックして利用している (リアルタイム処理やカスタム IO 向け)
    s    セッションリーダ
    l    マルチスレッド化されている (NPTL pthreads が行うように、CLONE_THREAD が使われている)
    +    フォアグラウンドのプロセスグループに含まれている
    (man psより)
    

    psコマンドやtopコマンドで表示されるプロセスの状態のうち、D(割り込み不可能なスリープ状態 (通常 IO 中))がTASKUNINTERRUPTIBLER(実行中または実行可能状態 (実行キューにある))がTASK_RUNNINGとなり、これが「実行待ちプロセス」となります。

この「実行待ちプロセス」の平均数が多いと、処理が遅延しているということになるため、ロードアベレージの値はサーバ負荷の判断基準となる数値になります。

「実行待ちプロセス」の数については vmstat コマンドで確認できます。

  • vmstat実行例
    $ vmstat 1 10
    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 171484 3943636 799392 12848000    0    0   548   397    0    0 29  2 64  5  0
     0  0 171484 3949092 799392 12848000    0    0   408   194 7060 15726  9  2 87  2  0
     2  0 171484 3923788 799392 12848028    0    0    64    56 8212 17021 22  2 75  0  0
     2  0 171484 3949324 799392 12848044    0    0   280  1242 10940 19173 12  3 83  2  0
     5  1 171484 3916356 799392 12848580    0    0   376  2384 10407 19323 38  3 54 
    ・・・
    

    実行結果のrの項目がrun queueで、実行可能で実行中もしくはCPU割り当て待ち(実行待ち)のプロセス(TASK_RUNNING)の数です。
    bの項目がwait queueの値を表しており、処理を終えてI/O待ちとなっているプロセス(TASK_UNINTERRUPTIBLE)となります。

       Procs
          r: ランタイム待ちのプロセス数
          b: 割り込み不可能なスリープ状態にあるプロセス数
    (man vmstatより)
    

ロードアベレージの値がCPU数(コア数)より大きくなっている場合は”処理が追いついていない”、つまりサーバ負荷が高いということになります。
例えば、2コアのCPUでロードアベレージが4となっている場合、2コアのCPUがそれぞれ1プロセスずつ処理を実行しており、残る2つのプロセスは実行待ち状態ということになります。
ただし、マルチタスクの場合、プログラムの作りにもよるため一概には言えません。(例えば4コアのマシン上でも、4つのタスク(A,B,C,D)に対して2CPUしか使わないようなプログラムを実行した場合、4CPUが全て空きの状態だったとしても、2CPU分のみが2つのタスク(A,B)で使用され、残る2つのタスク(C,D)は待ちの状態となるため、ロードアベレージは2となります。)

逆に、仮に1コアのCPUの使用率が常に100%の状態が1時間続いたとしても、その間のロードアベレージが常に1であったとすれば、サーバ負荷は0という事になります。これは1個のプロセスが常にCPUを占有している状態であり、そのプロセスがCPUをフルで使いつづけたという理想的な状態になるためです。

実行待ちとなっている原因は以下の2つになります。

  • 他のプロセスの処理にCPUが使われているため、別のプロセスがその処理が終了するのを待っている状態(CPU負荷)
  • ディスクへの読み書き要求を発行して、その結果を待っている状態(I/O負荷)

ロードアベレージの値だけでは、CPU負荷なのか、I/O負荷なのか判別できませんので、ロードアベレージが高い場合は、次に、CPU負荷なのか、I/O負荷なのかの切り分けを行う必要があります。
なお、ロードアベレージが低い状態のにスループット(処理速度)が上がらない場合、ネットワーク面での問題(パケットロスなど)や実行中のアプリケーションの問題が疑われます。

ロードアベレージの値を確認後、CPUやI/O負荷の状況を確認していくことになります。

CPU負荷の確認

実行中のプロセスとそれに伴うCPUの使用率を確認する場合には、topコマンドを利用します。
CPU使用率の詳細や時間推移を確認する場合には、vmstatコマンドコマンドを利用します。

  • top実行例
    % top
    top - 21:36:23 up 1250 days,  7:41,  3 users,  load average: 2.12, 2.07, 1.90
    Tasks: 397 total,   1 running, 396 sleeping,   0 stopped,   0 zombie
    Cpu0  :  0.3%us,  0.3%sy,  7.2%ni, 92.0%id,  0.1%wa,  0.0%hi,  0.1%si,  0.0%st
    Cpu1  :  0.5%us,  1.0%sy, 15.2%ni, 82.3%id,  0.5%wa,  0.0%hi,  0.5%si,  0.0%st
    Cpu2  :  0.5%us,  1.0%sy, 15.2%ni, 82.3%id,  0.6%wa,  0.0%hi,  0.5%si,  0.0%st
    Cpu3  :  0.5%us,  1.0%sy, 16.1%ni, 81.3%id,  0.5%wa,  0.0%hi,  0.5%si,  0.0%st
    Mem:     11882M total,    11810M used,       71M free,       32M buffers
    Swap:    12287M total,     1255M used,    11032M free,     2647M cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    26901 apache    18   2 9013m 6.6g  24m S   78 57.3  91:58.99 httpd
     1538 root      16   0 40912 6252 1792 S    3  0.1   2078:02 snmpd
    27128 apache    17   2 70436  13m 1784 S    1  0.1   0:00.97 httpd
      145 root      15   0     0    0    0 S    0  0.0 479:37.63 kswapd0
    10572 dshimizu  16   0  5788 1444  888 R    0  0.0   0:00.02 top
    27125 apache    18   2 70492  13m 1784 S    0  0.1   0:00.89 httpd
    27139 apache    17   2 71460  14m 1784 S    0  0.1   0:00.95 httpd
    27144 apache    18   2 74084  14m 2272 S    0  0.1   0:00.92 httpd
    29529 apache    17   2 69520  12m 1784 S    0  0.1   0:00.71 httpd
       1 root      16   0   804   76   40 S    0  0.0   0:48.32 init
       2 root      RT   0     0    0    0 S    0  0.0   0:00.29 migration/0
       3 root      34  19     0    0    0 S    0  0.0   0:09.12 ksoftirqd/0
       4 root      RT   0     0    0    0 S    0  0.0   0:00.67 migration/1
       5 root      34  19     0    0    0 S    0  0.0   0:16.69 ksoftirqd/1
    

    確認するのはCpu(s)行です。各項目は以下を意味します。
    キーボードの「1」を押下すると上記のようにコア数ごとの使用率(ここではCpu0,Cpu1,Cpu2,Cpu3)が表示されます。

    us ユーザプロセスの使用時間
    sy システムプロセスの使用時間
    ni 実行優先度を変更したユーザプロセスの使用時間
    id アイドル状態の時間
    wa I/Oの終了待ちをしている時間
    hi ハードウェア割込み要求での使用時間
    si ソフトウェア割込み要求での使用時間
  • vmstat実行例
    $ vmstat
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     1  0  25016 200692  29800 2139096    0    0    18    37    0    1  8  2 90  0  0
    

    CPU使用率を確認する際に見る項目は以下です。

       CPU
          これらは CPU の総時間に対するパーセンテージである。
          us: カーネルコード以外の実行に使用した時間 (ユーザー時間、nice 時間を含む)。
          sy: カーネルコードの実行に使用した時間 (システム時間)。
          id: アイドル時間。Linux 2.5.41 以前では、IO 待ち時間を含んでいる。
          wa: IO 待ち時間。Linux 2.5.41 以前では、0 と表示される。
          st: 仮想マシンから盗まれた時間。Linux 2.6.11より前では未知。
    (man vmstatより)
    

CPU負荷で着目するのは%user(各コマンドのus値),%system(各コマンドのsy値)の値です。

CPUの%user値が高い場合

ユーザプロセス(アプリケーションのプロセス等)がCPUを使用している状態になり、基本的にI/O等にボトルネックはありません。主な原因としては以下のようなものがあります。

  • 単にプロセスがCPUパワーの必要な処理をしている
  • プロセスが暴走して大量にCPUを消費しています。

CPUの%systemが高い場合

OS/カーネル空間の処理にCPUを消費している状態になります。原因としては主に以下のようなものが考えられます。
straceコマンドなどで処理の詳細を追跡できます。

  • I/O負荷でCPUを消費している
  • 負荷の高いシステムコールを多数発行している(大きいプロセスを高頻度でfork(プロセスのコピーを生成)しているなど)

CPU負荷が高い場合、topコマンド等で確認できるCPU使用率の高い該当プロセスを暫定的に停止させる、恒久的にはその処理を見直して、より効率的なロジックに変更するといった対応が必要になります。CPUを性能の良いものにするなども検討します。プロセスが暴走している場合は問題を見つけて改善する必要があります。sysが高い場合はカーネルのパラメータチューニングも効果があるかもしれません。

I/O負荷で着目すべきは%iowaitです。

%iowaitが高い場合

一般的にCPU使用率とは%user%systemの値を足した値を差し、%iowaitが高い状態であっても、CPUを必要とするプロセスが他にある場合には、そのプロセスがCPUを使用することが可能です。
I/O負荷は、CPUがアイドル状態(待機状態)で、OS上に未処理のディスク入出力(読み書き)要求があった時間の割合を意味します。

linux-3.13.0/kernel/sched/cputime.cから、iowait/idle共にCPUのidle時間となることが分かります。
rq->nr_iowaitはランキュー(実行可能なプロセスを管理するもの)の中のI/O待ちをしているタスクの数を取得しています。これが1以上の場合はiowaitとして、0の場合はidleとして記録されます。

/*
 * Account for idle time.
 * @cputime: the cpu time spent in idle wait
 */
void account_idle_time(cputime_t cputime)
{
        u64 *cpustat = kcpustat_this_cpu->cpustat;
        struct rq *rq = this_rq();

        if (atomic_read(&rq->nr_iowait) > 0)
                cpustat[CPUTIME_IOWAIT] += (__force u64) cputime;
        else
                cpustat[CPUTIME_IDLE] += (__force u64) cputime;
}

システムの応答が遅いなどの場合に、CPU使用率の%iowait%user%systemに比べて高い状態である場合、CPUを使用する処理が大きくないのにディスクへの読み書き要求が発生していることになり、ディスクへのI/Oがボトルネックになっている可能性があります。
これは、求められるI/O要求に対して、ディスクの処理速度が追いついていない状態になり、主には以下のような状態が考えられます。

  • アプリケーション自体がディスクにアクセスする速度が遅い
  • メモリが不足していてスワップ領域(つまりディスク)へのアクセスが頻発していて遅い

また、どれだけ読み書き(ディスクIO)が発生しているかは、vmstatコマンドの-dオプションで確認できます。

  • vmstat実行例
    disk- ------------reads------------ ------------writes----------- -----IO------
          total merged sectors      ms  total merged sectors      ms    cur    sec
    vda    42773      0 14200115   56799 706517  43951 37449070 1157437      0    210
    sr0        0      0       0       0      0      0       0       0      0      0
    dm-0   40397      0 14185599   55946 602111      0 37409223 1230875      0    211
    dm-1     311      0    2488     157      0      0       0       0      0      0
    

    各出力項目は以下を表します。

       Reads
          total: 成功した読み込みの総数
          merged: グループ化された (結果として 1 つの I/O となった) 読み込みの数
          sectors: 読み込みに成功したセクタ数
          ms: 読み込みに使用した時間 (ミリ秒)
    (man vmstatより)
    
       Writes
          total: 成功した書き出しの総数
          merged: グループ化された (結果として 1 つの I/O となった) 書き出しの数
          sectors: 書き出しに成功したセクタ数
          ms: 書き出しに使用した時間 (ミリ秒)
    (man vmstatより)
    
       IO
          cur: 実行中の I/O
          s: I/O に使用した時間 (秒)
    (man vmstatより)
    
  • vmstat -p実行例
    $ vmstat -p /dev/vda1
    vda1          reads   read sectors  writes    requested writes
                   1806       9868       1120      39847
    

    各出力項目は以下を表します。

    ディスクパーティションモードにおけるフィールドの説明
          reads: このパーティションに発行された読み込みの総数
          read sectors: このパーティションから読み込まれた総セクタ数
          writes : このパーティションに発行された書き出しの総数
          requested writes: このパーティションへの書き出し要求の総数
    (man vmstatより)
    

CPUの%iowaitが高い場合、まずメモリ、スワップの状態を確認します。メモリに問題がない場合、該当のプログラムを修正してディスクへのアクセスを減少させることも必要ですが、ディスクの増設・RAID構成の見直しを行い、ディスクへのI/Oを分散させて処理の向上を図ったり、最近ではSSDなど高速なディスクを利用するといったHWレベルでの改善も有効になります。

メモリ使用率の確認

プロセスのメモリ使用量を見るには上述のfreeのほかにvmstatコマンドで-rオプションを利用します。各プロセスのメモリ使用状況を確認するにはtopコマンドを利用します。

各コマンドの実行例が以下になります。

  • free実行例
    % free
              total       used       free     shared    buffers     cached
    Mem:        514912     504396      10516          0     249672     167500
    /+ buffers/cache:      87224     427688
    Swap:      1048568        120    1048448
    

    各項目はそれぞれ以下を表します。

    行名 項目名 意味
    Mem total 総物理メモリ量
    used 総物理メモリ使用量からfreeを引いたもの
    free 何の用途にも使っていない物理メモリ量
    shared 常に0。現在は利用されていない
    buffers ファイルなどのメタデータをキャッシュしている物理メモリ量
    cached ファイルのデータ本体をキャッシュしている物理メモリ量
    -/+ buffers/cache used- buffers、cachedを含めないused量
    free+ buffers、cachedを含めたfree量
    Swap total Swap領域の総量
    used totalからfreeを引いた量
    free 使用していないSwap領域の量
  • top実行例
    $ top
    top - 21:19:56 up 19 days, 22:55,  4 users,  load average: 5.39, 4.41, 2.53
    Tasks: 436 total,  16 running, 414 sleeping,   0 stopped,   6 zombie
    Cpu(s): 51.2%us, 31.1%sy,  0.0%ni,  1.0%id,  0.7%wa,  1.2%hi, 14.9%si,  0.0%st
    Mem:    514912k total,   495328k used,    19584k free,    28152k buffers
    Swap:  1048568k total,    19412k used,  1029156k free,   167012k cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    18284 test      17   0 21144  10m 1784 R 47.0  2.2   0:11.22 ab
    17181 test      16   0 11244 2996 1492 R 12.9  0.6   2:51.51 pgbench
    17187 postgres  15   0 51940  24m  23m S  1.7  4.9   0:08.48 postmaster
    17410 apache    15   0     0    0    0 Z  1.7  0.0   0:00.10 httpd 
    17602 apache    15   0 23776 4088 1056 S  1.7  0.8   0:00.09 httpd
    17195 postgres  15   0 51940  24m  23m S  1.3  4.9   0:08.57 postmaster
    17198 postgres  15   0 51944  24m  23m S  1.3  4.9   0:08.38 postmaster
    17205 postgres  15   0 23776 4088 1056 S  1.3  0.8   0:00.06 httpd
    17496 apache    15   0 23776 4088 1056 S  1.3  0.8   0:00.05 httpd
    17515 apache    15   0 23776 4088 1056 S  1.3  0.8   0:00.08 httpd
    17586 apache    15   0 23776 4088 1056 S  1.3  0.8   0:00.08 httpd
    17592 apache    15   0 23776 4088 1056 S  1.3  0.8   0:00.05 httpd
    17690 apache    15   0 23776 4088 1056 S  1.3  0.8   0:00.04 httpd
    18298 apache    15   0 23776 4088 1056 S  1.3  0.8   0:00.06 httpd
    18333 apache    15   0     0    0    0 Z  1.3  0.0   0:00.08 httpd 
    18355 apache    15   0 23776 4088 1056 S  1.3  0.8   0:00.05 httpd
    17185 postgres  15   0 51940  24m  23m S  1.0  4.9   0:08.60 postmaster
    

    %memとなっているところがプロセスの物理メモリ使用率です。
    オプションによってメモリ使用量やCPU使用率等でソートすることも可能ですので、場合によって使い分けると良いと思います。

  • vmstat実行例
    $ vmstat 1 10
    procs -----------memory---------- ---swap-- -----io---- -system-- -----cpu------
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     3  0   7320 1147828 516352 7155588    0    0    70    66    0    0  1  1 96  2  0
     2  0   7320 1147828 516352 7155588    0    0     0     0  271  249  0  0 100  0  0
     0  0   7320 1147828 516352 7155588    0    0     0   468  401  546  1  1 97  1  0
     2  0   7320 1147680 516352 7155588    0    0     0   372 1799 2758  6  5 90  0  0
    

    確認するのはmemory項目、swap項目です。それぞれの意味は以下となります。

       Memory
          swpd: 仮想メモリの量。
          free: 空きメモリの量。
          buff: バッファに用いられているメモリの量。
          cache: キャッシュに用いられているメモリの量。
          inact: アクティブでないメモリの量 (-a オプション)。
          active: アクティブなメモリの量 (-a オプション)。
    (man vmstatより)
    
       Swap
          si: ディスクからスワップインされているメモリの量 (/s)。
          so: ディスクにスワップしているメモリの量 (/s)。
    (man vmstatより)
    

プロセスのメモリ使用量が増えてくると、キャッシュにメモリが使えなくなるため、ディスクI/Oが増加します。ディスクへの読み書きはメモリへの読み書きよりはるかに遅いため、ディスクI/Oが増えると、I/Owaitとなるプロセスが増え、処理のボトルネックとなります。
Linuxの場合、メモリをできるだけ有効に活用しようとするため、空きメモリをキャッシュとして使用します。freeで確認できるメモリの使用量(Mem:行のused)の値は、キャッシュされているものも含めた値(used+buffers+cached)となるため大きいものになります。実際にプロセスがどの程度メモリを使用しているかは、キャッシュとバッファに利用されているメモリ容量を差し引いた値(-/+ buffers/cache:行のused)で判断する必要があります。

  • メモリ使用率が多く、スワップが多発している原因は以下になります。
  • メモリを大量に使用する処理があるが、搭載しているメモリが不足している

利用可能なメモリ量が不足してくると、メモリへ保存されている古いプロセスやデータはディスク上のスワップと呼ばれる領域に保存されます。つまり、スワップが多発するということは、その分ディスクへのアクセスが発生することになるため、サーバのスループットが低下します。
これを改善するには、暫定的には対象プロセスをkillする、恒久的にはプログラムのメモリの使用率を改善する、メモリ増設する、といった対策を講じることになります。

おわりに

以上がサーバの負荷状況、及びその原因を追及するための大まかな手順になります。実際のシステムは複数のサーバやネットワーク機器などが存在することが多いので、上記のような手順そのままに調査を進めることは難しいかもしれませんが、システム管理者の皆様の何らかの助けになればと思います。

参考

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です