fix: php-fpmをexec php-fpm -Fで起動しCrashLoopBackOff(9000二重bind)/502を修正
Helm Chart Release / release-chart (push) Successful in 6s

setupシェルの子として起動していたためコンテナ再起動時にシェル(PID1)がSIGTERMを子へ転送せず、旧php-fpmが9000を握ったまま残存し次回起動が Address in use で失敗する自己増殖ループに陥っていた。execでphp-fpmをPID1にしシグナルを直接受けさせ、-Fで必ずフォアグラウンド起動にする。
This commit is contained in:
2026-09-06 19:54:49 +09:00
parent 6de85b9ad4
commit b872dd4bd9
5 changed files with 89 additions and 2 deletions
+54
View File
@@ -0,0 +1,54 @@
# Changelog - Version 8.5.9-d
## 不具合修正
### php-fpm を `exec php-fpm -F` で起動(CrashLoopBackOff / 502 の修正)
php-fpm コンテナの起動コマンドを、setup シェルスクリプト末尾の `php-fpm`(シェルの子プロセスとしてフォアグラウンド起動)から **`exec php-fpm -F`** に変更しました。
**背景(実際に発生した障害)**
`selenium.enabled: true` の構成(例: hotels / booking.e-stay.jp)で、php-fpm コンテナが次のエラーで永続的に CrashLoopBackOff に陥る事象が発生しました。
```
[NOTICE] Failed implicitly binding to ::, retrying with 0.0.0.0
[ERROR] unable to bind listening socket for address '9000': Address in use (98)
[ERROR] FPM initialization failed
```
php-fpm が 9000 を bind できず起動に失敗するため、nginx が `fastcgi_pass 127.0.0.1:9000` に投げても FastCGI ではない応答が返り、`upstream sent unsupported FastCGI protocol version: 60` として **全リクエストが 502** になります(`60` = `<`、= 9000 を握っている別プロセスが HTML 等を返している)。
**原因**
起動コマンドが `sh -c "<setup> ... php-fpm"` の形で、**php-fpm が setup シェルの子プロセス**になっていた。コンテナ PID1 はシェルであり、シェルは既定で受け取った `SIGTERM` を子プロセスへ転送しない。そのためコンテナ(php-fpm コンテナのみ)の再起動時に、PID1 のシェルだけが先に終了し、**旧 php-fpm が一時的に残存して 9000 を握ったまま**になることがある。この状態で新しい php-fpm が起動すると `Address in use` で失敗 → 再起動 → 同じ残存 → という自己増殖ループに入り、CrashLoopBackOff から復帰できなくなる。selenium サイドカーは 9000 を使用しておらず無関係(別 pod でのポート占有調査でも確認済み)。
**対応**
- `exec php-fpm` … php-fpm が setup シェルを置き換えて**コンテナの PID1 になる**。kubelet からの `SIGTERM`/`SIGKILL` が php-fpm 本体へ直接届き、停止時に 9000 が確実に解放されるため、次回起動での二重 bind が起きない。
- `-F`(`--nodaemonize`)… php-fpm.conf の `daemonize` 設定に依存せず、必ずフォアグラウンドで起動する(master が背後に残る事故を防ぐ)。
これは全サイト共通で安全な変更(コンテナ内で php-fpm を動かす際の定石)であり、既存の正常稼働サイトの挙動は変わりません。
**復旧手順(重要)**
チャートを `8.5.9-d` に上げても、既存 pod のネットワーク名前空間に残存プロセスがある場合は、その pod を作り直してクリーンな状態にする必要があります。`helm upgrade` で新しい pod に置き換わりますが、置き換わらない場合は crashloop 中の pod を明示的に削除してください。
```bash
helm upgrade <リリース名> cafepieters/phpfpm --version 8.5.9-d -f values.yaml
# 置き換わらない場合:
kubectl delete pod -n <namespace> -l app.kubernetes.io/name=phpfpm
```
**対象ファイル**: `templates/deployment.yaml`, `Chart.yaml`, `README.md`, `CLAUDE.md`, `CHANGELOG-8.5.9-d.md`
## アップグレード手順
```bash
helm repo update
helm upgrade <リリース名> cafepieters/phpfpm --version 8.5.9-d -f values.yaml
```
---
**リリース日**: 2026-09-06
**担当**: プロサーバーエンジニア(Claude)
+6
View File
@@ -26,6 +26,12 @@ Websites 組織の各サイト(e-learn.jp / fcdomain.com / hotels / tskholding
## チャート改修履歴 ## チャート改修履歴
### php-fpm を `exec php-fpm -F` で起動しCrashLoopBackOffを修正(2026-09-06, v8.5.9-d)
setup シェル末尾の `php-fpm`(シェルの子プロセス)を **`exec php-fpm -F`** に変更。selenium 有効構成(hotels)で php-fpm が `unable to bind ... '9000': Address in use` を出して永続 CrashLoopBackOff になり、nginx が非FastCGI応答を受けて全リクエスト502(`unsupported FastCGI protocol version: 60`)になる障害の修正。原因は php-fpm が setup シェルの子で、コンテナ PID1 のシェルが `SIGTERM` を子へ転送しないため、コンテナ再起動時に旧 php-fpm が残存し 9000 を握ったまま次回起動が二重 bind で失敗する自己増殖ループ。`exec` で php-fpm を PID1 にしてシグナルを直接受けさせ(停止時に 9000 を確実解放)、`-F` で `daemonize` 設定に依存せず必ずフォアグラウンド起動にした。全サイト共通で安全(コンテナで php-fpm を動かす定石)。復旧には `helm upgrade` に加え、残存プロセスを持つ既存 pod の削除が必要な場合がある。
**対象ファイル**: `templates/deployment.yaml`, `Chart.yaml`, `README.md`, `CLAUDE.md`, `CHANGELOG-8.5.9-d.md`
### Ingressのproxy-body-sizeもuploadMaxSizeに連動(2026-08-06, v8.5.9-c) ### Ingressのproxy-body-sizeもuploadMaxSizeに連動(2026-08-06, v8.5.9-c)
v8.5.9-bのフォローアップ。`templates/ingress.yaml` で `merge (deepCopy .Values.ingress.annotations) (dict "nginx.ingress.kubernetes.io/proxy-body-size" .Values.uploadMaxSize)` を使い、ユーザーが明示指定していなければ `uploadMaxSize` をIngressの `proxy-body-size` にも自動反映。明示指定があればそちらが優先(sprigのmergeはdst=ユーザー指定annotationsが優先、srcのデフォルト値は不足分のみ補う)。これで `uploadMaxSize` 1箇所でnginx・PHP・Ingressの3箇所すべてが揃う。 v8.5.9-bのフォローアップ。`templates/ingress.yaml` で `merge (deepCopy .Values.ingress.annotations) (dict "nginx.ingress.kubernetes.io/proxy-body-size" .Values.uploadMaxSize)` を使い、ユーザーが明示指定していなければ `uploadMaxSize` をIngressの `proxy-body-size` にも自動反映。明示指定があればそちらが優先(sprigのmergeはdst=ユーザー指定annotationsが優先、srcのデフォルト値は不足分のみ補う)。これで `uploadMaxSize` 1箇所でnginx・PHP・Ingressの3箇所すべてが揃う。
+1 -1
View File
@@ -2,6 +2,6 @@ apiVersion: v2
name: phpfpm name: phpfpm
description: Nginx + PHP-FPM Helm Chart with external DB and optional Selenium support description: Nginx + PHP-FPM Helm Chart with external DB and optional Selenium support
type: application type: application
version: 8.5.9-c version: 8.5.9-d
appVersion: "8.5.9" appVersion: "8.5.9"
icon: https://upload.wikimedia.org/wikipedia/commons/thumb/2/27/PHP-logo.svg/330px-PHP-logo.svg.png icon: https://upload.wikimedia.org/wikipedia/commons/thumb/2/27/PHP-logo.svg/330px-PHP-logo.svg.png
+18
View File
@@ -867,6 +867,24 @@ composer:
- "autoconf" - "autoconf"
``` ```
### php-fpm が CrashLoopBackOff/全リクエストが 502(`Address in use` / `unsupported FastCGI protocol version: 60`)
**症状**: php-fpm コンテナが `CrashLoopBackOff` になり、nginx ログに `upstream sent unsupported FastCGI protocol version: 60`、php-fpm ログに次が出る。
```
[ERROR] unable to bind listening socket for address '9000': Address in use (98)
[ERROR] FPM initialization failed
```
**原因**: php-fpm が 9000 を bind できず起動失敗し、nginx が非 FastCGI 応答を受けて 502 になる。v8.5.9-c 以前は php-fpm を setup シェルの子プロセスとして起動していたため、コンテナ再起動時に PID1 のシェルが `SIGTERM` を子へ転送せず、旧 php-fpm が残存して 9000 を握ったまま次回起動が二重 bind で失敗する自己増殖ループに陥ることがあった。
**対策**: **v8.5.9-d 以降にアップグレードする**(起動を `exec php-fpm -F` に変更済み。php-fpm が PID1 になりシグナルを直接受けて 9000 を確実解放、`-F` で必ずフォアグラウンド起動)。アップグレード後も残存プロセスを持つ既存 pod が残る場合は作り直す。
```bash
helm upgrade <リリース名> cafepieters/phpfpm --version 8.5.9-d -f values.yaml
kubectl delete pod -n <namespace> -l app.kubernetes.io/name=phpfpm
```
### データベース接続エラー ### データベース接続エラー
**確認**: **確認**:
+10 -1
View File
@@ -252,7 +252,16 @@ spec:
{{- end }} {{- end }}
echo "Setup complete, starting PHP-FPM..." echo "Setup complete, starting PHP-FPM..."
php-fpm # exec で php-fpm をコンテナの PID1 に置き換える。
# 目的: setup シェルを介さず php-fpm 自身が SIGTERM/SIGKILL を直接受け取れる
# ようにする。シェルは既定で子プロセスへシグナルを転送しないため、exec せず
# `php-fpm`(子)のまま起動すると、コンテナ再起動時にシェル(PID1)だけが落ちて
# 旧 php-fpm が一時的に残存し、9000 を握ったまま次回起動が
# 「unable to bind ... '9000': Address in use」で失敗して CrashLoopBackOff に陥る。
# exec すれば php-fpm が PID1 になり、停止時に 9000 が確実に解放される。
# -F(--nodaemonize): 設定に依存せず必ずフォアグラウンドで起動する
# (デーモン化して master が背後に残る事故を防ぐ)。
exec php-fpm -F
ports: ports:
- containerPort: 9000 # PHP-FPMは9000ポートでリッスン - containerPort: 9000 # PHP-FPMは9000ポートでリッスン
volumeMounts: volumeMounts: