Files
phpfpm/CLAUDE.md
T
claude f8aaa5944d
Helm Chart Release / release-chart (push) Successful in 6s
docs: nginx.customConfig の取り違えと client_max_body_size 重複を防ぐ記述に修正
あるプロジェクトから「serverSnippet が効かない」「snippet は古いキー」との
指摘があり調査した結果、指摘の前提は誤りだった。

- 実際に使われる templates/configmap.yaml が読むのは snippet で、values.yaml と
  README も snippet で一致している。
- serverSnippet / httpSnippet は、.Values.nginx.enabled が存在せず一度も出力
  されない configmap-nginx.yaml だけが読むキーで、values・README に載ったことが
  なく、指定しても黙って無視される。
- README 例6・例9 が snippet に client_max_body_size を書く手順になっており、
  v8.5.9-b 以降は uploadMaxSize の自動出力と重複して nginx が起動しない
  (2026-09-29 の障害の発生源)。例6 は location ~ \.php$ も重複定義していた。

挙動の変更はなくドキュメント・コメントのみ。
- values.yaml / README: 読まれるキーは snippet のみ、挿入先は server ブロック内、
  client_max_body_size は書かず uploadMaxSize を使うことを明記
- README 例6・例9 を uploadMaxSize 指定に修正
- configmap-nginx.yaml: 未使用であることを示す注記を追加

手動リリース 8.5.11-a。
2026-10-20 20:38:07 +09:00

22 KiB
Raw Blame History

CLAUDE.md — phpfpm Helm Chart

共通ルール(要約) — 詳細は P:\Claude\CLAUDE.md(全体共通)と P:\Claude\HelmChart\CLAUDE.md(Helm チャート共通: リリースフロー・ワークフロー・Gitea Secrets・クラスタ構成)を参照。 作業前に pull / 機能変更時は README 更新 / 作業単位ごとに commit → push まで / コミットに AI クレジットを付けない / pull で入った変更を revert しない / Helm・PHP はローカル実行不可(Python のみ可)。

リポジトリ概要

Raspberry Pi などのベアメタルで稼働することを想定した、Kubernetes 上で動作する Nginx + PHP-FPM の Helm チャート。 Websites 組織の各サイト(e-learn.jp / fcdomain.com / hotels / tskholding.com など)はこのチャートでデプロイされる。

  • Gitea リポジトリ: ssh://git@192.168.9.65/helmchart/phpfpm
  • Helm リポジトリ: https://git.cafepieters.com/api/packages/helmchart/helm
  • 主アプリ: PHP(docker.io/php:<tag>-fpm-alpine)。サブアプリ: nginx

実行環境について

PHP の実行環境(インタープリタ)はこのリポジトリには含まれていない。 チャートは docker.io/php:<tag>-fpm-alpine の公式イメージを Kubernetes 上で起動するものであり、PHP コードそのものは PVC(/var/www/html)に別途配置する必要がある。

設計上の注意(既知の落とし穴)

  • templates/configmap.yaml が実際に使われている nginx 設定。templates/configmap-nginx.yaml は .Values.nginx.enabled が values.yaml に存在せず常に未出力の死んだファイルなので注意。
  • PVC はルートを /var/www にマウントする(html/ が公開フォルダ、それ以外は非公開)。/composer は Pod 再起動ごとにクリーン再構築される使い捨て領域。
  • uploadMaxSize 1箇所で nginx client_max_body_size・PHP upload_max_filesize/post_max_size・Ingress proxy-body-size の3箇所が連動する。
  • Secret / ConfigMap をファイルとして PHP-FPM ワーカー(www-data)から読ませる場合、権限は 0644 にする(0600 だと読めない)。
  • Selenium(selenium.enabled: true)は v8.5.10-b 以降、nginx/php-fpm と別の Deployment/Service(<release>-phpfpm-selenium)。同一 Pod のサイドカーにはしない(Selenium 単体の不調でメインの Pod が Service の Endpoints から外れ、静的ページ配信まで巻き込んで全断する設計だったため分離した)。

チャート改修履歴

