👻

プロキシサーバー、EC2、RDS で WordPress を立ち上げたい

に公開

はじめに

今までレンタルサーバーで Docker を用いて WordPress を立ち上げていました。
Docker で WordPress と MySQL のコンテナを立ち上げていたのですが、色々問題もありました。

  • 全体的に重くなる (レンタルサーバーの性能に依存するため、結構強いスペックが必要)
  • WordPress のバージョンが固定される (バージョンアップできない?)
  • コンテナをリサイクルすると、データが飛ぶ可能性がある

せめて DB のデータを安全にしたい、あわよくばサーバーサイド (WordPress) の動きも軽くしたい。
そのため、AWS を活用した構成を考えてみました。

構成

レンタルサーバーはドメイン管理も担っているので、入りのレンタルサーバーは外せません。
そのため、レンタルサーバーからプロキシで EC2 へ飛ばす構成としています。
これでレンタルサーバーと EC2 の役割を分散できたので、負荷もそれなりに分散できる形になりました。

また EC2 と RDS を同じ VPC 内に配置することで、RDS はプライベートからの通信のみとしました。
これにより、DB への不用意なアクセスを避けることができます。

さらに DB を RDS に切り出すことで、プロキシサーバー、WordPress の管理の手違いでうっかりデータを飛ばすことも無くなります。

※ 過去に自分の WordPress データを全部飛ばしたのはまた別の話。

プロキシサーバー

単に Nginx にプロキシ設定をするのみではありますが、工夫ポイントを少しだけ。

server {
	listen 80;
	listen [::]:80;

	server_name sample.com;

	return 301 https://$host$request_uri;
}

まず、http 通信は https に強制しています。

server {
	listen 443 ssl;
	listen [::]:443 ssl;

	server_name sample.com;

	# SSL 設定
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
	ssl_prefer_server_ciphers on;
	ssl_session_cache shared:SSL:10m;
	ssl_session_tickets off;

	ssl_certificate	 	/etc/letsencrypt/live/sample.com/fullchain.pem;
	ssl_certificate_key	/etc/letsencrypt/live/sample.com/privkey.pem;

	# セキュリティヘッダー
	add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
	add_header X-Content-Type-Options "nosniff" always;
	add_header X-Frame-Options "DENY" always;
	add_header X-XSS-Protection "1; mode=block" always;

	# Proxy設定
	location / {
		proxy_pass http://x.x.x.x;
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_set_header Host $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;

		# キャッシュを有効にする設定
		proxy_set_header Accept "*/*";
		proxy_cache my_cache;
		proxy_cache_valid 200 302 10m;  # 200と302のレスポンスを10分キャッシュ
		proxy_cache_valid 404 1m;      # 404は1分だけキャッシュ
		add_header X-Cache-Status $upstream_cache_status;  # キャッシュヒットしたか確認用ヘッダー
	}
}

次に、https の設定を施します。

proxy_pass のところがミソで、ここを AWS の EC2 に向くように設定します。
ここは http で問題ありませんが、一度外部に出る以上 https が好ましいとは思います。
しかし、AWS の IP アドレスで ssh 通信を有効化するのは難しかった (IP アドレスでの証明書は作れず、また AWS の共有ホスト名 (xxx.compute.amazonaws.com) での証明書も作れませんでした) ので、今回はやむを得ず http 通信に留めています。
いい設定方法があれば改善したいポイントだったり。
※ もちろんプロキシサーバーの https 化は必要です

VPC

細かな設定は省略します。勉強頑張りましょう (自分への戒め)。
最低限必要な設定のみ記します。

  • VPC の DNS の解決、DNS のホスト名は有効化する (外部との通信が必要なため)
  • サブネットはパブリック、プライベートの両方必要
    • 特にプライベートは RDS 用の DB サブネットグループのため、各アベイラビリティーゾーン分だけ作成する
  • セキュリティグループも EC2 用、RDS 用に作成する
    • EC2 用: 外部と通信するため、http、https、ssh は必要
      • ssh は任意かもしれない
      • ソースは 0.0.0.0/0 でもいいが、プロキシサーバーに限った方が堅牢ではある
    • RDS 用: MySQL のみ
      • ソースは EC2 用のセキュリティグループを指定する

EC2

特に工夫はありませんが、VPC 内に作成することのみ気を付ければいいと思います。

Apache (WordPress)

sudo yum -y update && sudo yum -y upgrade

初期のセットアップと必要なパッケージをインストールします。

