【WordPress】ホストネットワーキングモードでWP-Cronをちゃんと動かす

先日、Docker Desktop でもホストネットワーキングモードがサポートされたようです。

Docker Desktop内のコンテナに対して「localhost」でアクセス可能に、WSL2のストレージ領域を自動で縮小など新機能、Docker Desktop 4.34正式リリース
Docker社は、WindowsやMac、Linuxに手軽にDockerコンテナ環境を導入し利用できるソフトウェアであるDocker Desktopの最新版「Docker Desktop 4.34」正式版のリリースを発表しました。 Dock...

どのようなときにホストネットワーキングモードの恩恵を受けられるか考えていたのですが、ローカル環境かつ Nginx + PHP-FPM 構成で WP-Cron を使った時に良さそうだなと思ったのでやってみました。

WP-Cron が機能しない問題

ローカル環境で Nginx + PHP-FPM 構成で WordPress を動かそうとすると、WP-Cron が機能しなくなるというのは、誰もが一度は通った道かと思います。

WP-Cron が機能しなくなる理由を簡単に説明すると、以下のような流れになります。

  1. WP-Cron は WordPress へのリクエストによってトリガーされる
    WP-Cron はページへのアクセスがあった際にスケジュールを確認し、必要があれば site_url 配下のエンドポイントへ HTTP リクエストを送信します。
  2. site_url がローカル環境では localhost になる
    ローカル環境で WordPress を実行しているため、site_urlhttp://localhost になることが多いです。そのため、WP-Cron は localhost に対してリクエストを送信することになります。
  3. PHP-FPM コンテナで localhost にリクエストをすると、自分自身に送信される
    Docker コンテナで localhost はコンテナ自身を示します。
  4. PHP-FPM コンテナには HTTP サーバーは開かれていない
    PHP-FPM コンテナは HTTP リクエストを受け付けるポート (通常は 80 番) が開かれていません。よって、リクエストが失敗し、WP-Cron が実行できなくなります。

要するに、localhost が示す先がコンテナ自身になってしまうということが原因ですので、WordPress を実行するサーバーの IP アドレスにドメインが関連付けられている場合 (本番環境など) では発生し得ません。

WP-Cron が機能していないことは管理画面の通知から窺い知ることが出来ます。WP Crontrol をインストールした環境であれば設定画面で確認出来ます。

WP Crontrol の通知で、WP-Cron が機能しないことを示す「cURL error 7: Failed to connect to localhost port 80 after 0 ms: Couldn't connect to server。」が表示されている
cURL error 7: Failed to connect to localhost port 80 after 0 ms: Couldn't connect to server。

この事象の回避方法としては、HTTP リクエストを投げる先を cron_request フィルターフックで指定しているので、host.docker.internal などに書き換えるなどがあります。

$cron_request = apply_filters(
	'cron_request',
	array(
		'url'  => add_query_arg( 'doing_wp_cron', $doing_wp_cron, site_url( 'wp-cron.php' ) ),
		'key'  => $doing_wp_cron,
		'args' => array(
			'timeout'   => 0.01,
			'blocking'  => false,
			/** This filter is documented in wp-includes/class-wp-http-streams.php */
			'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
		),
	),
	$doing_wp_cron
);

ただ、ローカル環境のみで発生する事象に対して手を加えるのは、若干の気持ち悪さが残ります。

Docker のホストネットワーキングモードを使用すれば、localhost はホストを指すことになるので、上記の問題が解消しそうです。

やってみた

まずは、ホストネットワーキングモードを Docker Desktop 側で有効化します。手順は以下を参考にしました。

Host network driver
All about exposing containers on the Docker host's network

次に、Docker で Nginx + PHP-FPM + WordPress の環境を作ってみます。compose.yaml は以下です。

services:
   db:
     image: mysql:8.0
     volumes:
       - db_data:/var/lib/mysql
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress
     network_mode: "host"

   nginx:
    image: nginx:1.27.1-bookworm
    volumes:
      - ./default.conf:/etc/nginx/conf.d/default.conf
    volumes_from:
      - wordpress-fpm
    depends_on:
      - wordpress-fpm
    network_mode: "host"

   wordpress-fpm:
     image: wordpress:6.6.2-php8.1-fpm
     depends_on:
       - db
     network_mode: "host"

volumes:
    db_data:

ここで network_mode: "host" を指定することでホストネットワーキングモードが利用できます。

Nginx の default.conf は以下を用意しました。

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

    root /var/www/html;
    index index.php;

    location / {
        index  index.php;
    }

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
        fastcgi_pass   localhost:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

上記のファイルを作成出来たら docker compose up で立ち上げ、ブラウザから localhost にアクセスします。あとは適当にポチポチと設定をしてログインします。

データベースの設定での注意点として、ホスト名は localhost ではなく 127.0.0.1 を指定してください。

fw_error_www
fw_error_www

ログインしてダッシュボードまで辿り着いたら、WP-Cron の確認をします。WP Crontrol をインストールして設定画面を見てみると「cURL error 7: ~」のメッセージが表示されていないことが確認出来ます。

一覧から適当なイベントの「今すぐ実行」をクリックすると通知が表示され、正常に実行されたことが確認出来ます。

WP Crontrol の Cron イベント画面で正常終了した通知メッセージが表示されている

まとめと感想

Docker Desktop でホストネットワーキングモードがサポートされたと聞いたときに真っ先にこのネタが浮かびましたが、一般的には安定感の無い WP-Cron をここまでして使いたいモチベーションは無いだろうなとも思いました。個人的には長年抱え続けてきた気持ち悪さが解決したので満足度は高いです。

タイトルとURLをコピーしました