php-fpmをkubernetesで運用していて、ヘルスチェックで少し失敗したこと
php-fpmの子プロセス
php-fpmのプロセスを軽くおさらい
子プロセスの数を設定してチューニングをします
staticの場合は子プロセス数固定で、pm.max_children
で設定します
dynamicの場合は負荷に応じてプロセス数が変動しますが、最大値はstaticと同じくpm.max_children
での設定です
同時に処理するリクエストが増えて、子プロセスの最大値に達すると、当然それ以上子プロセスは増えないので、それ以降のリクエストは待機状態になり、処理中のものが終わってプロセスが空き次第処理されていきます
liveness/readiness
kubernetesでアプリケーションを運用する場合、基本的にはlivenessProbe/readinessProbeを設定しているはずです
方法はいくつかありますが、アプリケーションにヘルスチェックエンドポイントを作成してそこにhttpGetを設定するのがよくやるパターンかと思います
私もこの方法でphp-fpmのアプリケーションを運用していました
失敗
このアプリケーションが動作するクラスタに急激なリクエスト増加があり、HPAによるスケールが間に合わずレスポンスの遅延が起きました
この時、php-fpmは子プロセス上限数を使い切っていました
遅延はありつつも地道にリクエストを処理していたのですが、livenessProbeが失敗したことによりコンテナに再起動がかかってしまいました
ヘルスチェックの処理もひとつのリクエストの扱いになり、詰まったリクエストの中に紛れ込んで一緒に遅延してタイムアウトになったのです
ヘルスチェックがどれだけ軽い処理だったとしても、ボトルネックがプロセス数の上限で順番待ちになってしまえば関係なかったという
遅延しながらも処理はできていたのでコンテナ再起動はやめてほしかったんですけどね、設定に忠実に従った結果なので仕方ない
(処理によって必要なリソースは違うので、プロセス数の上限で管理するのがそもそも難しい…)
対応案
cpuやメモリのリソースではなくプロセス数の上限でボトルネックになるのが地味に面倒
- プロセス数の上限を増やす
- 詰まれば結局同じこと
- リソースとのバランスが崩れるとそれはそれで死ぬ
- 割り当てリソースを増やす
- これはもちろん意味がない
- プロセス数関係ないヘルスチェックにしたい
- ちなみにphp-fpmのステータスページを表示するのも子プロセスが必要です
対応
1. tcpSocketでprobeを設定
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
TCPソケットのコネクション確立で判定します
livenessはこれでもよいかも
readinessには足りないのではないかなと
2. ヘルスチェック用のプールを作成する
[global]
; グローバル設定
[www]
; アプリケーション用のプール設定
listen = [::]:9000
[health]
; ヘルスチェック用のプール設定
listen = [::]:9001
こんな感じでプールを設定して、ヘルスチェック用ものを確保します
proxy側でヘルスチェックのpathを専用プール側に振り分ける設定も必要です
そこを分けるのは果たしてアプリケーションのヘルスチェックとしてどうなのという疑問もあります
あとはメモリ消費量がほんの少し増えます
kubernetesでphp-fpmするの運用難しいです