Files
phpfpm/templates/deployment.yaml
T
claude b872dd4bd9
Helm Chart Release / release-chart (push) Successful in 6s
fix: php-fpmをexec php-fpm -Fで起動しCrashLoopBackOff(9000二重bind)/502を修正
setupシェルの子として起動していたためコンテナ再起動時にシェル(PID1)がSIGTERMを子へ転送せず、旧php-fpmが9000を握ったまま残存し次回起動が Address in use で失敗する自己増殖ループに陥っていた。execでphp-fpmをPID1にしシグナルを直接受けさせ、-Fで必ずフォアグラウンド起動にする。
2026-09-06 19:54:49 +09:00

360 lines
16 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "phpfpm.fullname" . }}
labels:
{{- include "phpfpm.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "phpfpm.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "phpfpm.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: nginx
image: "{{ .Values.image.nginx.registry }}/{{ .Values.image.nginx.repository }}:{{ .Values.image.nginx.tag }}"
imagePullPolicy: {{ .Values.image.nginx.pullPolicy }}
ports:
- containerPort: 80
volumeMounts:
- name: app-storage
mountPath: /var/www/html
subPath: html
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
- name: php-fpm
image: "{{ .Values.image.php.registry }}/{{ .Values.image.php.repository }}:{{ .Values.image.php.tag }}"
imagePullPolicy: {{ .Values.image.php.pullPolicy }}
command:
- sh
- -c
- |
echo "Starting PHP-FPM setup process..."
# ========================================
# アップロードサイズ上限(nginxのclient_max_body_sizeと連動)
# ========================================
cat > /usr/local/etc/php/conf.d/99-uploads.ini << 'UPLOADS_INI_EOF'
upload_max_filesize = {{ .Values.uploadMaxSize }}
post_max_size = {{ .Values.uploadMaxSize }}
UPLOADS_INI_EOF
{{- if or .Values.composer.enabled .Values.selenium.enabled .Values.externalDatabase.enabled .Values.python.enabled }}
# ========================================
# APKパッケージのインストール
# ========================================
APK_PACKAGES=""
{{- if or .Values.composer.enabled .Values.selenium.enabled }}
# Composer本体に必要なパッケージ
APK_PACKAGES="$APK_PACKAGES curl zip libzip-dev"
{{- end }}
{{- if .Values.externalDatabase.enabled }}
# MySQL/MariaDB接続に必要なパッケージ
APK_PACKAGES="$APK_PACKAGES mysql-client mysql-dev"
{{- end }}
{{- if .Values.smtp.enabled }}
# SMTP送信に必要なパッケージ
APK_PACKAGES="$APK_PACKAGES msmtp ca-certificates"
{{- end }}
{{- if .Values.python.enabled }}
# Python本体に必要なパッケージ
APK_PACKAGES="$APK_PACKAGES python3 py3-pip"
{{- end }}
{{- if .Values.composer.additionalApkPackages }}
# ユーザー指定の追加APKパッケージ(Composer用)
{{- range .Values.composer.additionalApkPackages }}
APK_PACKAGES="$APK_PACKAGES {{ . }}"
{{- end }}
{{- end }}
{{- if .Values.python.additionalApkPackages }}
# ユーザー指定の追加APKパッケージ(Python用)
{{- range .Values.python.additionalApkPackages }}
APK_PACKAGES="$APK_PACKAGES {{ . }}"
{{- end }}
{{- end }}
# PHP拡張(gd / mbstring)の依存パッケージを自動的に追加
{{- if .Values.composer.additionalPhpExtensions }}
for ext in {{ join " " .Values.composer.additionalPhpExtensions }}; do
if [ "$ext" = "gd" ]; then
echo "GD extension detected, adding required dependencies..."
APK_PACKAGES="$APK_PACKAGES libpng-dev libjpeg-turbo-dev freetype-dev"
fi
if [ "$ext" = "mbstring" ]; then
echo "mbstring extension detected, adding required dependencies..."
APK_PACKAGES="$APK_PACKAGES oniguruma-dev"
fi
done
{{- end }}
if [ -n "$APK_PACKAGES" ]; then
echo "Installing APK packages: $APK_PACKAGES"
apk add --no-cache $APK_PACKAGES
fi
# ========================================
# PHP拡張のインストール
# ========================================
{{- if or .Values.composer.enabled .Values.selenium.enabled }}
echo "Installing PHP zip extension..."
docker-php-ext-install zip
{{- end }}
{{- if .Values.externalDatabase.enabled }}
echo "Installing pdo_mysql extension..."
docker-php-ext-install pdo_mysql
{{- end }}
{{- if .Values.composer.additionalPhpExtensions }}
echo "Installing additional PHP extensions..."
{{- range .Values.composer.additionalPhpExtensions }}
docker-php-ext-install {{ . }}
{{- end }}
{{- end }}
# ========================================
# Composerのインストールと実行
# ========================================
{{- if or .Values.composer.enabled .Values.selenium.enabled }}
echo "Installing Composer..."
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Composer導入先(デフォルトはWebルート外の /composer)
# PHPからは require_once '{{ .Values.composer.workingDir | default "/composer" }}/vendor/autoload.php'; で読み込む
mkdir -p {{ .Values.composer.workingDir | default "/composer" }}
cd {{ .Values.composer.workingDir | default "/composer" }}
{{- if .Values.composer.enabled }}
# ユーザー定義のComposer設定
{{- if .Values.composer.useComposerJson }}
# composer.jsonファイルを使用
echo "Creating composer.json from values..."
cat > composer.json << 'COMPOSER_JSON_EOF'
{{- .Values.composer.composerJson | trim | nindent 14 }}
COMPOSER_JSON_EOF
{{- if .Values.composer.composerLock }}
echo "Creating composer.lock from values..."
cat > composer.lock << 'COMPOSER_LOCK_EOF'
{{- .Values.composer.composerLock | trim | nindent 14 }}
COMPOSER_LOCK_EOF
{{- end }}
echo "Running composer install..."
composer install {{ .Values.composer.installOptions }}
{{- else if .Values.composer.packages }}
# パッケージリストから直接インストール
echo "Installing Composer packages..."
{{- range .Values.composer.packages }}
composer require {{ . }}
{{- end }}
{{- if .Values.composer.installOptions }}
# オプティマイズ実行
composer install {{ .Values.composer.installOptions }}
{{- end }}
{{- else }}
# 旧Webルート(/var/www/html)に composer.json がある場合は移行(v8.5.6-a以前からのアップグレード対応)
if [ ! -f composer.json ] && [ -f /var/www/html/composer.json ]; then
echo "Migrating legacy composer files from /var/www/html to $(pwd)..."
mv /var/www/html/composer.json ./composer.json
if [ -f /var/www/html/composer.lock ]; then mv /var/www/html/composer.lock ./composer.lock; fi
if [ -d /var/www/html/vendor ]; then mv /var/www/html/vendor ./vendor; fi
fi
# composer.jsonが既に存在する場合はそれを使用
if [ -f composer.json ]; then
echo "Found existing composer.json, running composer install..."
composer install {{ .Values.composer.installOptions }}
else
echo "No composer configuration found, skipping package installation"
fi
{{- end }}
{{- end }}
{{- if .Values.selenium.enabled }}
# Selenium用のパッケージインストール(レガシー互換性)
echo "Installing Selenium WebDriver package..."
composer require php-webdriver/webdriver
{{- end }}
{{- end }}
{{- end }}
{{- if .Values.python.enabled }}
{{- $pythonDir := .Values.python.venvDir | default "/venv" }}
# ========================================
# Python仮想環境のセットアップ
# ========================================
echo "Setting up Python virtual environment..."
# Python導入先(デフォルトはWebルート外の /venv、Composerと同様の使い捨て領域)
# PHPからの呼び出し方: exec('{{ $pythonDir }}/bin/python3 /path/to/script.py 2>&1', $output, $exitCode);
python3 -m venv {{ $pythonDir }}
{{- if .Values.python.useRequirementsTxt }}
echo "Creating requirements.txt from values..."
cat > /tmp/requirements.txt << 'REQUIREMENTS_TXT_EOF'
{{- .Values.python.requirementsTxt | trim | nindent 14 }}
REQUIREMENTS_TXT_EOF
echo "Installing Python packages from requirements.txt..."
{{ $pythonDir }}/bin/pip install {{ .Values.python.installOptions }} -r /tmp/requirements.txt
rm -f /tmp/requirements.txt
{{- else if .Values.python.packages }}
echo "Installing Python packages..."
{{- range .Values.python.packages }}
{{ $pythonDir }}/bin/pip install {{ $.Values.python.installOptions }} "{{ . }}"
{{- end }}
{{- else }}
echo "No Python package configuration found, virtual environment created without additional packages"
{{- end }}
{{- end }}
{{- if .Values.smtp.enabled }}
# ========================================
# SMTP設定(msmtp)
# ========================================
echo "Configuring SMTP for mail sending..."
# msmtprc設定ファイルをコピー
# msmtpはPHP-FPMワーカー(www-data等の非root)から実行されるため、
# rootが作成するこのファイルも読み取り可にする必要がある
# (実パスワードはpasswordeval経由で別ファイルから取得するため、
# msmtprc自体に平文パスワードは含まれない)
cp /etc/smtp-config/msmtprc /etc/msmtprc
chmod 644 /etc/msmtprc
# PHP設定をphp.ini.d/に作成
cat > /usr/local/etc/php/conf.d/99-smtp.ini << 'PHP_SMTP_EOF'
; SMTP設定 - msmtp経由でメール送信
sendmail_path = "/usr/bin/msmtp -t"
sendmail_from = "{{ .Values.smtp.from }}"
PHP_SMTP_EOF
echo "SMTP configured:"
echo " Host: {{ .Values.smtp.host }}:{{ .Values.smtp.port }}"
echo " Protocol: {{ .Values.smtp.protocol }}"
echo " From: {{ .Values.smtp.from }}"
{{- end }}
echo "Setup complete, starting 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:
- containerPort: 9000 # PHP-FPMは9000ポートでリッスン
volumeMounts:
# PVCルートを /var/www にマウント
# - /var/www/html のみ nginx が公開(nginx側は html サブパスだけをマウント)
# - それ以外(/var/www/<任意>)は非公開ファイルの格納先として利用可能
# ※ Composer導入先(デフォルト /composer)は意図的にマウントしない:
# Pod再起動のたびにクリーンな環境へ再構築される使い捨て領域とする
- name: app-storage
mountPath: /var/www
{{- if and .Values.composer.enabled .Values.composer.useComposerJson }}
- name: composer-config
mountPath: /tmp/composer-init
{{- end }}
{{- if .Values.smtp.enabled }}
- name: smtp-config
mountPath: /etc/smtp-config
readOnly: true
{{- if .Values.smtp.auth.enabled }}
- name: smtp-secrets
mountPath: /etc/smtp-secrets
readOnly: true
{{- end }}
{{- end }}
env:
{{- if .Values.externalDatabase.enabled }}
- name: DB_HOST
value: {{ .Values.externalDatabase.host | quote }}
- name: DB_PORT
value: {{ .Values.externalDatabase.port | quote }}
- name: DB_NAME
value: {{ .Values.externalDatabase.database | quote }}
- name: DB_USER
value: {{ .Values.externalDatabase.username | quote }}
- name: DB_PASS
valueFrom:
secretKeyRef:
name: {{ include "phpfpm.fullname" . }}-db-secret
key: {{ include "phpfpm.fullname" . }}-db-key
{{- end }}
{{- range $key, $value := .Values.phpEnv }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
{{- if .Values.selenium.enabled }}
- name: selenium
image: "{{ .Values.image.selenium.registry }}/{{ .Values.image.selenium.repository }}:{{ .Values.image.selenium.tag }}"
imagePullPolicy: {{ .Values.image.selenium.pullPolicy }}
ports:
- containerPort: 4444 # Selenium用ポート
volumeMounts:
- name: app-storage
mountPath: /var/www/html
subPath: html
{{- end }}
dnsPolicy: ClusterFirst
dnsConfig:
options:
- name: ndots
value: "1"
volumes:
- name: app-storage
{{- if .Values.persistence.enabled }}
persistentVolumeClaim:
claimName: {{ include "phpfpm.fullname" . }}-pvc
{{- else }}
emptyDir: {}
{{- end }}
- name: nginx-config
configMap:
name: {{ include "phpfpm.fullname" . }}-nginx-config
{{- if and .Values.composer.enabled .Values.composer.useComposerJson }}
- name: composer-config
configMap:
name: {{ include "phpfpm.fullname" . }}-composer-config
{{- end }}
{{- if .Values.smtp.enabled }}
- name: smtp-config
configMap:
name: {{ include "phpfpm.fullname" . }}-smtp-config
items:
- key: msmtprc
path: msmtprc
{{- if .Values.smtp.auth.enabled }}
- name: smtp-secrets
secret:
secretName: {{ include "phpfpm.fullname" . }}-smtp
items:
- key: password
path: password
# 0600だとSecretボリュームの所有者はroot、
# msmtpはPHP-FPMワーカー(www-data等の非root)から実行されるため読めない
# → 全ユーザー読み取り可にする(ボリュームはPod内にしか存在しないため許容範囲)
mode: 0644
{{- end }}
{{- end }}