先日、Docker Desktop でもホストネットワーキングモードがサポートされたようです。
どのようなときにホストネットワーキングモードの恩恵を受けられるか考えていたのですが、ローカル環境かつ Nginx + PHP-FPM 構成で WP-Cron を使った時に良さそうだなと思ったのでやってみました。
WP-Cron が機能しない問題
ローカル環境で Nginx + PHP-FPM 構成で WordPress を動かそうとすると、WP-Cron が機能しなくなるというのは、誰もが一度は通った道かと思います。
WP-Cron が機能しなくなる理由を簡単に説明すると、以下のような流れになります。
- WP-Cron は WordPress のリクエストによってトリガーされる
WP-Cron はページへのアクセスがあった際にスケジュールを確認し、必要があればsite_url
配下のエンドポイントへ HTTP リクエストを送信します。 - site_url がローカル環境では localhost になる
ローカル環境で WordPress を実行しているため、site_url
はhttp://localhost
になることが多いです。そのため、WP-Cron はlocalhost
に対してリクエストを送信することになります。 - PHP-FPM コンテナで localhost にリクエストをすると、自分自身に送信される
Docker コンテナでlocalhost
はコンテナ自身を示します。 - PHP-FPM コンテナには HTTP サーバーは開かれていない
PHP-FPM コンテナは HTTP リクエストを受け付けるポート (通常は 80 番) が開かれていません。よって、リクエストが失敗し、WP-Cron が実行できなくなります。
要するに、localhost
が示す先がコンテナ自身になってしまうということが原因ですので、WordPress を実行するサーバーの IP アドレスにドメインが関連付けられている場合 (本番環境など) では発生し得ません。
WP-Cron が機能していないことは管理画面の通知から窺い知ることが出来ます。WP Crontrol をインストールした環境であれば設定画面で確認出来ます。
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 側で有効化します。手順は以下を参考にしました。
次に、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
を指定してください。
ログインしてダッシュボードまで辿り着いたら、WP-Cron の確認をします。WP Crontrol をインストールして設定画面を見てみると「cURL error 7: ~」のメッセージが表示されていないことが確認出来ます。
一覧から適当なイベントの「今すぐ実行」をクリックすると通知が表示され、正常に実行されたことが確認出来ます。
まとめと感想
Docker Desktop でホストネットワーキングモードがサポートされたと聞いたときに真っ先にこのネタが浮かびましたが、一般的には安定感の無い WP-Cron をここまでして使いたいモチベーションは無いだろうなとも思いました。個人的には長年抱え続けてきた気持ち悪さが解決したので満足度は高いです。