cd /var/www/html
sudo wget https://wordpress.org/latest.tar.gz
sudo tar -xzf latest.tar.gz
sudo mv wordpress/* .
sudo rm -rf wordpress latest.tar.gz

WordPress をダウンロードします。

sudo chown -R apache:apache /var/www/html
sudo chmod -R 755 /var/www/html

Apache のアクセス権限を変更します。

sudo systemctl restart httpd

Apache を再起動します。

sudo yum install php-gd

サイトヘルスのチェックに引っ掛かるので、PHP-GD をインストールしておきます。

sudo systemctl restart httpd

Apache を再起動します。

RDS

ここも特に工夫はありませんが、VPC 内に作成することのみ気を付けます。
また、オプションを確認して初期 DB を作成しておくと後に楽できます。
※ もし初期 DB を作れなかった場合、後に EC2 から mysql コマンドで接続し、DB を作成します。

最終設定

プロキシサーバーへアクセス (https) し、初期設定を済ませます。
DB 設定のみ注意が必要で、RDS のものを指定するようにします。

  • DB 名、ユーザー名、パスワード: RDS 作成時に指定したもの
  • エンドポイント: xxx.rds.amazonaws.com

特にエンドポイントはデフォルトだと localhost のため、要注意!

wp-config.php が作成されるユーザー登録まで進めます。
※ この段階ではスタイル崩れがある可能性がありますが、後に直すので気にせず進めます。

このままだと CSS や JavaScript が http で呼び出され、スタイルが壊れることがあります。
そのため、wp-config.php に工夫を加えておきます。

// HTTPSを有効化
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

こいつにかなり悩まされました。
これを読んでいるあなたが同じ轍を踏まないことを祈っています。
※ これを伝えたかったがためにこの記事を書いたといっても過言ではありません。

トラブルシューティング

ハマったポイントをまとめておきます。

なんか WordPress から DB にアクセスできない編

EC2 から mysql コマンドで接続はできているのに、WordPress からはこんなメッセージで怒られていた件。なんで?

Warning: mysqli_real_connect(): (HY000/1045): Access denied for user 'admin'@'x.x.x.x' (using password: YES)
Error establishing a database connection

セキュリティグループの設定、MySQL の権限、対象の DB の有無などを疑いましたが、どれもはずれ。
(DB の有無も該当してましたが、根本解決には至りませんでした)

なんと wp-config.php の DB のパスワードが間違っていただけという初歩的な罠でした。恥ずかしい。
ただ WordPress へのログイン情報、MySQL のログイン情報などが混在してしまうため、意外と混乱します。
恥を晒したので、上手くいかない時は自分を疑ってみてください!意外としょーもないとこでミスってたりします!
(ちなみに AWS のパスワードを入力してしまっていました)

無限リダイレクト編

ログイン画面へアクセスしようとすると、裏で無限にリダイレクトが走るなどで画面が表示されないことがあります。
WordPress の WordPress アドレス (URL) と サイトアドレス (URL) はプロキシサーバー (https) のものに設定しているのに、なぜ?
F12 の DevTools でも http のプロキシサーバーへデータを取りに行こうとしてたりして、もうなんでやねんって感じでした。

ここで必要になるのが最終設定で施した wp-config.php の工夫です。

記事とかまで作ってしまうと DB の書き換えなども必要になるため、wp-config.php ができたタイミングで工夫を施すことが重要です。
『EC2 のエンドポイントから設定しない』ことを一貫しておくと概ね防げるトラブルだったりします。
うっかり作ってしまった場合、妙に書き換えるより WordPress の再インストール、テーブルの再作成でリトライした方が早い気がします。

マジでご注意を!
※ これに数時間ハマりました。

パーマリンク構造を変えられない編

ここもハマりがちだと思います。

記事を変更すると 更新に失敗しました。 返答が正しい JSON レスポンスではありません。 が出てきたり、サイトにアクセスすると 404 Not Found になったりと、結構苦戦するポイントです。
Web 検索すると「パーマリンク構造を再度保存する」だの「.htaccess を見直す」だの出てくると思いますが、自前で立てばサーバーな手前、そんなヘマはしていないはずだと疑ってしまいます。

しかし、罠はまったく別のところにありました。
それは、Apache の設定ファイルで書き換えが有効化されていないことでした。

/etc/httpd/conf/httpd.conf の設定を見直してみます。

<Directory /var/www/html>
    ...
    AllowOverride None
    ...
</Directory>

こんな感じになってるんじゃないでしょうか。
ここをこう書き換えます。

<Directory /var/www/html>
    ...
    AllowOverride All
    ...
</Directory>

これだけです。これでパーマリンク構造を /投稿名 とかにできるようになります。

これはパスの書き換え設定で、パーマリンク構造を変える以上、WordPress 側がルーティングをハンドルすることになるので、それを許可してあげないといけなかったというオチです。

余談ですが、ここは Vue.js とかのサーバー設定でも必要になる工夫になります。
大昔にハマったことのある馴染みの罠すぎて、見つけた時は発狂してしまいました。
知識は残していかないとですね…。

サイトから XML が返ってくる編

ホーム、各記事に飛ぶと XML が返ってくる。でも管理画面のダッシュボードとか記事編集はできる。ナニコレ?
結論としては、Nginx のキャッシュが妙な悪さをしていたことでした。
キャッシュを消しても解消しなかったのが気持ち悪いところですが、キャッシュ設定の部分をコメントアウト → 再起動 → コメントアウト解除 → 再起動で治りました。
とりあえず解消法はわかったので、再現した時はもう少し詳しく掘ってみようと思います。

追記

Accept ヘッダーってのが悪さをしていたみたいで、下記設定で治りました。

location / {
    proxy_set_header Accept "*/*";
    proxy_pass http://x.x.x.x;
}

追記2

まだ稀に発生します。
ブログを Zenn に移行中なのでもうメンテナンスすることはほとんどなさそうなので、このトラブルは封印します。

終わりに

今まで何かとレンタルサーバーの Docker でゴリ押ししていましたが、今回 AWS を活用した構成に移行できました。
DB のデータ管理だけ分離させることは以前から成功していましたが、先述の構成のようにプロキシサーバー、Web サーバー、DB の分離ができたのは激アツでした。
これで今後もサービスを増やし放題!やったね!
個人開発もモリモリ頑張りたいです!

※ 自分への投資と思って目を瞑っていますが、それなりの課金は必要です。今回のような構成に移行するにあたり、不要になったサービスを手軽にクローズしやすくなったのもアツいです。

Discussion