WordPress を EC2 / CloudFront / S3 / SES を活用して構築する ①

WordPress サイトを AWS ネイティブなクラウド環境で構築してみたまとめです。

# やってみたこと

![AWS Architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869416/f05447be-60f1-5270-75e4-921e6c2d8c73.png)

* WordPress の閲覧者はインターネットから CloudFront を通して `https://example.com` のような URL  でアクセスします。
* CloudFront はアクセスされたパスに応じて、通常は EC2 上で動作している WordPress をオリジンに、`/wp-content/uploads` 以下にアクセスがあった場合は S3 をオリジンとします。これは WordPress でホストされているサイト上に容量の大きな静的ファイル (画像・動画など) が存在することを想定しています。
* 新規コメントや自動アップデートなどのメール通知は、SMTP を介して SES で送信されます。
* ドメインは Route 53 のホストゾーンに設定します。

# EC2 (WordPress)

## インスタンス

まずは、WordPress を動作させるために EC2 を起動します。それほどアクセスの多いウェブサイトでなければ、最小限のインスタンスタイプでも問題ないと思います。
今ですと、AMD CPU の `t3a.nano` などが安くて良いのではないでしょうか。

WordPress の構築方法は色々なところに書かれていますので割愛しますが、Amazon Linux 2 上に Apache や MySQL をインストールして構築しても良いですし、Bitnami が提供している [WordPress スタック](https://aws.amazon.com/marketplace/pp/Bitnami-WordPress-Certified-by-Bitnami-and-Automat/B00NN8Y43U)や LAMP スタックなどを利用しても良いと思います。Bitnami のスタックを利用する場合は、Apache や MySQL のバージョンアップをしたい際に、スタックの作り直しになるのでその点は注意が必要です。

EC2 上に構築した WordPress は、外部からアクセスできる必要がありますので、Elastic IP を付与し、必要最小限のポートをセキュリティグループのインバウンドルールで解放してください (HTTP/HTTPS/SSHなど)。

## ドメイン・証明書

また、Route 53 にドメインを登録します。
`https://example.com` というドメインでサイトを運用したい場合、このドメインは CloudFront に設定しますので、EC2 には `https://origin.example.com` など別のドメインでアクセスできるようにします。
サブドメインを origin として、さきほど Elastic IP で取得した IP アドレスを A レコードに設定します。

EC2 で起動している WordPress は HTTP / HTTPS どちらで運用しても構いませんが、HTTPS とする場合は証明書の発行が必要です。
Let's Encrypt を EC2 上にインストールして、自動更新設定をしておくのが良いでしょう。

Let's Encrypt の `certbot` には DNS 認証を自動化してくれるプラグインがあり、Route 53 にも対応していますので、[こちらのサイトの手順](https://certbot.eff.org/docs/using.html#dns-plugins)に従ってインストールすれば、EC2 上で証明書の発行・更新時に自動的に Route 53 のレコードを更新して DNS 認証をしてくれるので便利です。

# CloudFront

## ディストリビューション

WordPress の CDN として CloudFront のディストリビューションを新規作成します。
初期状態では `hoge.cloudfront.net` の URL が発行されますが、今回は `https://example.com` でアクセスしたいので、CNAME として登録します。

初回は、ACM で `example.com` の SSL 証明書を作成する必要がありますが、ドメインを Route 53 で管理していれば特に難しい手順はないかと思います。

Price Class　は CloudFront のエッジが配置される地域の設定で、サイトの主なターゲットに合わせて選択します。

ディストリビューションが作成できたら、Origin と Behavior を設定します。

* Origin ・・・ CloudFront がデータを読みにいく場所。今回は EC2 と S3
* Behavior ・・・ ユーザーがアクセスしてきた際のパスに応じた Origin やキャッシュの選択

## オリジン

この記事の流れではまだ、S3 は作成していませんが、最初にしめした構成図の通りに構築する場合、Origin には S3 バケットとカスタムオリジンとして `origin.example.com` を追加します。

WordPress が動作する `origin.example.com` の設定では、`Origin Response Timeout` の設定を最大値の 60 に設定します。管理画面で、画像や動画などのファイルをアップロードする際には、サーバー側からのレスポンスに時間がかかることがありますが、その際にこのタイムアウトの時間が短いと、アップロードの途中で接続が切れてしまうことがあります。

また、`https://origin.example.com` のような、オリジンサイトにつけたドメイン名でユーザーにアクセスされたくない場合には、`Origin Custom Headers` を利用します。ここで、例えば `X-CF-ORIGIN` に `hogehoge` と設定すると、CloudFront から WordPress のコンテンツにアクセスする際に、このヘッダーが付与されます。
逆に言えば、このヘッダーが付いていないアクセスは CloudFront を通っていない直アクセスということになりますので、`.htaccess` で以下のようなリダイレクトを記載することで、CloudFront からのオリジンアクセスのみを許可することができます。

```text:.htaccess
<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteBase /
	RewriteCond %{HTTP_HOST}  ^origin.example.com$
	RewriteCond %{HTTP:X-CF-ORIGIN} !^hogehoge$
	RewriteRule (.*) https://example.com/$1 [R=301,L]
</IfModule>
```

## ポリシー

Behavior の設定の前に、ポリシーをいくつか作成します。

CloudFront のポリシーは、キャッシュの有無や TTL のパターンを設定できるテンプレートのようなものです。
キャッシュポリシーとオリジンポリシーに基づく CloudFront の設定方法は比較的最近に利用可能になったもので、Google 検索などしても旧来の設定方法の記事が多く出てくるかと思います。
今回は、キャッシュポリシー・オリジンポリシーの挙動を調査の上、以下のような設定としました。

キャッシュポリシーとして以下の3つを用意しておきます。

* WordPress-No-Cache
    * Minimum TTL : 0
    * Maximum TTL : 0
    * Default TTL : 0

* WordPress-Cache
    * Minimum TTL : 3600
    * Maximum TTL : 31536000
    * Default TTL : 86400
    * Cache key contents
        * Headers (Whitelist)
            * Authorization
            * CloudFront-Forwarded-Proto
            * X-WP-Nonce
            * Host
        * Cookies (Whitelist)
            * wordpress_logged_in_*
            * wordpress_sec_*
            * wp-settings*
        * Query strings
            * All
    * Gzip enabled 

* WordPress-Static-S3
    * Minimum TTL : 86400
    * Maximum TTL : 31536000
    * Default TTL : 86400
    * Cache key contents
        * None

また、オリジンポリシーとして以下を用意します。

* WordPress-Origin
    * Origin request contents
        * Headers
            * All viewer headers
        * Cookies
            * All
        * Query strings
            * All

## Behavior

上記ポリシーを用意してやっと、リクエストのパスに基づいたオリジン・キャッシュ動作を選択することができます。

WordPress の CDN として配置する CloudFront に私は以下のような Behavior を割り当ててみました。

1. wp-admin/*
    * Origin: `origin.example.com`
    * Cache Policy: `WordPress-No-Cache`
    * Origin Request Policy: `WordPress-Origin`
1. *.php
    * Origin: `origin.example.com`
    * Cache Policy: `WordPress-No-Cache`
    * Origin Request Policy: `WordPress-Origin`
1. wp-json/*
    * Origin: `origin.example.com`
    * Cache Policy: `WordPress-No-Cache`
    * Origin Request Policy: `WordPress-Origin`
1. wp-content/uploads/*
    * Origin: `S3`
    * Cache Policy: `WordPress-Static-S3`
    * Origin Request Policy: -
1. Default (*)
    * Origin: `origin.example.com`
    * Cache Policy: `WordPress-Cache`
    * Origin Request Policy: `WordPress-Origin`

このように設定することで、CloudFront でキャッシュされて欲しくない管理画面やログイン画面、キャッシュされることで挙動がおかしくなる `wp-json` 以下のファイルなどをキャッシュ対象から除外することができます。

また、ブログ上で利用されている画像や動画などが `wp-contents/uploads/` 以下の URL でアクセスされた場合には、S3 をオリジンとしてファイルを提供し、キャッシュ時間を長くしてリロード時の読み込み時間を減らすことができます。

そして、上記のどれにも当てはまらない通常の記事等は、Default の Behavior でキャッシュ提供されます。

# 前半まとめ

さて、WordPress サイトを AWS サービスを活用して構築する記事ですが、長くなってしまいましたので、この辺りで一度分割いただきます。

後半では、画像や動画の提供元とする、S3 の作成、WordPress にプラグインを入れて CloudFront や S3 と一緒に利用する場合のチューニング方法、SES を利用した WordPress からのメール送信などについて、ご紹介したいと思います。