ENECHANGE のチーフエンジニアという肩書の 川西( a.k.a cuzic )です。
最近、子ども(男の子、6歳)がスーパーマリオメーカー2で作ったステージで遊んでいます。 見たことがない敵がたくさんいますね。びっくりします。 踏んだら倒せるかも分からないし、どう動くかもわかりません。 初見では死んじゃいます。
はじめに
Wordpress のインフラ改善シリーズ第4弾です。
今回は、Wordpress(PHP-FPM)に Nginx から接続する方法を改善した話を書きます。
移行前の設定
enechange.jp はもともと Ruby on Rails と Wordpress が一台の EC2 の中に同居しており、 Nginx でその2つを振分けするように設定していました。 /articles 以下のパスを受け取ったときは Wordpress 側が応答しています。
この Wordpress (PHP-FPM)側の設定のリファクタリングをしたというのが今回の記事の内容です。
もともと FastCGI に通信するときに一度 Unix ドメインソケットを経由するような Nginx の設定になっていました。 下記のようなかんじです。
server { listen unix:/var/run/nginx-backend.sock; server_name _; # FastCGI に接続するための設定 } upstream backend { server unix:/var/run/nginx-backend.sock; } server { listen 80; server_name _; location /articles { # proxy_cache 関連の設定 proxy_pass http://backend; } }
このように Unix ドメインソケットを経由していることによって、運用上の問題が生じていました。
サーバ再起動のタイミングで、まれに /var/run/nginx-backend.sock
が消えず、その結果、Nginx が起動せず、サービスが停止する問題が起きていました。
毎日行っている定期スナップショットを元に AMI を作りサーバを構築する場合は、かならず /var/run/nginx-backend.sock
が残っています。
そのため、定期スナップショットを元に台数を増やすときは、① AMIを作る、② インスタンスを作成、起動、③ 残っている sock ファイルを削除、④ サーバを停止、⑤ 再度 AMI を作成、⑥ その AMI を元にサーバ台数を増やすという長い手順が必要になってしまっていた。
また、Nginx 設定のファイル分割が不適切で、どこにどんな設定が書かれているのかが分かりにくく保守性が低い状態となっていました。
既存の設定は歴史的な経緯で上記のような Unix ドメインソケットを経由する設定となっていましたが、実際は Unix ドメインソケットがなくても問題ありません。
下記のようにできます。
server { listen 80; server_name _; location /articles { # FastCGI に接続するための設定 } }
この方がずっとシンプルで、保守性も高く、sock ファイルが残ることに起因する問題も発生しません。
そこで、 Unix ドメインソケットを廃止するような設定変更を実施することにしました。
移行時のポイント
もともと Unix ドメインソケットを使って、proxy の設定をすることで、下記を実現していました。
- proxy_cache を使って、Nginx のレイヤでページキャッシュする
- /articles という PATH を / にマッピングした上で FastCGI の設定をする
これらは下記の方法に移行しました。
- fastcgi_cache を使ってキャッシュする
- fastcgi_param の SCRIPT_FILENAME の設定を適切に行い、 Wordpress のディレクトリを正しく参照できるようにする
fastcgi_cache によるキャッシュ
Wordpress ログイン判定
Wordpress では記事編集などのために管理者としてログインした場合、キャッシュしたページを返すのではなく、常に最新のコンテンツを返す必要があります。 FastCGI でキャッシュする場合もこの点を考慮して実施する必要があります。
管理者としてログイン中かどうかは cookie の内容を見て判定できます。
具体的には下記のような処理を書きました。
set $do_not_cache 0; if ($http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-postpass_" ) { set $do_not_cache 1; } if ($request_method != GET) { set $do_not_cache 1; } fastcgi_no_cache $do_not_cache; fastcgi_cache_bypass $do_not_cache;
fastcgi_no_cache
というディレクティブを使うことで、該当の条件を満たしたときは、キャッシュせずに最新のコンテンツを応答するように設定しています。
CloudFront 配下にある場合の Nginx での モバイル、PC の判定
enechange.jp では、サーバサイドで出し分けして、スマートフォンでアクセスした場合はモバイル用のページ、PC でアクセスした場合は PC 用のページを応答するようにしています。また、enechange.jp で Wordpress のページは CDN ( Amazon CloudFront )でもキャッシュを行ってます。 Amazon CloudFront を使う場合は、 サーバでは元の User-Agent を知ることはできません。代わりに、 CloudFront-Is-Mobile-Viewer や CloudFront-Is-Tablet-Viewer という HTTP ヘッダに格納される Amazon CloudFront の判定結果を使う必要があります。
判定結果に応じてキャッシュキーを切り替えるために記述した設定が下記になります。
set $mobile ""; if ( $http_cloudfront_is_mobile_viewer = 'true' ) { set $mobile '@smartphone'; } if ( $http_cloudfront_is_tablet_viewer = 'true' ) { set $mobile '@smartphone'; } fastcgi_cache_key "$scheme://$host$request_uri$mobile";
SCRIPT_FILENAME の値の編集
Nginx から PHP-FPM に対して SCRIPT_FILENAME
というHTTPヘッダを送ると、その内容に応じたファイルで処理されます。
この SCRIPT_FILENAME
ヘッダを適切に設定するためには
location /articles/ { alias /var/www/html/; fastcgi_param SCRIPT_FILENAME $request_filename; }
というかんじに設定しています。
Amazon Elastic Beanstalk の PHP 環境ではアプリケーションのファイルは /var/www/html 以下で参照できます。
そのディレクトリを使うように alias
ディレクティブを使っています。
Nginx 側の $request_filename
の値の確認
しかしながら、当然かもしれませんが、上記をそのまま実行しても期待どおり動作しないこともありました。 そこで、デバッグが必要です。
ここでは $request_filename
の値が何なのか、知りたいところです。
いわゆる printf デバッグです。
Nginx で変数の値を知るためには
add_header "X-Request-Filename" $request_filename always;
などと設定します。 このように設定することで、 Google Chrome の開発者ツールを使って HTTP 応答ヘッダの中の X-Request-Filename の内容を確認できます。
always
を指定することで 404 Not Found のステータスで応答している場合でも
"X-Request-Filename" の HTTP レスポンスヘッダを確認できます。
Wordpress (PHP-FPM)側の SCRIPT_FILENAME の値の確認
PHP-FPM が受け取っている SCRIPT_FILENAME の値を確認するためには、ログに書き出す方法があります。
私が使っている環境ですと、/usr/local/etc/php-fpm.d/www.conf
に下記のような記述があります。
; The access log format. ; The following syntax is allowed ; 省略 ; %e: an environment variable (same as $_ENV or $_SERVER) ; it must be associated with embraces to specify the name of the env ; variable. Some exemples: ; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e ; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e ; %f: script filename ; 省略
この %e
や %f を使います。
もとの access.format
に対して、前に書くのであれば
access.format = "%{SCRIPT_FILENAME}e %f %R - %u %t \"%m %r%Q%q\" %s"
とするかんじです。
こうすることで、PHP-FPM 側で受け取っている SCRIPT_FILENAME の値をアクセスログに表示できます。期待と異なる値になっていれば、 Nginx 側の設定を修正することができます。
おわりに
今回は、Nginx から Wordpress に通信するときの設定をリファクタした話について書きました。
このあたり、デバッグする方法について、詳しく解説した記事が少なく、私自身設定を書くときに試行錯誤しました。
Wordpress の設定をしているが、ブラウザでは 404 Not Found のページが表示され、ログを見ると php-fpm got error 'primary script unknown'
と表示され、何なのか分からない、と悩んでいる方にお役に立てると嬉しいです。