# 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/
```

### セキュリティ上の利点

1. **WordPress本体の改ざん防止**: 毎回クリーンな状態から起動
2. **脆弱性への迅速な対応**: イメージ更新のみでコアファイル更新
3. **設定の一元管理**: wp-config.phpはvalues.yamlとSecretから生成
4. **ユーザーデータの保護**: wp-contentのみ永続化で管理が容易

## アーキテクチャ

- **Nginx**: リバースプロキシおよび静的ファイル配信
- **WordPress (PHP-FPM)**: WordPressアプリケーション実行
- **共有ボリューム**: Nginx と WordPress 間でファイルを共有

## 前提条件

- Kubernetes 1.19+
- Helm 3.0+
- PersistentVolume プロビジョナー（永続化を有効にする場合）
- MySQL/MariaDB データベース（別途デプロイが必要）

## インストール方法

### 1. チャートの準備

```bash
# チャートディレクトリの作成
mkdir -p wordpress-nginx/templates

# 必要なファイルをコピー
# Chart.yaml, values.yaml, templates/*
```

### 2. MySQLのデプロイ（必要な場合）

```bash
# 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のデプロイ

```bash
# デフォルト値でインストール
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経由で送信されるようになります。

**設定例**:

```yaml
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"
```

```bash
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に永続化されます。

## 使用例

### 基本的なインストール（パスワード自動生成）

```bash
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
```

### パスワードを指定してインストール

```bash
helm install my-wordpress ./wordpress-nginx \
  --set wordpress.dbPassword=SecurePassword123 \
  --set wordpress.adminPassword=AdminPass456 \
  --set wordpress.adminUser=myadmin
```

### ads.txt を配置

values.yamlで設定：

```yaml
wordpress:
  adsTxt:
    enabled: true
    content: |
      google.com, pub-1234567890, DIRECT, f08c47fec0942fa0
      adserver.com, 9876, RESELLER
```

またはコマンドラインで：

```bash
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での公開

```bash
helm install my-wordpress ./wordpress-nginx \
  --set service.type=LoadBalancer \
  --set wordpress.dbPassword=SecurePassword123
```

### Ingressでの公開

#### 基本的なIngress（HTTPのみ）

```bash
helm install my-wordpress ./wordpress-nginx \
  --set ingress.enabled=true \
  --set ingress.hostname=wordpress.example.com \
  --set service.type=ClusterIP
```

#### TLS有効化（自動設定）

```bash
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と組み合わせ

```bash
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 で：

```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"
```

#### 複数ホスト設定

```bash
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を有効化できます。

```bash
# 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` の場合）:

```bash
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 で調整してください。

### リソース制限のカスタマイズ

```bash
helm install my-wordpress ./wordpress-nginx \
  --set resources.nginx.limits.memory=1Gi \
  --set resources.wordpress.limits.memory=2Gi
```

## 初期化の動作

### 新規インストール時
1. WordPress本体ファイルをemptyDirにコピー（使い捨て領域）
2. wp-contentディレクトリをPVCに作成（永続化領域）
3. シンボリックリンクで wp-content を接続
4. wp-config.phpをSecretから生成
5. データベース接続を確認
6. テーブルが存在しない場合：
   - WP-CLIを使用してWordPressをインストール
   - 管理者アカウントを作成
   - パスワードが未指定の場合は16文字のランダム生成
7. ads.txtが有効な場合は配置（使い捨て領域）

### アップデート/再起動時
1. WordPress本体ファイルを新しくコピー（常にクリーン）
2. 既存のwp-content（PVC）をシンボリックリンクで接続
3. wp-config.phpを再生成
4. データベーステーブルの存在を確認
5. テーブルが存在する場合：
   - 初期化処理をスキップ
   - コアバージョンの更新確認
   - 必要に応じてデータベーススキーマをアップデート
6. 既存データ（wp-content）を保持したまま起動

### セキュリティ更新の適用方法

```bash
# イメージタグを更新して再デプロイするだけ
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
```

### ファイル構成の確認

```bash
# 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
```

### 管理者パスワードの確認方法

```bash
# Secretから取得
kubectl get secret <release-name>-wordpress-nginx-secret \
  -o jsonpath='{.data.admin-password}' | base64 -d

# または initContainer のログから確認（初回のみ）
kubectl logs <pod-name> -c wordpress-init
```

```bash
# 設定を変更してアップグレード
helm upgrade my-wordpress ./wordpress-nginx \
  --set wordpress.dbPassword=NewPassword

# values.yamlを使用してアップグレード
helm upgrade my-wordpress ./wordpress-nginx -f custom-values.yaml
```

## アンインストール

```bash
helm uninstall my-wordpress
```

## トラブルシューティング

### 初期化ログの確認

```bash
# initContainer のログを確認
kubectl logs <pod-name> -c wordpress-init

# メインコンテナのログ
kubectl logs <pod-name> -c nginx
kubectl logs <pod-name> -c wordpress
```

### 管理者パスワードの再確認

```bash
# Secretから取得
kubectl get secret my-wordpress-wordpress-nginx-secret \
  -o jsonpath='{.data.admin-password}' | base64 -d && echo
```

### データベース接続の確認

```bash
# WordPress コンテナで WP-CLI を実行
kubectl exec -it <pod-name> -c wordpress -- wp db check
kubectl exec -it <pod-name> -c wordpress -- wp db tables
```

### 再初期化が必要な場合

```bash
# 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
```

### ストレージの確認

```bash
# 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 の確認

```bash
# Pod内でファイルを確認
kubectl exec -it <pod-name> -c nginx -- cat /var/www/html/ads.txt

# ブラウザまたはcurlでアクセス
curl http://your-site.com/ads.txt
```

## セキュリティ考慮事項

本番環境では以下を必ず実施してください:

1. **データベースパスワードの保護**: Kubernetes Secretを使用
2. **HTTPS の有効化**: cert-manager等でTLS証明書を設定
3. **リソース制限の設定**: 適切なresources設定
4. **定期的なバックアップ**: wp-content（PVC）とデータベースのバックアップ
5. **セキュリティアップデート**: 
   - WordPress本体: イメージタグ更新 → Pod再起動で自動適用
   - プラグイン/テーマ: WordPress管理画面から更新
6. **wp-config.phpのセキュリティ**: Secretに保存され、Pod再起動時に再生成
7. **読み取り専用ファイルシステム**: WordPress本体は毎回クリーン、改ざん不可

### この構成のセキュリティメリット

- ✅ WordPress本体への不正な変更を防止（Pod再起動で復元）
- ✅ wp-config.phpへの直接アクセス不可（Secretから生成）
- ✅ 脆弱性対応が容易（イメージ更新のみ）
- ✅ ユーザーデータのみを管理（wp-contentのみバックアップ）
- ✅ 設定の一元管理（values.yaml + Secret）