nginx.customConfig の取り違え防止(ドキュメント修正、2026-10-20, v8.5.11-a)

  • 症状 / 背景: あるプロジェクトのレポートで「serverSnippet が効かない。既定の snippet: はどのテンプレートも読まない古いキーでは」という指摘があった。別件で、snippet に client_max_body_size を書いていたプロジェクトが directive is duplicate で nginx 起動失敗する障害(2026-09-29)も起きていた。
  • 原因:
    • 指摘は誤り。実際に使われる templates/configmap.yaml が読むのは snippet で、values.yaml・README と一致している。serverSnippet / httpSnippet は、一度も出力されない templates/configmap-nginx.yaml(.Values.nginx.enabled が values に無く常に偽)だけが読むキーで、values・README に載ったことがない(コミット 31106e0 由来)。指定すると黙って無視される。
    • README 例6・例9 が snippet に client_max_body_size を書く手順を案内しており、v8.5.9-b 以降は uploadMaxSize の自動出力と重複して起動失敗する。例6 はチャート本体と重複する location ~ \.php$ も再定義していた。
  • 修正内容: 挙動の変更なし(ドキュメント・コメントのみ)。values.yaml と README に「読まれるキーは snippet のみ、挿入先は server ブロック内、client_max_body_size は書かず uploadMaxSize を使う」を明記。README 例6・例9 を uploadMaxSize 指定に修正。configmap-nginx.yaml 先頭に「未使用」の注記を追加。
  • やらなかったこと: snippet を serverSnippet に改名する(動いているキーを壊すため)。serverSnippet を snippet の別名として有効化する(これまで無視されていた設定が突然有効になり、重複で起動失敗する恐れがあるため)。http レベルの snippet が必要になった場合は、別途 configmap.yaml に正式対応を追加する。
  • 対象ファイル: values.yaml, README.md, templates/configmap-nginx.yaml(コメントのみ), Chart.yaml

Cron(CronJob)定期実行機能を追加(2026-09-27, v8.5.10-e)

  • 背景: 定期実行したいPHPスクリプト(レポート送信・バッチ処理等)を動かす手段がなく、外部で別途スケジューラを用意する必要があった。
  • 追加内容: cron.jobs(配列)にジョブを定義すると、templates/cronjob.yamlがジョブごとにKubernetes CronJobを生成する。php-fpmと同じPHPイメージを使用し、PVCルートを/var/wwwにマウント、externalDatabase/phpEnv/extraEnv/extraEnvFrom/SELENIUM_URLをphp-fpmコンテナと同じ内容で注入する。smtp.enabled: trueの場合はCronJob内でも自動的にmsmtpを導入する(templates/deployment.yamlのSMTP設定と重複実装、変更時は両方揃えること)。
  • 設計上の注意点:
    • CronJobが生成するPodのラベル(app.kubernetes.io/name)はphpfpm.cron.nameヘルパーで<chart>-cronというバリアント名にしている。Selenium分離(v8.5.10-b)と同じ理由で、nginx/php-fpm DeploymentのmatchLabels(name: phpfpm, instance: <release>)と一致させると、ReplicaSetコントローラーがCronJob由来のPodを誤って自身の管理対象とみなし削除する事故につながるため。
    • CronJob名はtrunc 52で切り詰めている(Kubernetesの実務上の制約。CronJobは生成するJob/Pod名にタイムスタンプ等のsuffixが付くため、フルの253/63文字ではなく52文字程度に収める必要がある)。
    • Composer(/composer)・Python venv(/venv)はデフォルトでPVC非マウントの使い捨て領域のため、CronJobのPodには存在しない。意図的にCronJob側では再インストールを行わない設計とした(頻繁な実行での再インストールコストを避けるため)。CronJobから使いたい場合はcomposer.workingDir/python.venvDirを/var/www配下に変更しPVC上へ永続化することをREADMEで案内している。
  • 対象ファイル: templates/cronjob.yaml(新規), templates/_helpers.tpl, values.yaml, README.md, Chart.yaml

