WordPress with Nginx Helm Chart
このHelmチャートは、Nginx + WordPress (PHP-FPM) 構成をKubernetes上にデプロイします。 bitnami/wordpressのように、デプロイ後すぐに使用可能な状態で起動します。
主な機能
✅ 自動インストール: 初回デプロイ時にWordPressを自動セットアップ
✅ 自動パスワード生成: 管理者パスワードを指定しない場合は自動生成
✅ アップデート対応: 既存のDBがある場合は初期化をスキップ
✅ ads.txt対応: values.yamlから ads.txt を配置可能
✅ セキュアな構成: WordPress本体は使い捨て、wp-contentのみ永続化
✅ 本番環境対応: セキュアなSecret管理、HA構成
アーキテクチャとセキュリティ
ストレージ構成
/var/www/html/ ← emptyDir(使い捨て、Pod再起動で消える)
├── index.php ← WordPress本体ファイル
├── wp-admin/ ← 管理画面(使い捨て)
├── wp-includes/ ← WordPressコア(使い捨て)
├── wp-config.php ← Secretから毎回生成
├── ads.txt ← values.yamlから生成
└── wp-content/ ← シンボリックリンク → PVC
/var/www/html-persistent/ ← PVC(永続化)
└── wp-content/ ← テーマ、プラグイン、アップロード
├── themes/
├── plugins/
└── uploads/
セキュリティ上の利点
- WordPress本体の改ざん防止: 毎回クリーンな状態から起動
- 脆弱性への迅速な対応: イメージ更新のみでコアファイル更新
- 設定の一元管理: wp-config.phpはvalues.yamlとSecretから生成
- ユーザーデータの保護: wp-contentのみ永続化で管理が容易
アーキテクチャ
- Nginx: リバースプロキシおよび静的ファイル配信
- WordPress (PHP-FPM): WordPressアプリケーション実行
- 共有ボリューム: Nginx と WordPress 間でファイルを共有
前提条件
- Kubernetes 1.19+
- Helm 3.0+
- PersistentVolume プロビジョナー(永続化を有効にする場合)
- MySQL/MariaDB データベース(別途デプロイが必要)
インストール方法
1. チャートの準備
# チャートディレクトリの作成
mkdir -p wordpress-nginx/templates
# 必要なファイルをコピー
# Chart.yaml, values.yaml, templates/*
2. MySQLのデプロイ(必要な場合)
# Helm を使用して MySQL をデプロイ
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install mysql bitnami/mysql \
--set auth.rootPassword=rootpassword \
--set auth.database=wordpress \
--set auth.username=wordpress \
--set auth.password=changeme
3. WordPressのデプロイ
# デフォルト値でインストール
helm install my-wordpress ./wordpress-nginx
# カスタム値でインストール
helm install my-wordpress ./wordpress-nginx \
--set wordpress.dbPassword=your-secure-password \
--set service.type=LoadBalancer
# values.yaml を使用
helm install my-wordpress ./wordpress-nginx -f custom-values.yaml
設定パラメータ
基本設定
| パラメータ | 説明 | デフォルト値 |
|---|---|---|
replicaCount |
レプリカ数 | 2 |
image.nginx.registry |
Nginxイメージレジストリ | docker.io |
image.nginx.repository |
Nginxイメージリポジトリ | nginx |
image.nginx.tag |
Nginxイメージタグ | 1.29.3-alpine-perl |
image.wordpress.registry |
WordPressイメージレジストリ | docker.io |
image.wordpress.repository |
WordPressイメージリポジトリ | wordpress |
image.wordpress.tag |
WordPressイメージタグ | 6.8.3-php8.4-fpm-alpine |
WordPress設定
| パラメータ | 説明 | デフォルト値 |
|---|---|---|
wordpress.dbHost |
データベースホスト | mysql-service |
wordpress.dbName |
データベース名 | wordpress |
wordpress.dbUser |
データベースユーザー | wordpress |
wordpress.dbPassword |
データベースパスワード | changeme |
wordpress.tablePrefix |
テーブルプレフィックス | wp_ |
wordpress.siteTitle |
サイトタイトル | My WordPress Site |
wordpress.adminUser |
管理者ユーザー名 | admin |
wordpress.adminPassword |
管理者パスワード(空=自動生成) | "" |
wordpress.adminEmail |
管理者メール | admin@example.com |
wordpress.adsTxt.enabled |
ads.txtを有効化 | false |
wordpress.adsTxt.content |
ads.txtの内容 | "" |
SMTP設定(メール送信)
WordPressコンテナ内にはメール送信を行うMTA(sendmail相当のプログラム)が存在しないため、デフォルトのままではパスワードリセットメールやコメント通知などの wp_mail() によるメール送信ができません。外部SMTPリレー(Gmail、SendGrid、自前のメールサーバー等)経由で送信する場合は以下を設定してください。
| パラメータ | 説明 | デフォルト値 |
|---|---|---|
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.from |
送信元メールアドレス(固定) | noreply@example.com |
smtp.tls.verify |
TLS証明書検証 | true |
smtp.tls.allowSelfSigned |
自己署名証明書を許可 | false |
有効化すると、WordPressコンテナ起動時に msmtp を導入し、PHPの sendmail_path を msmtp 経由に設定します。wp_mail() はコード変更なしにそのままSMTP経由で送信されるようになります。
設定例:
smtp:
enabled: true
host: "smtp.gmail.com"
protocol: "starttls"
port: 587
auth:
enabled: true
username: "your-email@gmail.com"
password: "" # --set smtp.auth.password='your-app-password' で指定
from: "noreply@your-domain.com"
helm upgrade --install my-wordpress . \
-f values.yaml \
--set smtp.auth.password='your-app-password'
よくある設定:
| プロバイダ | ホスト | ポート | プロトコル |
|---|---|---|---|
| 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 |
注意: smtp.enabled: true の場合、msmtpのインストール(apk add)のためWordPressコンテナはrootとして起動し、セットアップ完了後に php-fpm へexecします。PHP-FPMのワーカープロセス自体はイメージ標準の設定により引き続き www-data(非root)で動作するため、実際のPHPコード実行権限は変わりません。smtp.enabled: false(デフォルト)の場合は従来どおり非rootユーザー(uid82)でコンテナが起動します。
Service設定
| パラメータ | 説明 | デフォルト値 |
|---|---|---|
service.type |
Serviceタイプ | LoadBalancer |
service.port |
Serviceポート | 80 |
service.externalTrafficPolicy |
外部トラフィックポリシー(LoadBalancer/NodePortのみ有効) |
Local |
実クライアントIPの取得について(ベアメタル/MetalLB構成):
このチャートのデフォルト構成(ルーター → MetalLB等のLoadBalancer Service → Pod)では、service.externalTrafficPolicy が Cluster(Kubernetesのデフォルト)だと、kube-proxyが送信元IPをノードの内部IPにSNATしてしまい、Nginx/WordPressが実際の訪問者IPを取得できません。本チャートではデフォルトで externalTrafficPolicy: Local を設定し、SNATを回避することで $remote_addr に実IPがそのまま渡るようにしています。
- 注意:
Localにすると、リクエストを受けたノードにPodが存在しない場合はそのノードでの接続が失敗します。MetalLB L2モードなど、Podが存在するノードにのみトラフィックが向く構成と組み合わせて使用してください(複数ノードにPodを分散させることを推奨)。 service.type: ClusterIPの場合、externalTrafficPolicyは無視されます(Kubernetes仕様上ClusterIPには適用不可のため)。- kube-vip(ARP方式)でServiceに直接VIPを払い出している場合は要注意: kube-vipはL2 ARP応答に加えて自前でTCP接続をプロキシする実装のため、
externalTrafficPolicy: Localを設定してもkube-vipの時点で送信元IPが失われ、効果がありません(type: ClusterIP+ kube-vipアノテーションによる手動VIP付与構成は特に該当します)。この場合は下記「kube-vip等でグローバルIP/VIPが限られている環境での実IP取得」の共有Ingress Controller構成を参照してください。
nginx.forwardRealIP は、Ingress ControllerやCDN(Cloudflareなど)、外部LBのように X-Forwarded-For ヘッダーを付与するリバースプロキシを前段に置く場合にのみ有効にしてください。本チャートのデフォルト構成(Service直下にPodがぶら下がる構成)ではX-Forwarded-Forを付与する層が存在しないため、forwardRealIP を有効にしても効果はなく、service.externalTrafficPolicy: Local のみで実IPが取得できます。
Ingress設定
| パラメータ | 説明 | デフォルト値 |
|---|---|---|
ingress.enabled |
Ingressを有効化 | false |
ingress.className |
IngressClass名 | nginx |
ingress.annotations |
Ingressアノテーション | {} |
ingress.hostname |
プライマリホスト名 | wordpress.example.com |
ingress.path |
パス | / |
ingress.pathType |
パスタイプ | Prefix |
ingress.tls |
TLS有効化(自動設定) | false |
ingress.extraHosts |
追加ホスト設定 | [] |
ingress.extraTls |
追加TLS設定 | [] |
TLS自動設定: tls: true にすると、hostname を使用して自動的に以下が設定されます:
- hosts:
[hostname] - secretName:
{hostname-with-dash}-tls(例:example-com-tls)
永続化設定
| パラメータ | 説明 | デフォルト値 |
|---|---|---|
persistence.enabled |
永続化を有効化(wp-contentのみ) | true |
persistence.storageClass |
StorageClass | "" |
persistence.accessMode |
アクセスモード | ReadWriteOnce |
persistence.size |
ストレージサイズ(wp-content用) | 10Gi |
注意: WordPress本体ファイル(wp-admin、wp-includesなど)はemptyDirに配置され、Pod再起動時に破棄されます。ユーザーデータ(wp-content)のみがPVCに永続化されます。
使用例
基本的なインストール(パスワード自動生成)
helm install my-wordpress ./wordpress-nginx \
--set wordpress.dbPassword=SecurePassword123 \
--set wordpress.siteTitle="My Blog" \
--set wordpress.adminEmail=admin@example.com
# パスワードの取得
kubectl get secret my-wordpress-wordpress-nginx-secret \
-o jsonpath='{.data.admin-password}' | base64 -d
echo
パスワードを指定してインストール
helm install my-wordpress ./wordpress-nginx \
--set wordpress.dbPassword=SecurePassword123 \
--set wordpress.adminPassword=AdminPass456 \
--set wordpress.adminUser=myadmin
ads.txt を配置
values.yamlで設定:
wordpress:
adsTxt:
enabled: true
content: |
google.com, pub-1234567890, DIRECT, f08c47fec0942fa0
adserver.com, 9876, RESELLER
またはコマンドラインで:
helm install my-wordpress ./wordpress-nginx \
--set wordpress.dbPassword=SecurePassword123 \
--set wordpress.adsTxt.enabled=true \
--set-string wordpress.adsTxt.content="google.com, pub-1234567890, DIRECT, f08c47fec0942fa0"
ads.txtはConfigMapとして管理され、Nginxコンテナに直接マウントされます。
LoadBalancerでの公開
helm install my-wordpress ./wordpress-nginx \
--set service.type=LoadBalancer \
--set wordpress.dbPassword=SecurePassword123
Ingressでの公開
基本的なIngress(HTTPのみ)
helm install my-wordpress ./wordpress-nginx \
--set ingress.enabled=true \
--set ingress.hostname=wordpress.example.com \
--set service.type=ClusterIP
TLS有効化(自動設定)
helm install my-wordpress ./wordpress-nginx \
--set ingress.enabled=true \
--set ingress.hostname=wordpress.example.com \
--set ingress.tls=true \
--set service.type=ClusterIP
これで自動的に以下が設定されます:
- TLS Secret名:
wordpress-example-com-tls - TLS対象ホスト:
wordpress.example.com
cert-managerと組み合わせ
helm install my-wordpress ./wordpress-nginx \
--set ingress.enabled=true \
--set ingress.hostname=blog.example.com \
--set ingress.tls=true \
--set ingress.annotations."cert-manager\.io/cluster-issuer"=letsencrypt-issuer \
--set ingress.annotations."acme\.cert-manager\.io/http01-ingress-class"=nginx
または values.yaml で:
ingress:
enabled: true
hostname: blog.example.com
tls: true
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-issuer"
acme.cert-manager.io/http01-ingress-class: "nginx"
nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
複数ホスト設定
helm install my-wordpress ./wordpress-nginx -f - <<EOF
ingress:
enabled: true
hostname: www.example.com
tls: true
extraHosts:
- name: blog.example.com
path: /
- name: news.example.com
path: /
extraTls:
- hosts:
- blog.example.com
- news.example.com
secretName: multi-domain-tls
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-issuer"
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
EOF
kube-vip等でグローバルIP/VIPが限られている環境での実IP取得(共有Ingress Controller構成)
kube-vip(ARP方式)でVIPを払い出す構成の場合、各Serviceを個別に type: LoadBalancer(またはClusterIP+kube-vipアノテーションの手動払い出し)にすると、kube-vipが自前でTCP接続をプロキシする関係上、Nginxに到達する時点で送信元IPが失われ、nginx.forwardRealIP を有効にしても実IPは取得できません(kube-vipはL2 ARP+自前プロキシであり、X-Forwarded-Forを付与するリバースプロキシではないため)。
これを避けつつ、限られたIP/VIPプールの消費も抑えたい場合は、Ingress Controllerを1つだけLoadBalancer公開し、各WordPress/PHPFPMリリースはClusterIPのままIngress経由でぶら下げる構成を推奨します。外部消費IPはIngress Controllerの1個のみに集約され、Ingress ControllerがX-Forwarded-Forを正しく付与するため、本チャート既存の nginx.forwardRealIP がそのまま機能します。
Ingress Controllerの選択: 既にCilium CNIを使用している場合(cilium-envoy Podが動作している環境)は、追加コンポーネントなしでCilium組み込みのIngress Controllerを有効化できます。
# Cilium組み込みIngress Controllerが有効か確認
kubectl get cm -n kube-system cilium-config -o yaml | grep -i ingress-controller-enabled
# 未設定の場合、Ciliumをupgradeして有効化(既存のCilium Helm valuesに追加)
helm upgrade cilium cilium/cilium -n kube-system --reuse-values \
--set ingressController.enabled=true \
--set ingressController.loadbalancerMode=shared \
--set ingressController.service.type=LoadBalancer \
--set ingressController.service.externalTrafficPolicy=Local
Cilium Ingressを使わない場合は ingress-nginx 等の一般的なIngress Controllerでも構いません。その場合もController自体のServiceに externalTrafficPolicy: Local を設定し、kube-vipでVIPを1つだけ払い出してください。
各リリースの移行例(mikoto-wordpress-nginx の場合):
helm upgrade mikoto . -n website \
--set service.type=ClusterIP \
--set ingress.enabled=true \
--set ingress.className=cilium \
--set ingress.hostname=mikoto.example.com \
--set nginx.forwardRealIP.enabled=true
移行後は、これまで手動で付与していた kube-vip.io/loadbalancerIPs 等のServiceアノテーションは不要になります(Helm管理外のアノテーションのため、kubectl annotate <svc> kube-vip.io/loadbalancerIPs- kube-vip.io/vipHost- 等で削除してください)。DNS(またはルーターの名前解決)側で各ホスト名をIngress ControllerのVIP 1個に向ける設定も必要です。
nginx.forwardRealIP.trustedProxies のデフォルトには 10.0.0.0/8 が含まれているため、Cilium/Flannel等のPod CIDRがこの範囲内であれば追加設定は不要です。異なるPod CIDRを使用している場合は values.yaml で調整してください。
リソース制限のカスタマイズ
helm install my-wordpress ./wordpress-nginx \
--set resources.nginx.limits.memory=1Gi \
--set resources.wordpress.limits.memory=2Gi
初期化の動作
新規インストール時
- WordPress本体ファイルをemptyDirにコピー(使い捨て領域)
- wp-contentディレクトリをPVCに作成(永続化領域)
- シンボリックリンクで wp-content を接続
- wp-config.phpをSecretから生成
- データベース接続を確認
- テーブルが存在しない場合:
- WP-CLIを使用してWordPressをインストール
- 管理者アカウントを作成
- パスワードが未指定の場合は16文字のランダム生成
- ads.txtが有効な場合は配置(使い捨て領域)
アップデート/再起動時
- WordPress本体ファイルを新しくコピー(常にクリーン)
- 既存のwp-content(PVC)をシンボリックリンクで接続
- wp-config.phpを再生成
- データベーステーブルの存在を確認
- テーブルが存在する場合:
- 初期化処理をスキップ
- コアバージョンの更新確認
- 必要に応じてデータベーススキーマをアップデート
- 既存データ(wp-content)を保持したまま起動
セキュリティ更新の適用方法
# イメージタグを更新して再デプロイするだけ
helm upgrade my-wordpress ./wordpress-nginx \
--set image.wordpress.tag=6.8.4-php8.4-fpm-alpine
# Pod再起動で自動的にクリーンなWordPress本体に置き換わる
kubectl rollout restart deployment/my-wordpress-wordpress-nginx
ファイル構成の確認
# WordPress本体(emptyDir - 使い捨て)
kubectl exec -it <pod-name> -c wordpress -- ls -la /var/www/html/
# wp-content(PVC - 永続化)
kubectl exec -it <pod-name> -c wordpress -- ls -la /var/www/html-persistent/wp-content/
# シンボリックリンクの確認
kubectl exec -it <pod-name> -c wordpress -- ls -la /var/www/html/ | grep wp-content
管理者パスワードの確認方法
# Secretから取得
kubectl get secret <release-name>-wordpress-nginx-secret \
-o jsonpath='{.data.admin-password}' | base64 -d
# または initContainer のログから確認(初回のみ)
kubectl logs <pod-name> -c wordpress-init
# 設定を変更してアップグレード
helm upgrade my-wordpress ./wordpress-nginx \
--set wordpress.dbPassword=NewPassword
# values.yamlを使用してアップグレード
helm upgrade my-wordpress ./wordpress-nginx -f custom-values.yaml
アンインストール
helm uninstall my-wordpress
トラブルシューティング
初期化ログの確認
# initContainer のログを確認
kubectl logs <pod-name> -c wordpress-init
# メインコンテナのログ
kubectl logs <pod-name> -c nginx
kubectl logs <pod-name> -c wordpress
管理者パスワードの再確認
# Secretから取得
kubectl get secret my-wordpress-wordpress-nginx-secret \
-o jsonpath='{.data.admin-password}' | base64 -d && echo
データベース接続の確認
# WordPress コンテナで WP-CLI を実行
kubectl exec -it <pod-name> -c wordpress -- wp db check
kubectl exec -it <pod-name> -c wordpress -- wp db tables
再初期化が必要な場合
# wp-contentのみを保持して再インストール
# (データベースをクリアすれば再初期化される)
kubectl exec -it <pod-name> -c wordpress -- wp db reset --yes
# 完全なクリーンインストール(PVCごと削除)
kubectl delete pvc <pvc-name>
helm uninstall my-wordpress
helm install my-wordpress ./wordpress-nginx
ストレージの確認
# PVCの確認(wp-contentのみ)
kubectl get pvc
kubectl exec -it <pod-name> -c wordpress -- du -sh /var/www/html-persistent/wp-content/
# emptyDirの使用量確認
kubectl exec -it <pod-name> -c wordpress -- du -sh /var/www/html/
ads.txt の確認
# Pod内でファイルを確認
kubectl exec -it <pod-name> -c nginx -- cat /var/www/html/ads.txt
# ブラウザまたはcurlでアクセス
curl http://your-site.com/ads.txt
セキュリティ考慮事項
本番環境では以下を必ず実施してください:
- データベースパスワードの保護: Kubernetes Secretを使用
- HTTPS の有効化: cert-manager等でTLS証明書を設定
- リソース制限の設定: 適切なresources設定
- 定期的なバックアップ: wp-content(PVC)とデータベースのバックアップ
- セキュリティアップデート:
- WordPress本体: イメージタグ更新 → Pod再起動で自動適用
- プラグイン/テーマ: WordPress管理画面から更新
- wp-config.phpのセキュリティ: Secretに保存され、Pod再起動時に再生成
- 読み取り専用ファイルシステム: WordPress本体は毎回クリーン、改ざん不可
この構成のセキュリティメリット
- ✅ WordPress本体への不正な変更を防止(Pod再起動で復元)
- ✅ wp-config.phpへの直接アクセス不可(Secretから生成)
- ✅ 脆弱性対応が容易(イメージ更新のみ)
- ✅ ユーザーデータのみを管理(wp-contentのみバックアップ)
- ✅ 設定の一元管理(values.yaml + Secret)