最近、メモリの問題により、Apache mpm-prefork(PHPモジュール)からmpm-worker(PHP-FPM)に変更しました。私は非常に大きなPHPアプリケーションを実行しており、プリフォークプロセスごとに約20〜30Mを必要とします。
全体として、サーバーは安定して高速に実行されます。ただし、時々、ページはsomeユーザーが数分間利用できなくなります。
作業仮説1(=大まかなアイデア)は、プロセスの1つ(通常は2、場合によっては5または6まで)がハングし、このプロセスに割り当てられた各クライアント(たとえば、クライアントの50%)がエラーメッセージを受信することです。
作業仮説2は、MaxRequestsPerProcessが責任を負うというものです。 500回の呼び出しの後、プロセスはシャットダウンを試みますが、mod_fcgidは正常に強制終了せず、プロセスが強制終了を待機している間、さらにクライアントがプロセスに割り当てられます(そしてプロセスによって拒否されます)。しかし、Apacheがそれほど愚かであることを私は本当に想像することはできません。
私の問題は:いくつかを除いてエラーログには何もありません
[warn] mod_fcgid: process ???? graceful kill fail, sending SIGKILL
問題を追跡する場所のアイデアが不足しています。それは散発的に現れ、私はまだそれを誘発することができませんでした。サーバーのパフォーマンス(CPU/RAM)は、全体的な負荷がここ数週間低い範囲にあるため、問題にはなりません。
ヒントをありがとう。私の仮説についてのコメントはありますか(それは解決策を見つけるのに役立ちませんでしたが、MaxRequestsPerProcessを無効にしようとしましたが、それが役立つかどうかはまだわかりません)?この問題を追跡する方法についていくつかのアイデアをいただければ幸いです。
Apache構成
<Directory /var/www/html>
...
# PHP FCGI
<FilesMatch \.php$>
SetHandler fcgid-script
</FilesMatch>
Options +ExecCGI
</Directory>
<IfModule mod_fcgid.c>
FcgidWrapper /var/www/php-fcgi-starter .php
# Allow request up to 33 MB
FcgidMaxRequestLen 34603008
FcgidIOTimeout 300
FcgidBusyTimeout 3600
# Set 1200 (>1000) for PHP_FCGI_MAX_REQUESTS to avoid problems
FcgidMaxRequestsPerProcess 1000
</IfModule>
Apacheモジュール構成
<IfModule mod_fcgid.c>
AddHandler fcgid-script .fcgi
FcgidConnectTimeout 20
FcgidBusyTimeout 7200
DefaultMinClassProcessCount 0
IdleTimeout 600
IdleScanInterval 60
MaxProcessCount 20
MaxRequestsPerProcess 500
PHP_Fix_Pathinfo_Enable 1
</IfModule>
注:アプリケーションの実行に時間がかかることはめったにないため、タイムアウトは2時間に設定されました(たとえば、データベースの最適化を行う夜間のcronジョブ)。
スタータースクリプト
#!/bin/sh
PHP_FCGI_MAX_REQUESTS=1200
export PHP_FCGI_MAX_REQUESTS
export PHPRC="/etc/php5/cgi"
exec /usr/bin/php5-cgi
#PHP_FCGI_CHILDREN=10
#export PHP_FCGI_CHILDREN
パッケージバージョン
プロセスあたり20〜30MBは非常に小さいと思います。それはすべて実際には相対的ですが、たとえばほとんどのCMSアプリケーションは少なくとも100MBを必要とします。また、それが重要な場合、最大アップロードサイズは最大プロセスサイズによって制約されます。
サーバーが利用できない場合、phpワーカープロセスがすべてビジーである可能性がありますが、それは最も近い原因にすぎません。何かがサーバーの速度を低下させているため、少なくともしばらくの間、phpプロセスが着信要求に追いつくことができません。サーバーの速度が低下している原因を判断するのは困難ですが、「正常な強制終了の失敗」により、強制終了される予定のプロセスがディスクで待機している可能性が高いと思われます。
これが発生している間にログインしましたか?システムは応答性を感じますか?
上部で、プロセスの状態を確認し、IOを待機している「D」の状態を探します。これらはたくさんありますか?上部の要約の「wa」は、プロセスがIOの待機に費やす合計時間です。 (パーセントと表示されますが、これは1つのプロセッサの時間のパーセンテージである可能性があります)。 iotop、atop、vmstatなどのツールは、ディスクにバインドされているプロセス、およびディスクが全体的なパフォーマンスを制限している程度を確認するのにも役立つ場合があります。
ワーカープロセスが新しいリクエストを受け取ることができない場合に何が起こるかについてのあなたの理解は正しくありません。新しいリクエストは割り当てられません。
ワーカーを殺す前の1000リクエストは高いです。 10から50の間のどこかにドロップすることをお勧めします。
あなたは仮説1で正しい方向に進んでいると思います。mc0eのアドバイスはかなりしっかりしているので、私はほとんどそれに追加しています。
表示されているこれらのログメッセージは、個々のプロセスがpreforkMPMの下でロックされていることを示しています。これにより、worker。これは本番環境で以前に見たことがありますが、コードの動作に問題があることを意味します。
子あたりの最大リクエスト数が多いこととハングしているプロセスの間で、これはメモリの膨張の段階を設定します。ドキュメントでは、 ゼロ以外の値はメモリリークからの保護に役立ちます という事実を具体的にカバーしていますが、その値を高く設定しすぎると、メリットが失われます。プロセスをその上にハングアップさせると、全体的なメモリフットプリントがさらに複雑になります。
これにより、2つの重要なポイントがすぐに残ります。
MaxRequestsPerChild
を大幅に下げます。これは、個々のプロセスが重大なメモリリークを蓄積するのに十分長く存続するのを防ぐのに役立ちます...しかし、彼が言ったように、20-30Mはおそらくそれほど大きな問題ではありません。lsof
を実行するとmayは、コードの実行内容に応じてヒントを提供します(つまり、ファイルハンドルのリーク、および最大ファイルハンドルの上限に達することが関連している可能性があります)プロセスのデッドロックに)、しかしそれ以外の場合はコードのデバッグを見ています。