はじめに
PHP のパッケージ管理と言えば Composer が上がりますが、WordPress でしか PHP を触ったことがなかった私は、今まで Composer を使う機会がほとんどありませんでした。現代の PHP 開発において Composer を使わないなんてあり得ないと思われますが、かく言う私も Composer を触り始めて何で使ってこなかったんだと後悔している状況です。
さて置き、Composer で WordPress を管理できるの?
答えは可能です(知っていたら申し訳ないです)。
roots がオープンソースプロジェクトのもと Bedrock なる WordPress のボイラーテンプレートを開発しています。
Bedrock はモダンな WordPress の開発環境を提供することを目的とし、Composer で簡単にインストールすることができます。また、実はプラグインも Composer で管理できます。そのため、依存関係が composer.json の1つのテキストファイルに集約されるので、Git 等でのバージョン管理がはかどります。また、docker-compose を併用すればインフラレベルでスクラップ&ビルドして、ある程度モダンな開発環境も整えることができます。
と言うよりは、こちらに開発/本番環境を移行するべきかと最近は考えています。私は当ブログを絶賛インフラを含めてリファクタリング/リプレイス作業中です。
そこで今回は Bedrock の開発環境を docker-compose で構築する内容を共有したいと思います。
現在、当ブログは WordPress ではなく Gatsby.js で構築されています 🙇♂️
TL; DR
リポジトリも用意しているので、さくっと見たい方はこちらを参照ください。 https://github.com/canji53/bedoc
環境
- MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)
- macOS Catalina バージョン 10.15.4
- docker desktop community Version 2.3.0.3 (45519)
1# グローバルでcomposerをインストール済
2$ composer --version
3Composer version 1.10.6 2020-05-06 10:28:10
手順
以下の手順はあくまでも私が個人的に開発環境を整える一例になりますので、本番運用を含めたインフラ構築ではないことに留意いただければと思います。
また、以後の手順は PHP 界隈でとても有益な情報を提供してくださるゆうきゃんさんの最強の Laravel 開発環境を Docker を使って構築する【新編集版】」とPHP7.4 ぼくのかんがえたさいきょうの php.ini」の情報を多分に取り込んだ内容になっております。私の場合は Laravel ではなく、WordPress の Bedrock に置き換えて個人的にカスタマイズしている感じになります。
正直に申し上げると、上記の2つの参考文献は非常に有益な知見に富んでおり、上記文献で事足り、、、お暇があれば是非ともご一読願います。しっかりと要点が整理されており、技術記事としてちゃんと権威性があります 💦
0. 完成後のディレクトリ構造
全体像を掴むためにも予め完成後のディレクトリ構造を示しておきます。
1# 一部不要なファイルやディレクトリは非表示にしています
2$ tree ./ -aL 3
3
4./
5├── .env
6├── docker
7│ ├── bedrock
8│ │ └── develop.env
9│ ├── mysql
10│ │ └── data
11│ ├── nginx
12│ │ └── conf.d
13│ └── php
14│ ├── Dockerfile
15│ ├── conf.d
16│ └── php-fpm.d
17├── docker-compose.yml
18├── mytheme -> src/web/app/themes/mytheme
19└── src
20 ├── .env
21 ├── .gitignore
22 ├── README.md
23 ├── composer.json
24 ├── composer.lock
25 ├── config
26 │ ├── application.php
27 │ └── environments
28 ├── phpcs.xml
29 ├── vendor
30 └── web
31 ├── app
32 ├── index.php
33 ├── wp
34 └── wp-config.php
1. Bedrcok の生成
まず、./src
と命名して Composer で Bedrock プロジェクトを生成します。
1$ composer create-project roots/bedrock src
2
3# 一部不要なファイルやディレクトリは非表示にしています
4$ tree ./src -aL 3
5
6./src
7├── .env
8├── .gitignore
9├── README.md
10├── composer.json
11├── composer.lock
12├── config
13│ ├── application.php
14│ └── environments
15│ ├── development.php
16│ └── staging.php
17├── phpcs.xml
18├── vendor
19├── web
20│ ├── app
21│ │ ├── mu-plugins
22│ │ ├── plugins
23│ │ ├── themes
24│ │ └── uploads
25│ ├── index.php
26│ ├── wp
27│ └── wp-config.php
28└── wp-cli.yml
少しだけディレクトリ構造を解説しますと、
./src/.env
は WordPress で使用する環境変数を簡単に追加するファイルです。./src/config
には 開発/検証/本番 で使用する WordPress のシステム設定が記述されたファイルが格納されており、各ファイルは wp-config.php に相当しています。また、処理内容は./src/.env
の環境変数を define 展開するようなものです。./src/web/app
は実装したコードを配置するディレクトリで、wp-content に相当しています。./src/web/wp
は WordPress のシステムコードが集約されており、開発者は基本的に触らずに隠匿しておきます。また、./src/web/wp-config.php
も Bedrock では触る必要はありません。
2. ./docker-compose.yml と ./.env
ひとまず、./docker-compose.yml
1version: "3"
2
3services:
4 mysql:
5 image: mysql:5.7
6 volumes:
7 - ./docker/mysql/data:/var/lib/mysql
8 environment:
9 MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
10 MYSQL_USER: ${DB_USER}
11 MYSQL_PASSWORD: ${DB_PASSWORD}
12 MYSQL_DATABASE: ${DB_NAME}
13 TZ: "Asia/Tokyo"
14 ports:
15 - 3306:3306
16 restart: always
17
18 php:
19 build:
20 context: "."
21 dockerfile: ./docker/php/Dockerfile
22 args:
23 - ENVIRONMENT=${ENVIRONMENT}
24 volumes:
25 - ./docker/php/conf.d/${ENVIRONMENT}.php.ini:/usr/local/etc/php/conf.d/php.ini
26 - ./docker/php/php-fpm.d/zzz-www.conf:/usr/local/etc/php-fpm.d/zzz-www.conf
27 - ./src:/var/www/html
28 - ./docker/bedrock/${ENVIRONMENT}.env:/var/www/html/.env
29 - php-fpm-socket:/var/run/php-fpm
30 depends_on:
31 - mysql
32 restart: always
33
34 nginx:
35 image: nginx:stable-alpine
36 volumes:
37 - ./docker/nginx/conf.d/${ENVIRONMENT}.conf:/etc/nginx/conf.d/default.conf
38 - ./src:/var/www/html
39 - ./docker/bedrock/${ENVIRONMENT}.env:/var/www/html/.env
40 - php-fpm-socket:/var/run/php-fpm
41 ports:
42 - 80:80
43 depends_on:
44 - php
45 restart: always
46
47volumes:
48 php-fpm-socket:
docker-compose 側の ./env
1# environment
2ENVIRONMENT=develop
3
4# mysql
5DB_ROOT_PASSWORD=rootsample
6DB_USER=wordpress
7DB_PASSWORD=sample
8DB_NAME=sample-dev
9DB_HOST=mysql
マウントしたい環境依存ファイルを可変に
1volumes:
2 - ./docker/php/conf.d/${ENVIRONMENT}.php.ini:/usr/local/etc/php/conf.d/php.ini
トリッキーなことをしているのは、docker 側の .env
の環境変数 ENVIRONMENT
で volumes する対象環境( develop / staging / production )のファイルを選定している点です。これで、本番投入も楽になるかと考えていますが、まだ検証も済ませてないので、ファイルを直指定することをオススメします。
プログラム的に記述すると、下記になるかと思います。
if (ENVIRONMENT == develop) { ${ENVIRONMENT}.php.ini = develop.php.ini; }
args で Dockerfile に環境変数を渡して build 処理を可変に
docker-compose.yml
1php:
2 build:
3 context: "."
4 dockerfile: ./docker/php/Dockerfile
5 args:
6 - ENVIRONMENT=${ENVIRONMENT}
Dockerfile
1ARG ENVIRONMENT
2
3RUN set -ex; \
4 # #
5 # 略 #
6 # #
7 # if develop-environment, install xdebug.
8 if [ "${ENVIRONMENT}" = "develop" ]; then \
9 echo "pecl install xdebug"; \
10 echo "docker-php-ext-enable xdebug"; \
11 fi; \
また、php コンテナでは args で ENVIRONMENT
を引数として渡して、環境ごとに build 処理を変えています。ここでは、xdebug を導入するために処理を分岐させています。ただし、シンプルな記述を求められる docker にてこの処理は褒められた方法ではありませんので、適宜変えることをオススメします。
※ xdebug はデバッグ内容が筒抜けになってしまうため本番環境では絶対に投入しないようお願いします。
UNIX ドメインソケットをマウントして socket 通信
docker-compose.yml
1php:
2 volumes:
3 - php-fpm-socket:/var/run/php-fpm
4
5nginx:
6 volumes:
7 - php-fpm-socket:/var/run/php-fpm
nginx.conf
1location ~ \.php$ {
2 include fastcgi_params;
3 try_files $uri =404;
4 fastcgi_split_path_info ^(.+\.php)(/.+)$;
5 fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
6 fastcgi_index index.php;
7 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
8 fastcgi_param PATH_INFO $fastcgi_path_info;
9}
socket 通信は TCP 通信に比べてスループット(単位時間あたりの処理能力)が向上するらしく、今回はこちらを採用しています。後述する php-fpm の conf にて UNIX ドメインソケットを /var/run/php-fpm
に格納するため、こちらを nginx 側にもマウントしておきます。nginx の conf で fastcgi_pass に /var/run/php-fpm/php-fpm.sock
を指定するとソケット通信が可能になります。
3. 各コンテナ(./docker)の詳細設定
1# 一部不要なファイルやディレクトリは非表示にしています
2$ tree ./docker -L 3
3
4./docker
5├── bedrock
6│ └── develop.env
7├── mysql
8│ └── data
9├── nginx
10│ └── conf.d
11│ └── develop.conf
12└── php
13 ├── Dockerfile
14 ├── conf.d
15 │ └── develop.php.ini
16 └── php-fpm.d
17 └── zzz-www.conf
基本的に環境依存のファイルが格納されていますが、環境名をプリフィックスに持つことで、個人的に管理しやすくしています。
./docker/bedrock
./docker/bedrock/develop.env
1DB_NAME='sample-dev'
2DB_USER='wordpress'
3DB_PASSWORD='sample'
4
5# Optionally, you can use a data source name (DSN)
6# When using a DSN, you can remove the DB_NAME, DB_USER, DB_PASSWORD, and DB_HOST variables
7# DATABASE_URL='mysql://database_user:database_password@database_host:database_port/database_name'
8
9# Optional variables
10DB_HOST='mysql'
11DB_PREFIX='wp_'
12
13WP_ENV='development'
14WP_HOME='http://localhost'
15WP_SITEURL="${WP_HOME}/wp"
16WP_DEBUG_LOG=/path/to/debug.log
17
18# 以下略
同ファイルは Bedrock の ./src/.env
からコピーしたものになります。src 直下で 各環境ごとの .env ファイルを管理しても良いのですが、マウント時の処理が複雑化するので src から切り離して ./docker/bedrock/ 下においています。
./docker/mysql/data
mysql のデータディレクトリをマウントすることで DB を永続化しています。
./docker/nginx
./docker/nginx/develop.conf
1server {
2 listen 80;
3 listen [::]:80;
4 server_name localhost;
5
6 root /var/www/html/web;
7 index index.php;
8
9 access_log /var/log/nginx/access.log;
10 error_log /var/log/nginx/error.log;
11
12 gzip on;
13 gzip_disable "MSIE [1-6]\\.(?!.*SV1)";
14 gzip_vary on;
15 gzip_types text/plain text/css text/javascript image/svg+xml image/x-icon application/javascript application/x-javascript;
16
17 location / {
18 try_files $uri $uri/ /index.php$is_args$args;
19 }
20
21 location ~ \.php$ {
22 include fastcgi_params;
23 try_files $uri =404;
24 fastcgi_split_path_info ^(.+\.php)(/.+)$;
25 fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
26 fastcgi_index index.php;
27 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
28 fastcgi_param PATH_INFO $fastcgi_path_info;
29 }
30}
ポイントとしては速度面を考慮して TCP ソケットではなく、UNIX ドメインソケットを使用している点です。どうも TCP に比べて UNIX ドメインは遥かにスループットが優れてるらしいです。
また、 gzip を使ってコンテンツの圧縮配信も対応させています。
./docker/php
./docker/php/Dockerfile
1FROM php:7.3-fpm-alpine
2
3ARG ENVIRONMENT
4
5# Initial setup
6RUN set -ex; \
7 # 1) Install initial modules
8 apk update; \
9 apk add --no-cache \
10 bash \
11 sed \
12 ghostscript; \
13 # 2) Install php extension
14 apk add --no-cache --virtual .build-deps \
15 $PHPIZE_DEPS \
16 freetype-dev \
17 imagemagick-dev \
18 libjpeg-turbo-dev \
19 libpng-dev \
20 libzip-dev; \
21 docker-php-ext-configure gd --with-freetype-dir=/usr --with-jpeg-dir=/usr --with-png-dir=/usr; \
22 docker-php-ext-install -j "$(nproc)" \
23 bcmath \
24 exif \
25 gd \
26 mysqli \
27 opcache \
28 zip; \
29 pecl install imagick-3.4.4; \
30 docker-php-ext-enable imagick; \
31 # if develop-environment, install xdebug.
32 if [ "${ENVIRONMENT}" = "develop" ]; then \
33 echo "pecl install xdebug"; \
34 echo "docker-php-ext-enable xdebug"; \
35 fi; \
36 runDeps="$( \
37 scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \
38 | tr ',' '\n' \
39 | sort -u \
40 | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
41 )"; \
42 apk add --virtual .wordpress-phpexts-rundeps $runDeps; \
43 apk del .build-deps;
WordPress の 公式 Docker イメージを参考に、イメージをデトックスするために RUN で単一実行できるようにしています。また、前述しましたが、docker-compose 側から args で引数を受け取り、develop 環境時に xdebug をインストールするようにしています。ただし、こちらの条件分岐は、なるべくシンプルな記述が求められる Docker においてはアンチパターンかなと思います。
./docker/php/conf.d/develop.conf
1zend.exception_ignore_args = Off
2expose_php = On
3max_execution_time = 30
4max_input_vars = 1000
5upload_max_filesize = 128M
6post_max_size = 128M
7memory_limit = 128M
8
9; error logging
10error_reporting = E_ALL
11display_errors = On
12display_startup_errors = On
13log_errors = On
14error_log = /var/log/php/php-error.log
15ignore_repeated_errors = On
16ignore_repeated_source = On
17html_errors = On
18
19default_charset = UTF-8
20
21[mysqlnd]
22mysqlnd.collect_memory_statistics = On
23
24[Assertion]
25zend.assertions = 1
26
27[mbstring]
28mbstring.language = Japanese
29
30[xdebug]
31xdebug.remote_enable = 1
32xdebug.remote_autostart = 1
33xdebug.remote_host = host.docker.internal
34xdebug.remote_port = 9001
35xdebug.remote_log = /tmp/xdebug.log
36
37[opcache]
38opcache.enable = 1
39opcache.memory_consumption = 64
40opcache.interned_strings_buffer = 8
41opcache.max_accelerated_files = 4000
42opcache.validate_timestamps = 1
43opcache.revalidate_freq = 60
44opcache.huge_code_pages = 0
45opcache.fast_shutdown = 1
./docker/php/php-fpm.d/zzz-www.conf
1[www]
2listen = /var/run/php-fpm/php-fpm.sock
3listen.owner = www-data
4listen.group = www-data
5listen.mode = 0666
こちらの develop.conf と zzz-www.conf はゆうきゃんさんの「PHP7.4 ぼくのかんがえたさいきょうの php.ini」を参考に作成しており、詳しい内容は前述のリンクに飛んで詳細に確認していただければと思います。
4. (オプション)自テーマをシンボリックリンク
1$ ln -s ./src/web/app/themes/mytheme ./mytheme
自テーマを開発するディレクトリは src の下層の themes までネストしており、さらに mytheme も開発が進むにつれネストするのは必定なので、見通しをよくするためにも私は作業ディレクトリのルートにシンボリックリンクを貼っています。また、mytheme と味気ない名前を付けていますが、必要に応じてリネームしてください。
5. docker-compose を起動
1$ docker-compose up -d
ブラウザで http://localhost にアクセスして WordPress のインストール画面が出てきたら開発環境の構築は完了です。
6. Composer で プラグインをインストール
1$ composer require wpackagist-plugin/safe-redirect-manager
2$ composer require wpackagist-plugin/simple-page-ordering
3$ composer require wpackagist-plugin/woocommerce
プラグインのインストールは require で一発です。ただし、WordPress のプラグインやテーマを Composer リポジトリとして提供する WordPress Packagist で検索できないプラグインはインストールすることができません。Composer を導入しても肝心のプラグインがインストールできないとなると中途半端な環境移行になり、却ってコストがかさむ可能性もあります。そのため、予め WordPress Packagist にプラグインが存在しているかチェックしてもらえればと思います。
おわりに
ここまで比較的モダンな WordPress 環境を整えるのに奮闘しましたが、まだまだ問題点はあります。それは DB に WordPress のシステム設定が保持されている点です。これによって環境ごとに構成がバラつき、環境差異によるデプロイ困難が起きるなど、問題はまだまだ山積しております。
それを解決する手段として、wp-cfm と呼ばれるプラグインがあります。Composer でもインストール可能です。同プラグインは、DB に保持する設定を json 形式で diff / pull / push できる優れものです。これを使うことで限りなく環境ごとの DB の差異をコマンド形式で埋めることが可能になります(ただし 100%ではありません)。
まだ、検証が済んでいないので導入にまで至っていませんが、運用までもっていけたら、また記事を書きたいなと考えております。