「502 Bad Gateway」とページが表示されている。
と問い合わせを受けたことがあります。
Nginxをフロント、php-fpmをバックエンドに配置するシステムで「502 Bad Gateway」に遭遇されたことはありますか?
きっと皆さんあると思うんですけど、ここでは「502 Bad Gateway」を検知する方法について考察したいと思います。
手っ取り早く方法を知りたい方は、同ページ内の「php-fpmモニタリングページを利用」からご覧ください。
目次
502 Bad Gatewayをどう検知する?
Webのレスポンスやステータスコードの値は定期的にチェックしていました。
それなのに監視サーバーからのアラートは無かったんです。
監視サーバーは200を検知
監視サーバーにはステータスコードが「200」以外であれば通知をするという設定。
HTTP OK: Status line output matched "200"
アラートが送信されることなく気付くことができなかった理由は、
502 Bad Gatewayが表示されている間も監視サーバーは「200」の値を取得。
WebサーバーとAPサーバー間の異常
このことから、Webサーバーとアプリケーションサーバーとの間に異常があることは明らかなんですけど、php-fpmのプロセスは動いていました。
[30-Jul-2018 09:31:07] WARNING: [pool xxxxxxxx] server reached pm.max_children setting (10), consider raising it
php-fpmのログにはpm.max_childrenの値を増やすことを検討すべきと書かれていたので要チューニングなのですが、状況的にはこうです。
php-fpmのレスポンスが悪いから「502 Bad Gateway」を出した、でもNginxは元気なので200のステータスコードを返すといったところ。
このようなケースで、皆さんはどうやって502を検知していますか?
php-fpmモニタリング機能の利用
php-fpmには外部から状態をモニタリングできる機能があるので、これを使わない手はない。デフォルトでは無効になっています。
php-fpmの設定ファイル「www.conf」を編集してモニタリングできるようにします。
; The ping URI to call the monitoring page of FPM. If this value is not set, no
; URI will be recognized as a ping page. This could be used to test from outside
; that FPM is alive and responding, or to
; - create a graph of FPM availability (rrd or such);
; - remove a server from a group if it is not responding (load balancing);
; - trigger alerts for the operating team (24/7).
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
;ping.path = /ping
今回はデフォルトの「/ping」で応答させる手順を説明をします。
ping.pathの有効化
ping.path = /ping
php-fpmの設定ファイル(www.conf)に「ping.path = /ping」と設定を入れます。
同時にpm.status_pathを設定することで、php-fpmのステータス情報を表示させることができるようになります。
pm.status_path = /status
設定が終わったらphp-fpmを再起動。
Nginxの設定
Nginxが「/ping」のリクエストに対してレスポンスできるように設定を追加。
location ~ ^/(status|ping)$ {
allow xxx.xxx.xxx.xxx;
deny all;
access_log off;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_pass unix:/run/php-fpm-www.sock;
}
「allow xxx.xxx.xxx.xxx」は環境に合わせて変更し、Nginxを再起動します。
curlで/pingを確認
allowで設定した環境から「/ping」にアクセスしてレスポンスを確認してみましょう。
curl https://www._DOMAIN_NAME_/ping
pongの応答があれば正常
php-fpmが正常に稼働していれば、以下のように「pong」と表示されます。
pong
ping-pongのやり取りができていれば大丈夫ってことです。
監視サーバーで検知
つまり、監視サーバーから「ping.path」に設定したURLにアクセスして、値が「pong」以外であればアラートを出せばいい訳です。
Nagiosのcheck_httpでチェック
前述のphp-fpmモニタリングページを利用の設定が完了していることが前提で、Nagiosのcheck_httpを利用してチェックします。
/usr/lib/nagios/plugins/check_http --ssl --sni -I _IP_ADDRESS_ -H www._DOMAIN_NAME_ -u /ping --string 'pong'
バーチャルホストでは、「--sni」オプションを付けて実行。
HTTP OK: HTTP/1.1 200 OK - 302 bytes in 0.012 second response time |time=0.011798s;;;0.000000 size=302B;;;0
HTTP CRITICAL: HTTP/1.1 502 Bad Gateway - string 'pong' not found on 'https://www.xxxxx.jp:443/ping' - 310 bytes in 0.040 second response time |time=0.039823s;;;0.000000 size=310B;;;0
監視エージェントも必要なく外部からチェックできるのでNagios監視サーバーに上記「check_http」を仕込めばphp-fpmの異常を検知することができます。
ログで「HTTP/2.0" 502」を検知
Nginxのログを確認することでも「502 Bad Gateway」を検知することができます。
grep 'HTTP\/2.0\" 502' /var/log/nginx/*/access.log
バーチャルホストを設定しているケースでは、上記のように「*」を使って全てのホストのログを検索するようにします。
/var/log/nginx/www.yyyyy.jp/access.log:2.347 - - xxx.xxx.xxx.xxx - - [05/Jul/2018:21:56:34 +0900] "GET /robots.txt HTTP/2.0" 502 552 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36" "-"
/var/log/nginx/www.zzzzz.com/access.log:23.718 - - xxx.xxx.xxx.xxx - - [05/Jul/2018:20:08:17 +0900] "GET /ana-mileage/life-time-miles.html HTTP/2.0" 502 552 "-" "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm) Chrome/103.0.5060.134 Safari/537.36" "-"
/var/log/nginx/www.zzzzz.com/access.log:10.629 - - xxx.xxx.xxx.xxx - - [05/Jul/2018:21:03:51 +0900] "GET / HTTP/2.0" 502 552 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36" "-"
/var/log/nginx/www.zzzzz.com/access.log:4.050 - - xxx.xxx.xxx.xxx - - [05/Jul/2018:21:31:05 +0900] "GET /cabin/economy-class.html HTTP/2.0" 502 150 "https://www.google.co.jp/" "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1" "-"
/var/log/nginx/www.zzzzz.com/access.log:3.461 - - xxx.xxx.xxx.xxx - - [05/Jul/2018:21:31:05 +0900] "GET /cabin/a320neo-handset.html HTTP/2.0" 502 150 "https://www.google.com/" "Mozilla/5.0 (iPhone; CPU iPhone OS 16_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/109.0.5414.112 Mobile/15E148 Safari/604.1" "-"
これをシェル等で毎分チェックして「HTTP/2.0" 502」にマッチしたらアラートを出せば検知できます。
1分前のログのみをチェック
ポイントはdateコマンドを使い「1分前」のログに「HTTP/2.0" 502」という文字列が含まれていないかチェックすること。
そうでなければ一度「HTTP/2.0" 502」を検出したログファイルであれば、毎回アラートが出ることになります。
1分前の時刻をNginxのログフォーマットで表示してみます。
date --date '-1 minute' "+%d/%h/%Y:%H:%M"
grepで「502」と「1分前」を検索
つまりgrepコマンドで「HTTP/2.0" 502」と「1分前の時刻」をログの中から検索。
grep "HTTP\/2.0\" 502" /var/log/nginx/*/access.log | grep `date --date '-1 minute' "+%d/%h/%Y:%H:%M"`
/var/log/nginx/www.zzzzz.com/access.log:0.000 - - xxx.xxx.xxx.xxx - - [13/Feb/2023:09:45:03 +0900] "GET / HTTP/2.0" 502 552 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36" "-"
ヒットすればアラートを飛ばし、php-fpmを再起動させる等の処理を加えれば「502 Bad Gateway」の検知と一時対処はできます。
502 Bad Gateway の原因
Nginxとphp-fpmの組み合わせ、WordPressが動く環境でもありますが「502 Bad Gateway」が頻繁に表示される場合、まずはphp-fpmの設定を確認してみてください。
pm.max_children
php-fpm設定ファイル内の「pm.max_children」の値は何がセットされていますか?
CPUのコア数を無視した値になっていませんか?
grep processor /proc/cpuinfo | wc -l
「CPUのコア数」よりも「pm.max_children」の値が大き過ぎると高負荷の状態になりやすくなります。
pm.max_requests
「502 Bad Gateway」と表示された時、php-fpmを再起動することで復旧することから、メモリリークしていると察しが付きます。
メモリリーク回避に「pm.max_requests」という設定があります。
一定数のリクエストを処理したらプロセスを自動で再起動させるというものです。
pm.max_requests = 1000
php-fpmの設定ファイルにこのような記述を追記するとメモリ使用の増大を抑えることができる可能性があります。
php-fpmメモリ使用量の計算
php-fpmに限った話ではありませんが、プロセスが使用するメモリ量がどのくらいなのかを知ることは設定をする上でとても重要です。
php-fpmのメモリ使用量(RSSの合計)を確認します。
ps aux | grep php-fpm | grep -v grep | grep -v master | awk '{sum+=$6} END {print sum}'
1プロセス当たりの平均値は以下のコマンドで求められます。
ps aux | grep php-fpm | grep -v grep | grep -v master | awk '{sum+=$6} END {print sum/NR}'
闇雲にではなく、値を取ることで設定値の調整をしやすくなりますし、リクエストに対してサーバーのキャパは十分なのかなど明確になります。
まとめ
502 Bad Gateway を検知する方法として、
- php-fpmのモニタリング機能を有効にする
- 監視サーバーに返り値「pong」をチェックさせる
- アクセスログ内に「HTTP/2.0" 502」の書き込みがないか
という内容を書いてきました。
みなさんの検知方法をシェアしていただけると嬉しいです。
コメント