ingress.tls[].secretNameをhostから自動導出可能に(2026-09-27, v8.5.10-d)

  • 背景: ingress.hosts[].host は既にYAMLアンカー(&appDomain)で ingress.tls[].hosts と値を共有できるようになっていたが、ingress.tls[].secretName は独立した文字列指定が必須で、ドメイン変更時に別途書き換えが必要だった。
  • 修正内容: templates/ingress.yaml で secretName を省略可能にし、未指定時はそのエントリの最初のhostから自動生成する(.を-に置換して-tlsを付与。例: example.tld → example-tld-tls)。明示的に指定した場合はそちらが優先される(後方互換)。デフォルトのvalues.yamlも自動導出を使う例に更新。
  • 対象ファイル: templates/ingress.yaml, values.yaml, README.md, Chart.yaml

v8.5.10-bのDeployment.spec.selector変更が既存リリースのアップグレードを壊す不具合を修正(2026-09-26, v8.5.10-c)

v8.5.10-bでnginx/php-fpm Deploymentのspec.selector.matchLabelsにapp.kubernetes.io/component: webを追加したが、Deployment.spec.selectorは作成後immutableのため既存リリースのhelm upgradeが「field is immutable」で失敗する不具合を確認・修正した。既存Deploymentのselectorは元の内容(name+instanceのみ)に戻し、Selenium側はcomponentラベルではなくapp.kubernetes.io/name自体を<chart名>-seleniumという別名にする方式(新ヘルパーphpfpm.selenium.name/phpfpm.selenium.selectorLabels)に変更した。componentラベルだけを足す設計だと、SeleniumのPodもnginx/php-fpm側Deploymentのselector(name+instance)に一致してしまい2つのDeployment(ReplicaSet)が同じPod集合を取り合う選択重複を起こすため、name自体をvariantにして構造的に防止する。v8.5.10-bは未適用ならスキップしてv8.5.10-c以降を直接使うこと。

対象ファイル: templates/_helpers.tpl, templates/deployment.yaml, templates/deployment-selenium.yaml, templates/service-selenium.yaml, Chart.yaml, CHANGELOG-8.5.10-c.md

Selenium を独立Deployment/Serviceに分離 + resources死んだ設定を修正(2026-09-26, v8.5.10-b)

selenium.enabled: true 構成で断続的な php-fpm CrashLoopBackOff とサイト完全停止が続いた件の調査で、2つの設計上の欠陥を確認した。(1) resources values キーがテンプレート側で全く参照されておらず、nginx/php-fpm/selenium いずれのコンテナにも requests/limits が反映されない死んだ設定だった(全コンテナが QoS Class: BestEffort → ノードのメモリ逼迫時に OOM Kill の対象になりやすい)。(2) nginx・php-fpm・Selenium が同一 Pod のコンテナで、Kubernetes の Pod Ready 判定は全コンテナの AND のため、Selenium だけが不調(起動失敗・OOM 等)でも Pod 全体が Service の Endpoints から外れ、Selenium を使わない静的ページ配信まで巻き込まれてサービス全体が停止していた。対応: Selenium を独立した Deployment(<release>-phpfpm-selenium)/Service に分離(app.kubernetes.io/component: web/selenium ラベルでセレクタを分離、php-fpm の SELENIUM_URL は新Serviceを指すよう自動注入)。resources を resources.nginx/resources.phpfpm/selenium.resources に構造化し実際にコンテナへ反映されるよう修正。既存 selenium.enabled: true 利用者は selenium.mountHtml(PVC html サブパスへの依存があれば true + persistence.accessMode: ReadWriteMany)を確認する必要がある。OOM Kill が実際のクラッシュトリガーだったかは本リリース時点では未確認(要検証)だが、上記2点はコード上確認済みの確定した欠陥のため先行修正した。

対象ファイル: templates/deployment.yaml, templates/deployment-selenium.yaml(新規), templates/service.yaml, templates/service-selenium.yaml(新規), values.yaml, README.md, Chart.yaml, CHANGELOG-8.5.10-b.md

外部Secret連携(extraEnvFrom / extraEnv / SMTP existingSecret)を追加(2026-09-08, v8.5.10-a)

背景: 別リポジトリの helmchart/secret-manager チャートが各ネームスペースへ配布する cred-<名前> Secret(メールの msmtprc / API キー)を、phpfpm 側から参照する口が無かった (phpEnv は直書きのみ、SMTP パスワードは smtp.auth.password 直書き+自作 Secret のみ)。

