ENECHANGE Developer Blog

ENECHANGE開発者ブログ

Wordpress(PHP-FPM)と Nginx との間の FastCGI の設定を見直した話

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' と表示され、何なのか分からない、と悩んでいる方にお役に立てると嬉しいです。