【Nginx】サーバ状況監視ツール

こんにちは。

nginxのサーバー監視ツール。
リソースを継続に調べるツールがないくて、探しましたが、古い情報と、各種のソースファイルの場所に四苦八苦したので、
まとめます。

今回はバージョン固定なので、コピペすればOKかもしれません。


#1.リソースを調べる
##1.1 TOPコマンド
有名ですが、topコマンド習熟必要です。

```topコマンド
$ top
top - 02:22:36 up  3:19,  2 users,  load average: 8.03, 6.75, 6.15
Tasks: 351 total,   4 running, 339 sleeping,   0 stopped,   8 zombie
Cpu(s): 23.9%us,  6.6%sy,  0.6%ni, 53.4%id, 15.4%wa,  0.0%hi,  0.1%si,  0.1%st
Mem:   5967892k total,  5337344k used,   630548k free,    21132k buffers
Swap:  2064380k total,  1304292k used,   760088k free,   528380k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
23978 aaa       20   0  402m  74m  49m R 49.0  1.3   0:03.18 php-cgi
23897 bbb       20   0  288m 104m  10m R 37.8  1.8   0:13.51 php-cgi
23869 cccc      20   0  250m  76m 8584 S 21.5  1.3   0:02.41 php-cgi
23976 dddd      20   0  274m  90m  10m S 20.2  1.6   0:02.01 php-cgi
23917 eeee      20   0  246m  72m 8308 S 18.9  1.2   0:02.44 php-cgi
23911 eeee      20   0  238m  63m 8140 D  5.6  1.1   0:00.59 php-cgi
23913 bbb       20   0  246m  71m 8144 D  5.6  1.2   0:00.66 php-cgi
23914 aaa       20   0  238m  63m 8140 D  5.6  1.1   0:00.60 php-cgi
```

##1.2 vmstatコマンド

```vmstatコマンド
$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 2  0      0 534592  44512 134928    0    0     5     1   32  132  1  0 99  0
 0  0      0 534568  44520 134920    0    0     0    12   32   75  0  0 100  0
 0  0      0 534568  44520 134928    0    0     0     0   27   58  0  1 99  0
```

##1.3 dstatコマンド

```dstatコマンド
$ dstat -Tclmdrn
--epoch--- ----total-cpu-usage---- ---load-avg--- ------memory-usage----- -dsk/total- --io/total- -net/total-
  epoch   |usr sys idl wai hiq siq| 1m   5m  15m | used  buff  cach  free| read  writ| read  writ| recv  send
1393744402|  1   0  99   0   0   0|   0 0.01 0.05| 295M 43.5M  132M  518M|4782B  912B|0.24  0.09 |   0     0
1393744403|  0   0 100   0   0   0|   0 0.01 0.05| 295M 43.5M  132M  518M|   0     0 |   0     0 | 396B 1708B
1393744404|  0   0 100   0   0   0|   0 0.01 0.05| 295M 43.5M  132M  518M|   0     0 |   0     0 | 132B  580B
1393744405|  0   0 100   0   0   0|   0 0.01 0.05| 295M 43.5M  132M  518M|   0     0 |   0     0 | 132B  580B
1393744406|  0   0 100   0   0   0|   0 0.01 0.05| 295M 43.5M  132M  518M|   0     0 |   0     0 | 132B  580B
```

#2. Nginxの接続状況確認
##2.1 stub_statusが利用できるか確認。

"HttpStubStatusModule"モジュールを有効にします。
有効化されているかの確認をします。

```
$ nginx -V
nginx version: nginx/1.2.1
TLS SNI support enabled
configure arguments:・・・ --with-http_stub_status_module ・・・
```

##2.2 Configファイルを作ります。
以下のファイルは、http{ }の中にserver{}が入っていることがポイントです。

```
$ cd /etc/nginx/
$ cat nginx.conf
・・・
http {
    include       mime.types;
    default_type  application/octet-stream;
・・・
    include /etc/nginx/conf.d/*.conf;
}

$ cd conf.d
$ cat nginx_status.conf
server {
    listen 80;
    server_name ap7;

    location /nginx_status {
        stub_status on;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}
```

allow 127.0.0.1 とすることで，サーバ内部からしか見れないようにします

127.0.0.1のローカルサーバーが見えるようにします。

```
$ cat /etc/hosts
127.0.0.1 localserver
```

##2.3 サーバーのステータスを取得する方法

```
$ curl http://localserver/nginx_status
Active connections: 25 
server accepts handled requests
 6261 6261 9336 
Reading: 0 Writing: 6 Waiting: 19 
``` 

見ての通り、縦型に表示です。これはvmstatみたいな１行タイプがいいので、ツールで変更します。

```
$ ./nginxstats.py http://localserver/nginx_status
Conn     Conn/s     Request/s  Read  Write Wait 
-------- ---------- ---------- ----- ----- -----
      33       1.33       2.93     0     6    27
      19       2.00       3.73     0     5    14
```

##2.4 監視ツールの取得
場所に困りましたが、googleにありました。

```
$ wget https://code.google.com/p/timoseven/source/browse/trunk/python/nginxstats.py?r=717
・・・
$ chmod +x nginxstatus.py
$ cat nginxstatus.py
#!/usr/bin/env python

import re
import sys
import time
import urllib

TIME_SLEEP = 30


def get_data(url):
    data = urllib.urlopen(url)
    data = data.read()
    result = {}

    match1 = re.search(r'Active connections:\s+(\d+)', data)
    match2 = re.search(r'\s*(\d+)\s+(\d+)\s+(\d+)', data)
    match3 = re.search(r'Reading:\s*(\d+)\s*Writing:\s*(\d+)\s*'
        'Waiting:\s*(\d+)', data)
    if not match1 or not match2 or not match3:
        raise Exception('Unable to parse %s' % url)

    result['connections'] = int(match1.group(1))

    result['accepted'] = int(match2.group(1))
    result['handled'] = int(match2.group(2))
    result['requests'] = int(match2.group(3))

    result['reading'] = int(match3.group(1))
    result['writing'] = int(match3.group(2))
    result['waiting'] = int(match3.group(3))

    return result


def main():
    url = sys.argv[1]
    prev = None
    next = time.time() + TIME_SLEEP
    total = None
    count = 0
    try:
        while True:
            data = get_data(url)
            if prev:
                result = print_stat(prev, data)
                if total is None:
                    total = list(result)
                else:
                    for i, v in enumerate(result):
                        total[i] += v
                count += 1
            else:
                print_head()
            prev = data
            time.sleep(next - time.time())
            next += TIME_SLEEP
    except KeyboardInterrupt:
        if total:
            print_foot(total, count)


def print_foot(total, count):
    total = [v / count for v in total]
    print '-------- ---------- ---------- ----- ----- -----'
    print '%8d %10.2f %10.2f %5d %5d %5d' % tuple(total)


def print_head():
    print '%-8s %-10s %-10s %-5s %-5s %-5s' % (
        'Conn', 'Conn/s', 'Request/s', 'Read', 'Write', 'Wait')
    print '-------- ---------- ---------- ----- ----- -----'


def print_stat(prev, data):
    result = (
        data['connections'],
        float(data['accepted'] - prev['accepted']) / TIME_SLEEP,
        float(data['requests'] - prev['requests']) / TIME_SLEEP,
        data['reading'],
        data['writing'],
        data['waiting'])

    print '%8d %10.2f %10.2f %5d %5d %5d' % result
    return result
        

if __name__ == '__main__':
    main()
```
コピペしてもOKです。

envのパスを確認します。

```
$ ls /usr/bin/env
/usr/bin/env
```

pythonのパスが張れているか確認します。

``` 
$ which python
/usr/bin/python
```

#3. リクエスト制限！

細かい設定の項目の説明はこちらを参照
http://server-setting.info/centos/nginx-limit_rate.html

limit_req_zone、limit_req を設定します。

ポイントは、毎秒いくつのリクエスト数、待ち行列の場合に、制限をかけるかです。

##3.1 limit_req_zone

```
limit_req_zone $binary_remote_addr  zone=[name]:[size] rate=[rate];
 
# $binary_remote_addr - 公式でこの変数名
# [name] - ゾーン名（公式では"one"という名称を使っている）
# [size] - ゾーンの最大サイズ
# [rate] - 毎秒/分/時 の最大リクエスト数。この数を超えると503を返す
 
# httpディレクティブ
```

##3.2  limit_req

```
limit_req zone=[name] burst=[burst];
 
# [name] - limit_req_zone で設定したnameを設定
# [burst] - limit_req_zoneの[rate]で設定した上限を超えた場合の待ち行列数。ここを設定しないと、[rate]を超えた瞬間すぐに503になってしまう。
# ※ この数値を超えたら503になる
 
# http、server、locationディレクティブ
```

##3.3 実際に設定

大元のトップファイルに追記です。

```
# limit_req_zoneはhttpディレクティブに設定

http {
    limit_req_zone $binary_remote_addr  zone=one:10m rate=50r/s;
    # 毎秒50リクエスト
 
    server {
        listen 80;
        server_name hogehoge.com;
        limit_req zone=one burst=100;
    }
}
```

##4. まとめ

dstatでリソース/IO状況を見て、nginxstats.pyで同時接続数を見ながら、nginxのリソース管理をしてみてください。