ingress.hosts[].hostは既にYAMLアンカー(&appDomain)でingress.tls[].hosts と値を共有できていたが、secretNameは独立した文字列指定が必須で、 ドメイン変更時に別途書き換えが必要だった。 secretNameを省略可能にし、未指定時はそのエントリの最初のhostから 自動生成する(.を-に置換して-tlsを付与。例: example.tld → example-tld-tls)。 明示的に指定した場合はそちらが優先されるため既存挙動は変わらない。 手動リリース 8.5.10-d。
PHP-FPM Helm Chart
Alpine LinuxベースのNginx + PHP-FPM環境をKubernetesにデプロイするためのHelmチャートです。Composer、Selenium、外部データベース接続など、本格的なPHPアプリケーション実行に必要な機能を統合しています。
特徴
- 🚀 軽量: Alpine Linuxベースで高速起動
- 📦 Composer対応: 依存パッケージ管理を柔軟にカスタマイズ
- 🌐 Nginx統合: PHP-FPMとNginxのマルチコンテナ構成
- 🔧 カスタマイズ可能: Nginx設定、PHP拡張を自由に追加
- 🤖 Selenium統合: ブラウザ自動化・スクレイピングに対応
- 💾 永続化ストレージ: PVCによるデータ永続化
- 🔐 外部DB接続: MySQL/MariaDB接続機能を内蔵
- 📊 本番対応: Ingress、TLS、リソース管理に対応
クイックスタート
前提条件
- Kubernetes 1.19+
- Helm 3.0+
- PV プロビジョナー(persistence有効時)
基本インストール
# Helmリポジトリを追加
helm repo add cafepieters https://git.cafepieters.com/api/packages/helmchart/helm
helm repo update
# デフォルト設定でインストール
helm install my-phpfpm cafepieters/phpfpm
# カスタム設定でインストール
helm install my-phpfpm cafepieters/phpfpm -f my-values.yaml
# 特定のバージョンをインストール
helm install my-phpfpm cafepieters/phpfpm --version 2.0.0
ソースからのインストール
# リポジトリのクローン
git clone https://git.cafepieters.com/helmchart/phpfpm.git
cd phpfpm
# デフォルト設定でインストール
helm install my-phpfpm .
# カスタム設定でインストール
helm install my-phpfpm . -f my-values.yaml
動作確認
# Podの確認
kubectl get pods -l app.kubernetes.io/name=phpfpm
# サービスの確認
kubectl get svc -l app.kubernetes.io/name=phpfpm
# ログ確認
kubectl logs -l app.kubernetes.io/name=phpfpm -c php-fpm
kubectl logs -l app.kubernetes.io/name=phpfpm -c nginx
アーキテクチャ
┌─────────────────────────────────────────────┐
│ Kubernetes Pod │
│ │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ Nginx │─────▶│ PHP-FPM │ │
│ │ :80 │ │ :9000 │ │
│ └──────────────┘ │ + Composer │ │
│ │ │ + Extensions │ │
│ │ └─────────────────┘ │
│ │ │ │
│ ┌──────▼──────────────────────▼────────┐ │
│ │ Persistent Volume (PVC) │ │
│ │ /var/www/html │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
│ │
▼ ▼ SELENIUM_URL(自動注入)
┌─────────────────────────────┐ ┌──────────────────────────────┐
│ External Database (Optional)│ │ Selenium Deployment(Optional)│
│ MySQL/MariaDB │ │ 独立Pod・独立Service :4444 │
└─────────────────────────────┘ └──────────────────────────────┘
Selenium は独立した Deployment/Service(v8.5.10-b以降)。以前は上の Pod 内の サイドカーコンテナだったが、Selenium 単体の不調(起動失敗・OOM等)で Pod 全体が Service の Endpoints から外れ、サイト全体が停止する問題があったため分離した (下記トラブルシューティング参照)。
主要な設定パラメータ
基本設定
| パラメータ | 説明 | デフォルト |
|---|---|---|
replicaCount |
Podのレプリカ数 | 1 |
image.php.tag |
PHPバージョン | 8.5.2-fpm-alpine3.23 |
image.nginx.tag |
Nginxバージョン | 1.29.4-alpine-perl |
service.type |
Serviceタイプ | LoadBalancer |
service.port |
Serviceポート | 80 |
uploadMaxSize |
アップロード最大サイズ(nginx/PHP両方に反映) | "100m" |
📌 アップロードサイズ上限について
uploadMaxSizeの値は、以下すべてに同時に反映されます。どれか一つでも小さいと、そこで上限に達して失敗するため、この1つの値で揃える設計です。
- nginx(Podコンテナ内)の
client_max_body_size- PHPの
upload_max_filesize/post_max_sizeingress.enabled: trueの場合、ingress-nginxのnginx.ingress.kubernetes.io/proxy-body-size(明示指定があればそちらが優先)単位は
k/m/g(大文字小文字問わず)が使用できます(例:"200m","1g")。大きなファイルをPHP側でメモリに読み込んで処理する場合は、PHPの
memory_limit(デフォルト128M程度)が不足する可能性があります。その場合はphpEnv等ではなくカスタムイメージ、または将来的な設定拡張で対応してください。
永続化ストレージ
| パラメータ | 説明 | デフォルト |
|---|---|---|
persistence.enabled |
PVC有効化 | true |
persistence.size |
ストレージサイズ | 1Gi |
persistence.accessMode |
アクセスモード | ReadWriteOnce |
📁 PVCマウント構成(php-fpmコンテナ) php-fpm コンテナには PVC のルートが
/var/wwwにマウントされます。
/var/www/html— 公開フォルダ(nginxが配信するWebルート)/var/www/<任意のフォルダ>— 非公開の格納先(PHPからは読み書きできるが、Web経由では公開されない)nginx コンテナは
htmlサブパスのみをマウントするため、html/以外のディレクトリはWebサーバーのファイルシステムに存在せず、設定ミスでも公開されることはありません。設定ファイル・秘密鍵・アップロード受け皿・ログなど、公開したくないファイルの置き場として利用できます。
Composer設定
| パラメータ | 説明 | デフォルト |
|---|---|---|
composer.enabled |
Composer有効化 | false |
composer.workingDir |
Composer導入先ディレクトリ({workingDir}/vendor/autoload.php) |
"/composer" |
composer.packages |
インストールパッケージ | [] |
composer.useComposerJson |
composer.json使用 | false |
composer.composerJson |
composer.json内容 | "" |
composer.installOptions |
installオプション | "--no-dev --optimize-autoloader" |
composer.additionalPhpExtensions |
追加PHP拡張 | [] |
composer.additionalApkPackages |
追加APKパッケージ | [] |
Python設定(オプション)
PHPからPythonスクリプトを呼び出す用途(データ処理・機械学習ライブラリの利用など)向けに、Python仮想環境(venv)を導入できます。
| パラメータ | 説明 | デフォルト |
|---|---|---|
python.enabled |
Python有効化 | false |
python.venvDir |
venv導入先ディレクトリ({venvDir}/bin/python3) |
"/venv" |
python.packages |
pipでインストールするパッケージ | [] |
python.useRequirementsTxt |
requirements.txt使用 | false |
python.requirementsTxt |
requirements.txtの内容 | "requests>=2.31.0" |
python.installOptions |
pip installオプション | "--no-cache-dir" |
python.additionalApkPackages |
追加APKパッケージ(ネイティブ拡張のビルド用) | [] |
📌 Python導入先について Composerと同様、デフォルトの
/venvはWebルート外かつPVCにマウントされない使い捨て領域です。Pod再起動のたびにクリーンな仮想環境へ再構築されます。PVCに永続化したい場合はpython.venvDirに/var/www配下(例:/var/www/venv)を指定してください。PHPコードからは
exec()等で呼び出します:exec('/venv/bin/python3 /var/www/scripts/process.py 2>&1', $output, $exitCode);⚠️ 注意:
numpyやpillowなど、Alpine(musl)向けの事前ビルド済みwheelが無いパッケージは、Pod起動のたびにソースからビルドされ、Raspberry Pi等のARM環境では起動に数分〜十数分かかる場合があります。ビルドに必要なパッケージ(gcc,musl-dev,python3-dev等)はpython.additionalApkPackagesに指定してください。永続化(venvDirをPVC配下に)すれば再起動のたびの再ビルドを回避できますが、使い捨てによるクリーン環境維持のメリットとのトレードオフになります。
Selenium設定
v8.5.10-b以降、Seleniumは独立したDeployment/Service(<release>-phpfpm-selenium)として起動する(nginx/php-fpmと同一Podのサイドカーではない)。
| パラメータ | 説明 | デフォルト |
|---|---|---|
selenium.enabled |
Selenium有効化 | false |
selenium.replicaCount |
Selenium Deploymentのレプリカ数 | 1 |
selenium.resources |
SeleniumコンテナのCPU/メモリ requests・limits | {} |
selenium.mountHtml |
PVCのhtmlサブパスをSeleniumにもマウントするか(別Podからの同時マウントになるためtrueにする場合はpersistence.accessMode: ReadWriteManyが必要) |
false |
image.selenium.tag |
Seleniumバージョン | 144.0-chromedriver-144.0 |
selenium.enabled: trueのとき、php-fpmコンテナのSELENIUM_URL環境変数は自動的にこのServiceを指す値(http://<release>-phpfpm-selenium:4444/wd/hub)に設定される。phpEnv.SELENIUM_URLを独自に指定していても、この自動注入された値が優先される。
外部データベース設定
| パラメータ | 説明 | デフォルト |
|---|---|---|
externalDatabase.enabled |
外部DB接続有効化 | false |
externalDatabase.host |
DBホスト名 | mariadb-hostname |
externalDatabase.port |
DBポート | 3306 |
externalDatabase.database |
データベース名 | dbname |
externalDatabase.username |
ユーザー名 | user |
externalDatabase.password |
パスワード | pass |
PHP 汎用環境変数(phpEnv)
| パラメータ | 説明 | デフォルト |
|---|---|---|
phpEnv |
PHP-FPM コンテナに渡す任意の環境変数(キーと値のマップ) | {} |
extraEnvFrom |
既存の Secret / ConfigMap をまるごと環境変数として注入(envFrom 配列) |
[] |
extraEnv |
個別の環境変数を追加(生の env エントリ配列。valueFrom/secretKeyRef 可) |
[] |
PHP 側では $_ENV['KEY'] または getenv('KEY') で取得できます。
extraEnvFrom は、別リポジトリの secret-manager チャートが各ネームスペースへ配布する
cred-<名前> Secret(API キー等)をそのまま読み込む用途を想定しています。認証情報の変更は
secret-manager 側を更新するだけで全プロジェクトに反映できます。
extraEnvFrom:
- secretRef:
name: cred-gemini # GEMINI_API_KEY 等が丸ごと環境変数になる
- secretRef:
name: cred-openai
extraEnv:
- name: ANTHROPIC_API_KEY
valueFrom:
secretKeyRef:
name: cred-claude
key: ANTHROPIC_API_KEY
SMTP設定
| パラメータ | 説明 | デフォルト |
|---|---|---|
smtp.enabled |
SMTP機能有効化 | false |
smtp.host |
SMTPサーバーホスト名 | smtp.example.com |
smtp.protocol |
プロトコル(auto/starttls/tls) | auto |
smtp.port |
ポート番号 | 587 |
smtp.auth.enabled |
認証有効化 | true |
smtp.auth.username |
SMTPユーザー名 | smtp-user@example.com |
smtp.auth.password |
SMTPパスワード | "" (Secretで指定) |
smtp.auth.existingSecret |
パスワードを取得する既存 Secret 名(smtp.auth.password と自作 Secret の代わり) |
"" |
smtp.auth.existingSecretKey |
上記 Secret 内のキー名 | password |
smtp.existingMsmtprcSecret |
完成済み msmtprc を持つ既存 Secret 名。指定時はチャートの msmtprc 生成を行わず host/port/auth.* は無視(from のみ使用) |
"" |
smtp.existingMsmtprcSecretKey |
上記 Secret 内の msmtprc キー名 |
msmtprc |
smtp.from |
送信元メールアドレス(固定) | noreply@example.com |
smtp.tls.verify |
TLS証明書検証 | true |
smtp.tls.allowSelfSigned |
自己署名証明書を許可 | false |
secret-manager チャートとの連携(メール設定の一元管理)
secret-manager が cred-gmail などに生成する完成済み msmtprc をそのまま使う場合:
smtp:
enabled: true
from: "no-reply@your-domain.com" # PHP の sendmail_from 用に必要
existingMsmtprcSecret: "cred-gmail" # このSecretの msmtprc キーをそのまま利用
サーバ設定はチャート側 values に残し、パスワードだけ一元管理したい場合:
smtp:
enabled: true
host: "smtp.gmail.com"
port: 587
auth:
enabled: true
username: "no-reply@your-domain.com"
existingSecret: "cred-gmail"
existingSecretKey: "SMTP_PASSWORD"
Ingress設定
| パラメータ | 説明 | デフォルト |
|---|---|---|
ingress.enabled |
Ingress有効化 | false |
ingress.className |
IngressClass名 | "" |
ingress.annotations |
アノテーション | {} |
ingress.hosts |
ホスト設定 | [{host: "example.tld"}] |
ingress.tls |
TLS設定(secretName省略可) |
[] |
📌 Ingressのアップロードサイズ上限
ingress.enabled: trueの場合、nginx.ingress.kubernetes.io/proxy-body-sizeアノテーションがuploadMaxSizeの値で自動的に付与されます(明示的にingress.annotationsへ指定した場合はそちらが優先されます)。Ingressコントローラーがnginx以外の場合は、ingress.annotationsにそのコントローラー向けの上限設定を明示的に指定してください。
📌 hostとTLS secretNameの共有
ingress.hosts[].hostにYAMLアンカー(例:&appDomain)を付けておくと、ingress.tls[].hostsで*appDomainとして再利用でき、ドメイン名を1箇所の変更で揃えられます(デフォルトのvalues.yamlも参照)。 さらにingress.tls[].secretNameは省略可能で、省略した場合はそのエントリの最初のhostから自動生成されます(.を-に置換して-tlsを付与。例:example.tld→example-tld-tls)。別の証明書Secret名を使いたい場合のみsecretNameを明示的に指定してください(指定時はそちらが優先されます)。
Nginx設定
| パラメータ | 説明 | デフォルト |
|---|---|---|
nginx.customConfig.enabled |
カスタム設定有効化 | false |
nginx.customConfig.snippet |
設定スニペット | "" |
nginx.forwardRealIP.enabled |
リアルIP取得有効化 | false |
nginx.forwardRealIP.header |
リアルIP取得ヘッダー | "X-Forwarded-For" |
nginx.forwardRealIP.recursive |
再帰的IP検索 | true |
nginx.forwardRealIP.trustedProxies |
信頼するプロキシネットワーク | ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] |
nginx.forwardRealIP.additionalTrustedProxies |
追加の信頼プロキシ | [] |
📌 sitemap.xml のContent-Type
/sitemap.xml(静的ファイルとして配置した場合)は、常にContent-Type: application/xml; charset=utf-8で配信されます。標準のmime.typesでは.xmlがtext/xml扱いでcharsetが付かず、ブラウザやGoogle Search Consoleなどのツールが正しく解釈できない場合があるための組み込み対応です(設定不要、常時有効)。 なお、PHPで動的に生成する場合はこの設定は適用されません(PHP側でheader('Content-Type: application/xml; charset=utf-8');を指定してください)。
使用例
例1: シンプルなPHPアプリケーション
# values.yaml
replicaCount: 2
persistence:
enabled: true
size: 5Gi
service:
type: LoadBalancer
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"
helm install my-app cafepieters/phpfpm -f values.yaml
例2: Composerパッケージ利用
# values.yaml
composer:
enabled: true
packages:
- "monolog/monolog:^3.0"
- "guzzlehttp/guzzle:^7.0"
- "vlucas/phpdotenv:^5.0"
installOptions: "--no-dev --optimize-autoloader"
persistence:
enabled: true
size: 2Gi
📌 Composerの導入先について ComposerパッケージはデフォルトでWebルート外の
/composerに導入されます(composer.workingDirで変更可能)。vendor/やcomposer.jsonがWeb経由で公開されないため、情報漏洩などのセキュリティリスクを回避できます。
/composerはPVCにマウントされない使い捨て領域です。Pod再起動のたびにクリーンな環境へ再構築されるため、万一不正アクセスでマルウェアを書き込まれても再起動で消え、常にクリーンなComposer環境を維持できます(PVCに永続化したい場合は/var/www/composerなど/var/www配下を指定)。PHPコードからは以下のように読み込みます:
require_once '/composer/vendor/autoload.php';旧バージョン(Webルート直下
/var/www/htmlに導入)と同じ動作に戻す場合はcomposer.workingDir: "/var/www/html"を指定してください。旧バージョンからのアップグレード: 旧Webルートに
composer.json/composer.lock/vendor/が残っている場合、Pod起動時に自動的にworkingDirへ移行されます(v8.5.6-b以降)。
例3: composer.jsonを使用
# values.yaml
composer:
enabled: true
useComposerJson: true
composerJson: |
{
"require": {
"php": ">=8.0",
"slim/slim": "^4.0",
"slim/psr7": "^1.0",
"monolog/monolog": "^3.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
installOptions: "--optimize-autoloader"
replicaCount: 3
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: api.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: api-tls
hosts:
- api.example.com
例4: MySQL接続
# values.yaml
composer:
enabled: true
packages:
- "illuminate/database:^10.0"
externalDatabase:
enabled: true
host: mysql.default.svc.cluster.local
port: 3306
database: myapp
username: appuser
password: "SecurePassword123"
persistence:
enabled: true
size: 3Gi
例5: Selenium統合(スクレイピング)
# values.yaml
composer:
enabled: true
packages:
- "symfony/dom-crawler:^6.0"
- "symfony/css-selector:^6.0"
- "monolog/monolog:^3.0"
selenium:
enabled: true # php-webdriver/webdriverが自動追加。独立Deployment/Serviceとして起動する
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "3Gi"
cpu: "2000m"
persistence:
enabled: true
size: 5Gi
resources:
nginx:
requests: { memory: "64Mi", cpu: "50m" }
limits: { memory: "128Mi" }
phpfpm:
requests: { memory: "256Mi", cpu: "200m" }
limits: { memory: "512Mi", cpu: "1000m" }
PHPコード例:
<?php
require_once '/composer/vendor/autoload.php';
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
// Seleniumに接続(SELENIUM_URLはselenium.enabled:trueのとき自動注入される。
// 別Deploymentになったため localhost では到達できない点に注意)
$driver = RemoteWebDriver::create(
getenv('SELENIUM_URL') ?: 'http://<release>-phpfpm-selenium:4444/wd/hub',
DesiredCapabilities::chrome()
);
$driver->get('https://example.com');
echo $driver->getTitle();
$driver->quit();
例6: カスタムNginx設定
# values.yaml
nginx:
customConfig:
enabled: true
snippet: |
client_max_body_size 100M;
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
}
location /admin {
auth_basic "Administrator Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
persistence:
enabled: true
例7: ベアメタル環境でのリアルIP取得
ベアメタルKubernetesやLoadBalancer経由でのアクセスで、PHPからクライアントの実IPアドレスを取得する設定です。
# values.yaml
service:
type: LoadBalancer
nginx:
forwardRealIP:
# リアルIP取得機能を有効化
enabled: true
# リアルIPを取得するヘッダー(環境に応じて変更)
# - X-Real-IP: シンプルな1段プロキシ
# - X-Forwarded-For: 多段プロキシ対応(推奨)
header: "X-Forwarded-For"
# 再帰的にリアルIPを検索(多段プロキシ環境で推奨)
recursive: true
# 信頼するプロキシのネットワーク範囲
# ベアメタルクラスターのPodネットワークとServiceネットワークを指定
trustedProxies:
- "10.0.0.0/8" # Kubernetesデフォルトのクラスタネットワーク
- "172.16.0.0/12" # Dockerデフォルトネットワーク
- "192.168.0.0/16" # プライベートネットワーク
# 追加で信頼するプロキシ(外部LoadBalancerなど)
additionalTrustedProxies:
- "203.0.113.0/24" # 外部LoadBalancerのIPレンジ(例)
persistence:
enabled: true
この設定により、PHP側で以下の変数から実IPアドレスが取得できます:
$_SERVER['REMOTE_ADDR']- クライアントの実IPアドレス$_SERVER['HTTP_X_REAL_IP']- 同上(互換性用)$_SERVER['HTTP_X_FORWARDED_FOR']- プロキシチェーン全体
検証用PHPコード:
<?php
echo "Real IP: " . $_SERVER['REMOTE_ADDR'] . "\n";
echo "X-Real-IP: " . ($_SERVER['HTTP_X_REAL_IP'] ?? 'not set') . "\n";
echo "X-Forwarded-For: " . ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? 'not set') . "\n";
注意事項:
trustedProxiesには、信頼できるプロキシ(LoadBalancer、Ingressコントローラー)のみを指定してください- 不正なプロキシを信頼すると、IPスプーフィング攻撃のリスクがあります
customConfig.snippetとの併用も可能です(競合しません)
例8: 画像処理アプリケーション(GD拡張)
# values.yaml
composer:
enabled: true
packages:
- "intervention/image:^2.0"
additionalPhpExtensions:
- "gd"
- "exif"
# 注:GDが指定されている場合、依存パッケージ (libpng-dev, libjpeg-turbo-dev, freetype-dev)
# は自動的にインストールされます。手動指定は不要です。
persistence:
enabled: true
size: 10Gi
service:
type: ClusterIP
ingress:
enabled: true
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
hosts:
- host: images.example.com
特記事項: GD拡張を additionalPhpExtensions に指定した場合、以下の依存パッケージが自動的にインストールされます:
libpng-dev- PNG画像処理libjpeg-turbo-dev- JPEG画像処理freetype-dev- フォント処理
additionalApkPackages で明示的に指定する必要はありません。
例9: 本番環境構成(フル機能)
# production-values.yaml
replicaCount: 5
image:
php:
tag: "8.5.2-fpm-alpine3.23"
nginx:
tag: "1.29.4-alpine-perl"
composer:
enabled: true
useComposerJson: true
composerJson: |
{
"require": {
"php": ">=8.1",
"slim/slim": "^4.0",
"monolog/monolog": "^3.0",
"vlucas/phpdotenv": "^5.0",
"firebase/php-jwt": "^6.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
installOptions: "--no-dev --optimize-autoloader --classmap-authoritative"
additionalPhpExtensions:
- "opcache"
externalDatabase:
enabled: true
host: mysql-primary.database.svc.cluster.local
port: 3306
database: production_db
username: prod_user
password: "{{ .Values.dbPassword }}" # 外部から注入
persistence:
enabled: true
size: 20Gi
storageClass: ssd-storage
service:
type: ClusterIP
port: 80
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
hosts:
- host: api.production.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: production-tls
hosts:
- api.production.com
nginx:
customConfig:
enabled: true
snippet: |
client_max_body_size 50M;
fastcgi_read_timeout 300;
# Opcache設定
fastcgi_param PHP_VALUE "
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=0
opcache.validate_timestamps=0
";
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
デプロイ:
# Secretを先に作成
kubectl create secret generic db-password --from-literal=password='SuperSecurePass'
# Helmリポジトリからデプロイ
helm install production-api cafepieters/phpfpm \
-f production-values.yaml \
--set externalDatabase.password=$(kubectl get secret db-password -o jsonpath='{.data.password}' | base64 -d)
例10: APIキーなどの任意環境変数を PHP に渡す(phpEnv)
phpEnv を使うと、DB 接続情報と同様に任意のキーと値を PHP-FPM コンテナの環境変数として渡せます。
# values.yaml
phpEnv:
API_KEY: "your-api-key"
STRIPE_SECRET: "sk_live_xxxxxxxxxxxxxxxx"
REDIS_URL: "redis://localhost:6379"
EXTERNAL_SERVICE_URL: "https://api.example.com"
PHPでの使用例:
<?php
// $_ENV または getenv() で取得
$apiKey = getenv('API_KEY');
$stripe = $_ENV['STRIPE_SECRET'];
$redisUrl = getenv('REDIS_URL');
echo "API Key: " . $apiKey;
確認コマンド:
# 設定された環境変数を確認
kubectl exec -it <pod-name> -c php-fpm -- env | grep -E "API_|STRIPE_|REDIS_"
注意事項:
- 機密性の高い値(APIキー、シークレット等)は
--set phpEnv.KEY=valueでデプロイ時に注入するか、Kubernetes の Secret を別途作成して参照することを推奨します。 phpEnvの値はすべてプレーンテキストとして環境変数に設定されます(externalDatabase の password のみ Secret 経由)。
例11: SMTP設定によるメール送信
PHPアプリケーションからSMTP経由でメール送信を行う設定です。
# values.yaml
smtp:
enabled: true
# Gmail の場合
host: "smtp.gmail.com"
protocol: "starttls" # または "tls" (port 465)
port: 587
# Office365の場合は以下のようにコメント解除
# host: "smtp.office365.com"
# port: 587
# protocol: "starttls"
auth:
enabled: true
username: "your-email@gmail.com"
# password は以下の方法で指定:
# helm install ... --set smtp.auth.password='your-password'
password: ""
# 送信元アドレス(PHPで上書き可能)
from: "noreply@your-domain.com"
tls:
verify: true
allowSelfSigned: false
persistence:
enabled: true
PHPでの使用例:
<?php
// 設定を初期化
require_once '/etc/smtp-config/php-smtp-config.php';
SmtpConfig::init();
// デフォルトの送信元で送信
$to = 'user@example.com';
$subject = 'テストメール';
$body = 'このメールはSMTP経由で送信されています。';
if (mail($to, $subject, $body)) {
echo 'メール送信成功';
} else {
echo 'メール送信失敗';
}
// 異なる送信元で上書き指定
$headers = [
'From: custom@your-domain.com',
'Return-Path: custom@your-domain.com',
];
SmtpConfig::mail($to, $subject, $body, $headers, 'custom@your-domain.com');
例12: Python連携(requirements.txtを使用)
PHPからPythonスクリプトを呼び出してデータ処理を行う設定です。
# values.yaml
python:
enabled: true
useRequirementsTxt: true
requirementsTxt: |
requests>=2.31.0
pandas>=2.0.0
installOptions: "--no-cache-dir"
PHPでの使用例:
<?php
$script = escapeshellarg('/var/www/scripts/process.py');
exec("/venv/bin/python3 {$script} 2>&1", $output, $exitCode);
if ($exitCode === 0) {
echo implode("\n", $output);
} else {
error_log('Python script failed: ' . implode("\n", $output));
}
/var/www/scripts/ のようなPVC上の非公開フォルダにPythonスクリプトを配置すれば、Web経由で公開されずに済みます(詳細はPVCマウント構成の項を参照)。
デプロイ手順:
# 1. Secretにパスワードを設定して展開
helm install my-app cafepieters/phpfpm \
-f values.yaml \
--set smtp.auth.password='your-secure-password'
# または、既存のSecretから取得
helm install my-app cafepieters/phpfpm \
-f values.yaml \
--set smtp.auth.password=$(kubectl get secret email-password -o jsonpath='{.data.password}' | base64 -d)
設定詳細:
-
protocol:
auto: 自動判定(推奨)starttls: SMTP + STARTTLS(ポート587)tls: SSL/TLS(ポート465)
-
from: 固定の送信元アドレス
- PHPコードで
$fromAddressパラメータを指定することで上書き可能 mail($to, $subject, $body, $headers, '-f custom@example.com')で指定
- PHPコードで
-
認証: PLAIN認証に対応(ほとんどのSMTPサーバーで利用可能)
よくある設定:
| プロバイダ | ホスト | ポート | プロトコル |
|---|---|---|---|
| Gmail | smtp.gmail.com | 587 | starttls |
| Office365 | smtp.office365.com | 587 | starttls |
| Sendgrid | smtp.sendgrid.net | 587 | starttls |
| Amazon SES | email-smtp.region.amazonaws.com | 587 | starttls |
アップグレード
# リポジトリを更新
helm repo update cafepieters
# 最新バージョンにアップグレード
helm upgrade my-phpfpm cafepieters/phpfpm -f values.yaml
# 特定のパラメータのみ変更
helm upgrade my-phpfpm cafepieters/phpfpm --set replicaCount=5
# 特定のバージョンにアップグレード
helm upgrade my-phpfpm cafepieters/phpfpm --version 2.1.0
# ロールバック
helm rollback my-phpfpm 1
アンインストール
# チャートの削除
helm uninstall my-phpfpm
# PVCも削除する場合
kubectl delete pvc -l app.kubernetes.io/name=phpfpm
トラブルシューティング
Pod起動が遅い
原因: Composerパッケージのダウンロード時間
対策:
composer:
installOptions: "--no-dev --optimize-autoloader --prefer-dist"
メモリ不足エラー
対策:
resources:
requests:
memory: "512Mi"
limits:
memory: "2Gi"
PHP拡張エラー
対策: 必要なAPKパッケージを追加
composer:
additionalApkPackages:
- "build-base"
- "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 が残る場合は作り直す。
helm upgrade <リリース名> cafepieters/phpfpm --version 8.5.9-d -f values.yaml
kubectl delete pod -n <namespace> -l app.kubernetes.io/name=phpfpm
selenium.enabled: true でコンテナが不安定になるとサイト全体が完全停止する
症状: selenium.enabled: true の構成で、いずれかのコンテナ(php-fpm・Selenium 等)が CrashLoopBackOff になっている間、Selenium を全く使わない静的ページも含めてサイト全体が閲覧不能(502等)になる。kubectl get pod で 2/3 や 1/3 の READY 表示が続く。
原因: v8.5.10-b より前は Selenium が nginx/php-fpm と同一 Pod のサイドカーコンテナだった。Kubernetes は Pod の Ready 条件を「全コンテナが Ready」の AND で判定するため、readinessProbe を持たないコンテナは Running であれば Ready 扱いになる一方、いずれか1つでも CrashLoopBackOff で Waiting/Terminated を繰り返している間は Pod 全体が Ready=false になる。Service は Ready な Pod にしかトラフィックを流さないため、replicaCount: 1(冗長性なし)の構成では 1つのコンテナの不調がサイト全体の完全停止に直結していた。
これに加えて、resources values キーが v8.5.10-a 以前はテンプレート側で参照されておらず、どのコンテナにも requests/limits が反映されない「死んだ設定」だった。全コンテナが QoS Class: BestEffort になり、ノードのメモリ逼迫時に OOM Killer の対象になりやすい状態だった(Chromium を含む Selenium は特にメモリを消費するため、この影響を受けやすい)。
対策: v8.5.10-c 以降にアップグレードする(v8.5.10-b は既存リリースの
アップグレードが spec.selector: ... field is immutable で失敗する不具合を含むため、
v8.5.10-b を経由せず直接 v8.5.10-c 以降を使うこと。詳細は CHANGELOG-8.5.10-c.md)。
- Selenium が独立した Deployment/Service(
<release>-phpfpm-selenium)になり、Selenium 単体の不調がメイン Pod(nginx/php-fpm)の Ready 状態に一切影響しなくなった。 resources.nginx/resources.phpfpm/selenium.resourcesで実際に requests/limits を設定できるようになった。本番運用では設定を強く推奨する(未設定のままだと引き続き BestEffort のままで、OOM Kill のリスクは残る)。
helm upgrade <リリース名> cafepieters/phpfpm --version 8.5.10-c -f values.yaml
アップグレード後に確認すべき点は CHANGELOG-8.5.10-b.md の「互換性(重要・要対応)」を参照(selenium.mountHtml / phpEnv.SELENIUM_URL の扱い)。
データベース接続エラー
確認:
# 環境変数確認
kubectl exec -it <pod-name> -c php-fpm -- env | grep DB_
# 接続テスト
kubectl exec -it <pod-name> -c php-fpm -- mysql -h $DB_HOST -u $DB_USER -p$DB_PASS
ログ確認
# PHP-FPMログ
kubectl logs <pod-name> -c php-fpm
# Nginxログ
kubectl logs <pod-name> -c nginx
# Seleniumログ(有効時。v8.5.10-b以降は別Pod・別Deploymentのため -c 指定は不要)
kubectl logs -l app.kubernetes.io/component=selenium
# リアルタイム監視
kubectl logs -f <pod-name> -c php-fpm
高度な設定
カスタムイメージの使用
image:
php:
registry: my-registry.com
repository: custom-php-fpm
tag: "latest"
水平オートスケーリング
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: phpfpm-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-phpfpm
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
リソース制限の推奨値
| 環境 | CPU Request | CPU Limit | Memory Request | Memory Limit |
|---|---|---|---|---|
| 開発 | 100m | 500m | 256Mi | 512Mi |
| ステージング | 200m | 1000m | 512Mi | 1Gi |
| 本番(小) | 500m | 1000m | 512Mi | 1Gi |
| 本番(大) | 1000m | 2000m | 1Gi | 2Gi |
セキュリティ考慮事項
データベースパスワードの管理
# Secretの作成
kubectl create secret generic db-secret \
--from-literal=password='YourSecurePassword'
# values.yamlで参照
externalDatabase:
password: "{{ .Values.dbPassword }}"
# デプロイ時に注入
helm install my-app . \
--set dbPassword=$(kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d)
NetworkPolicyの適用
# networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: phpfpm-netpol
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: phpfpm
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 80
パフォーマンス最適化
Opcacheの有効化
composer:
additionalPhpExtensions:
- "opcache"
nginx:
customConfig:
enabled: true
snippet: |
fastcgi_param PHP_VALUE "
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
";
Composerの最適化
composer:
installOptions: "--no-dev --optimize-autoloader --classmap-authoritative --apcu-autoloader"
additionalPhpExtensions:
- "apcu"
additionalApkPackages:
- "apcu-dev"
よくある質問(FAQ)
Q: Helmリポジトリを追加するには?
A: 以下のコマンドでリポジトリを追加できます。
helm repo add cafepieters https://git.cafepieters.com/api/packages/helmchart/helm
helm repo update
# 利用可能なバージョンを確認
helm search repo cafepieters/phpfpm --versions
Q: PHPバージョンを変更するには?
A: image.php.tagを変更してください。
image:
php:
tag: "8.3.15-fpm-alpine3.21"
Q: 複数ドメインに対応できますか?
A: Ingressで複数ホストを設定できます。
ingress:
hosts:
- host: domain1.com
paths: [...]
- host: domain2.com
paths: [...]
Q: SeleniumとComposerは同時に使えますか?
A: はい、完全に共存可能です。
Q: 既存のPHPアプリをデプロイするには?
A: PVCにファイルをコピーしてください。
kubectl cp ./my-app <pod-name>:/var/www/html/ -c php-fpm
関連ドキュメント
- Composer使用ガイド
- マイグレーションガイド
- Selenium別Deployment化の設計・経緯は本READMEの「Selenium設定」セクションと
CHANGELOG-8.5.10-b.md(設計)・CHANGELOG-8.5.10-c.md(既存リリースのアップグレードを壊す不具合の修正)を参照(旧: 本リンク先の設計ドキュメントは作成されず実装が先行したため削除)
ライセンス
このHelmチャートはMITライセンスで公開されています。
サポート
- Helmリポジトリ: https://git.cafepieters.com/api/packages/helmchart/helm
- Issue: https://git.cafepieters.com/helmchart/phpfpm/issues
- リポジトリ: https://git.cafepieters.com/helmchart/phpfpm
変更履歴
v2.0.0
- Composer対応追加
- PHP拡張の柔軟な追加機能
- 統合的な依存関係管理
v1.0.0
- 初回リリース
- Nginx + PHP-FPM基本構成
- Selenium統合
- 外部データベース対応