修正内容:

  • values.yaml に extraEnvFrom(envFrom 配列)と extraEnv(生の env エントリ配列)を追加。 php-fpm コンテナに既存 Secret/ConfigMap をまるごと env 注入できる。
  • smtp.auth.existingSecret / existingSecretKey を追加。チャートで msmtprc を生成しつつ パスワードファイルだけ既存 Secret から取る。
  • smtp.existingMsmtprcSecret / existingMsmtprcSecretKey を追加。完成済み msmtprc を持つ 既存 Secret をそのまま /etc/smtp-config/msmtprc としてマウント(secret-manager が生成する msmtprc キーを想定)。指定時は configmap-smtp を生成せず host/port/auth.* は無視、 smtp-secrets ボリュームも張らない(msmtprc にパスワードが内包されるため)。
  • templates/secret-smtp.yaml / templates/configmap-smtp.yaml を上記フラグで出し分け。
  • 既定値は全て空 → 既存挙動は不変(helm template の全シナリオで確認済み)。
  • 主アプリ(PHP 8.5.10)は変わらないため手動リリース 8.5.10-a。

対象ファイル: values.yaml, templates/deployment.yaml, templates/configmap-smtp.yaml, templates/secret-smtp.yaml, Chart.yaml, README.md, CLAUDE.md

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)

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箇所すべてが揃う。

対象ファイル: templates/ingress.yaml, values.yaml, README.md, Chart.yaml, CHANGELOG-8.5.9-c.md

uploadMaxSize の追加(2026-08-06, v8.5.9-b)

nginxの client_max_body_size とPHPの upload_max_filesize/post_max_size を1つの uploadMaxSize(デフォルト "100m")で連動させるパラメータを追加。片方だけ上げてももう片方の上限でアップロードが無言で失敗する問題への対応。PHP側は起動時に /usr/local/etc/php/conf.d/99-uploads.ini を常時生成(他機能の有効/無効に依存しない)。memory_limitは対象外(別途必要なら将来検討)。

対象ファイル: values.yaml, templates/configmap.yaml, templates/deployment.yaml, README.md, Chart.yaml, CHANGELOG-8.5.9-b.md

sitemap.xml のContent-Type明示(2026-08-06, v8.5.9-a)

templates/configmap.yaml に location = /sitemap.xml { types { } default_type "application/xml; charset=utf-8"; ... } を追加。標準mime.typesでは .xml が text/xml 扱いでcharset無しになる問題への対応。静的ファイルの場合のみ有効、PHP動的生成には効かない(PHP側でheader()指定が必要)。設定不要・常時有効。

対象ファイル: templates/configmap.yaml, README.md, Chart.yaml, CHANGELOG-8.5.9-a.md

Python導入オプションの追加(2026-07-19, v8.5.8-a)

python セクションを新設し、values.yaml の設定だけでPython仮想環境(venv)を導入可能にした。Composerと同じ設計方針(デフォルト導入先 /venv はPVC非マウントの使い捨て領域、packages/useRequirementsTxtの二択、additionalApkPackagesでネイティブ拡張ビルド対応)。requirements.txtの展開は trim | nindent 14(v8.5.6-dのバグ修正と同じ手法)。PHPからは exec('/venv/bin/python3 script.py') で呼び出す。

なお、この改修中に自動更新ワークフローがPHP 8.5.8へのフルリリース(v8.5.8)を実行したため、本機能はそのタグの上に手動リリース 8.5.8-a として適用した。

対象ファイル: values.yaml, templates/deployment.yaml, README.md, Chart.yaml, CHANGELOG-8.5.8-a.md

/etc/msmtprc の権限を0600→0644に修正(2026-07-19, v8.5.6-g)

v8.5.6-f のフォローアップ。/etc/smtp-secrets/password の権限修正だけでは不十分で、起動スクリプトが cp 後に chmod 600 していた /etc/msmtprc 自体もrootのみ読み取り可のままだった(msmtp: account default not found, exit:78)。chmod 644 に変更。msmtprcには平文パスワードは含まれない(passwordeval経由で別ファイル参照)ため実害なし。

対象ファイル: templates/deployment.yaml, Chart.yaml, CHANGELOG-8.5.6-g.md

