Munin FastCGIの問題点

MuninをFastCGIで表示するときにおこるDynazoomページのURLがおかしくなるバグの対処法で「Muninを高速に表示するためには、グラフとHTMLの生成をFastCGI化するのがいい」と書いた。しかしFastCGI化しても通常であれば1プロセスしか起動されないため、複数人がページを開いたり、一人で複数ページ開いているとCPUの1コアが100%に張り付き、グラフが描画できないことがある。また負荷テスト時や性能問題発生時に調査などでページを開いている人が大量にいる場合、FastCGIが生成すべきMuninグラフも大量になってしまい、いくらCPUがあっても足りなくなってしまう。

これらの問題を解決するために、以下の対策を実施する。

  1. CPUを複数コア使えるようにFastCGIを複数プロセス起動し、Nginxにロードバランシングさせる
  2. NginxのFastCGIキャッシュを導入し、同一ページへのリクエストであればFastCGIで処理せずともレスポンスを返せるようにする

なお、1ページ内に大量に含まれるグラフと違い、HTML生成はあまり負荷がかからないため、本対策の実施対象外とする。

CPUを複数コア使えるようにFastCGIを複数プロセス起動し、Nginxにロードバランシングさせる

FastCGIを複数プロセス起動する

MuninをFastCGIで表示するときにおこるDynazoomページのURLがおかしくなるバグの対処法でも記載した通り、yum install munin munin-nginx spawn-fcgi --enablerepo epelすると、systemd用のFastCGI起動設定ファイル(munin-cgi-graph.socket, munin-cgi-graph.service)がinstallされる。

$ systemctl cat munin-cgi-graph.socket
# /usr/lib/systemd/system/munin-cgi-graph.socket
[Unit]
Description=Munin FastCGI Graphs Socket

[Socket]
ListenStream=/run/munin/munin-cgi-graph.sock
SocketMode=0660
SocketUser=nginx
SocketGroup=munin

[Install]
WantedBy=sockets.target

$ systemctl cat munin-cgi-graph.service
# /usr/lib/systemd/system/munin-cgi-graph.service
[Unit]
Description=Munin FastCGI Graphs Service
After=network.target
Requires=munin-cgi-graph.socket

[Service]
User=munin
ExecStart=/var/www/html/munin/cgi/munin-cgi-graph
StandardInput=socket

spawn-fcgiコマンドを直接使って複数プロセス起動するスクリプトを書いてもいいが、yumで標準でinstallされるこの1プロセスだけ起動するようになっている設定ファイルを、起動したいプロセス数だけこのファイルをコピーして少し変更してあげるのが一番簡単。

sudo su -

# 8プロセス起動したいので7個分設定ファイルを作成する
for i in {2..8}
do
  sed "s@/run/munin/munin-cgi-graph.sock@/run/munin/munin-cgi-graph${i}.sock@" /usr/lib/systemd/system/munin-cgi-graph.socket > /usr/lib/systemd/system/munin-cgi-graph${i}.socket
  sed "s@munin-cgi-graph.socket@munin-cgi-graph${i}.socket@" /usr/lib/systemd/system/munin-cgi-graph.service > /usr/lib/systemd/system/munin-cgi-graph${i}.service
done

systemctl enable munin-cgi-graph.socket
systemctl start munin-cgi-graph.socket

for i in {2..8}
do
  systemctl enable munin-cgi-graph${i}.socket
  systemctl start munin-cgi-graph${i}.socket
done

NginxですべてのFastCGIプロセスにリクエストを分散する

複数プロセス起動できたので、Nginxのfastcgi_passですべてのプロセスにリクエストを分散できるようにする。

upstream munin_backend {
    server unix:/var/run/munin/munin-cgi-graph.sock;
    server unix:/var/run/munin/munin-cgi-graph2.sock;
    server unix:/var/run/munin/munin-cgi-graph3.sock;
    server unix:/var/run/munin/munin-cgi-graph4.sock;
    server unix:/var/run/munin/munin-cgi-graph5.sock;
    server unix:/var/run/munin/munin-cgi-graph6.sock;
    server unix:/var/run/munin/munin-cgi-graph7.sock;
    server unix:/var/run/munin/munin-cgi-graph8.sock;
}

server {
    listen 443 http2;

    略

    location /munin-cgi/munin-cgi-graph/ {
        fastcgi_split_path_info ^(/munin-cgi/munin-cgi-graph)(.*);
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_pass munin_backend;
        include fastcgi_params;
    }

    略
}

FastCGIのメモリリークバグの対策

少し話が脇にそれるが、MuninのFastCGIにはメモリリークがあるようで、ずっと起動しておくとどんどんメモリを食っていく。

今回複数プロセスを起動するようにしたので、おそらく1プロセス起動の時よりも速いペースでメモリを食いつぶすのではないかと思う。

対策としてcronでFastCGIを再起動してあげればいい。(systemdのversionが229以降であればRuntimeMaxSecというオプションが使えるらしい。How can I configure a systemd service to restart periodically?

$ cat << 'EOF' | sudo tee /etc/cron.d/munin_cgi_restart
0 7 * * * root systemctl restart munin-cgi-html.socket
0 7 * * * root systemctl restart munin-cgi-graph.socket
0 7 * * * root systemctl restart munin-cgi-graph2.socket
0 7 * * * root systemctl restart munin-cgi-graph3.socket
0 7 * * * root systemctl restart munin-cgi-graph4.socket
0 7 * * * root systemctl restart munin-cgi-graph5.socket
0 7 * * * root systemctl restart munin-cgi-graph6.socket
0 7 * * * root systemctl restart munin-cgi-graph7.socket
0 7 * * * root systemctl restart munin-cgi-graph8.socket
EOF

NginxのFastCGIキャッシュを導入し、同一ページへのリクエストであればFastCGIで処理せずともレスポンスを返せるようにする

Muninは5分ごとにグラフの値が更新されるため、1, 2分のキャッシュを行うのが適正である。キャッシュが長すぎるとグラフの最新値がなかなか表示できない。

NginxのconfでFastCGIキャッシュの設定を行う。設定項目はfastcgi_cacheから始まる項目である。特に凝った設定はせず、URLが/munin-cgi/munin-cgi-graph/から始まる場合のみキャッシュする。

http {
    略

    fastcgi_cache_path /tmp/munincache keys_zone=munincache:30m inactive=2m;
    fastcgi_cache_key "$scheme$request_method$host$request_uri";
}

略

server {
    listen 443 http2;

    略

    location /munin-cgi/munin-cgi-graph/ {
        fastcgi_split_path_info ^(/munin-cgi/munin-cgi-graph)(.*);
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_pass munin_backend;
        include fastcgi_params;

        fastcgi_cache        munincache;
        fastcgi_cache_valid  200 2m;
    }

    略
}
$ sudo mkdir /tmp/munincache
$ sudo chmod nginx.nginx /tmp/munincache
$ sudo chown 0700 /tmp/munincache

$ sudo systemctl restart nginx