SMTPパスワードSecretの権限を0600→0644に修正(2026-07-19, v8.5.6-f)

smtp-secrets ボリュームの mode: 0600 が原因で、rootではなく www-data(PHP-FPMワーカー)として動く msmtp が /etc/smtp-secrets/password を読めず、SMTP認証に失敗する不具合を修正。0644 に変更(Pod内にしか存在しないファイルのため許容範囲)。

対象ファイル: templates/deployment.yaml, Chart.yaml, CHANGELOG-8.5.6-f.md

PVCルートを /var/www にマウント + /composer を使い捨て化(2026-07-19, v8.5.6-e)

php-fpm コンテナのマウントを「html サブパスのみ」から「PVCルート → /var/www」に変更。html/ は公開フォルダ、それ以外(/var/www/<任意>)は非公開ファイルの格納先として使える。nginx は html サブパスのみマウントするため非公開分は公開されない。あわせて /composer のPVC永続化マウントを廃止し、Pod再起動ごとにクリーン再構築される使い捨て領域とした(マルウェア混入対策、ユーザー要望)。永続化したい場合は workingDir に /var/www/composer 等を指定。

対象ファイル: templates/deployment.yaml, values.yaml, README.md, Chart.yaml, CHANGELOG-8.5.6-e.md

useComposerJson の YAML parse error 修正(2026-07-19, v8.5.6-d)

useComposerJson: true 指定時、heredoc 内の {{ .Values.composer.composerJson }} がインデント無しで展開され、複数行JSONの2行目以降がブロックスカラーを破壊して YAML parse error になる潜在バグを修正。trim | nindent 14 で展開するよう変更(composerLock も同様)。

対象ファイル: templates/deployment.yaml, Chart.yaml, CHANGELOG-8.5.6-d.md

Composerデフォルト導入先を /composer に変更(2026-07-19, v8.5.6-c)

/var/www はWebルート /var/www/html の親のため、ネストマウントでPVCの composer サブパス内に空の html/ が生成されていた。デフォルトをWebルートと親子関係にない /composer に変更してネストを解消。autoloadは require_once '/composer/vendor/autoload.php';(values.yaml のコメントにも明記)。

対象ファイル: values.yaml, templates/deployment.yaml, README.md, Chart.yaml, CHANGELOG-8.5.6-c.md

旧composer.jsonの自動移行 + mbstring依存の自動追加(2026-07-19, v8.5.6-b)

v8.5.6-a のフォローアップ。起動時に workingDir に composer.json が無く旧Webルート /var/www/html にある場合、composer.json / composer.lock / vendor/ を自動移行する。また additionalPhpExtensions に mbstring 指定時、ビルドに必要な oniguruma-dev を自動追加(GDと同じ仕組み)。

なお、PVC上の composer/html/(空ディレクトリ)はネストマウントのマウントポイント作成による正常な副産物で、実害なし。

対象ファイル: templates/deployment.yaml, README.md, Chart.yaml, CHANGELOG-8.5.6-b.md

composer.workingDir — Composer導入先のWebルート外への変更(2026-07-19, v8.5.6-a)

Composerの導入先をWebルート /var/www/html からWebルート外の /var/www に変更し、vendor/ や composer.json がWeb経由で公開されないようにした。values.yaml の composer.workingDir で導入先を変更可能(旧動作に戻すには "/var/www/html" を指定)。導入先がWebルート以外の場合、PVCの composer サブパスをマウントして永続化する。

PHP側の読み込みは require_once '/var/www/vendor/autoload.php';。

対象ファイル: values.yaml, templates/deployment.yaml, README.md, Chart.yaml, CHANGELOG-8.5.6-a.md

phpEnv — 汎用 PHP 環境変数サポート(2026-04-17)

values.yaml に phpEnv セクションを追加し、DB 接続情報と同様に任意のキーと値を PHP-FPM コンテナの環境変数として渡せるようにした。

phpEnv:
  API_KEY: "your-api-key"
  STRIPE_SECRET: "sk_live_..."
  REDIS_URL: "redis://localhost:6379"

PHP 側では $_ENV['API_KEY'] または getenv('API_KEY') で取得できる。

対象ファイル: values.yaml, templates/deployment.yaml, README.md