docker-compose で作る nginx + PHP-FPM7 + HTTP/2 に対応したモダンな WordPress 開発環境

はじめに

前回のエントリで Docker ( docker-compose ) を使った nginx + HTTP/2 + PHP-FPM7 + MySQL 環境の構築方法をご紹介しました。

これまでは WordPress ないし PHP の動作環境をローカルマシン上に構築するとなると、XAMP や MAMP といったオールインワンパッケージをインストールしたり、VCCW と VirtualBox で仮想環境を構築するといった手法が考えられました。個人開発やちょっとした動作検証程度であればまだしも、環境ごとへの依存性が強いこれらの手法では、開発者間やローカルマシンと本番サーバとの間で環境を全く同じ状態にするのに困難を極めることでしょう。

Docker はシンプルかつ軽量な仕組みのコンテナ型仮想環境技術です。もともとはローカルマシンでの動作確認や開発環境構築を主としてましたが、Amazon EC2 Container Service ( AWS ECS ) 等の登場によってローカルマシン上に構築したものをそのまま本番サーバに適用することが出来るようになりました。『マシンに依存しない』『動作が軽量』『更新された時の統一が容易』と、これまでの負を一挙に解決出来るツールと言えます。

今回は前回のエントリでご紹介した内容をベースに WordPress 開発環境の構築手順をご紹介します。他ではなかなか難しいモダンな構成も Docker を利用すれば簡単かつ確実に作ることが出来ます

構築までのステップ

  1. 基本となる WordPress 環境を構築
  2. WordPress を日本語化対応させる
  3. WordPress テーマを開発できるようにする
  4. 任意の WordPress プラグインがインストールされた状態で起動する

このようなチュートリアル形式で進めていきます。当エントリのサンプルコードは GitHub にて公開しておりますので、ぜひ併せてご参照ください。

Step.1) 基本となる WordPress 環境を構築

はじめに基本となる WordPress 環境を構築します。docker-compose.yml にそれぞれのコンテナ情報を定義します。前回のエントリでの構成をベースにしつつ、このような構成にします。

version: '2'
services:
  web:
    build: ./web
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - app
    volumes_from:
      - app
    volumes:
      - ./web/default.conf:/etc/nginx/conf.d/default.conf
  app:
    image: wordpress:4.9.8-php7.2-fpm-alpine
    depends_on:
      - db
  db:
    image: mysql:5.7.23
    env_file: .env
    ports:
      - "3306:3306"
    volumes:
      - db-data:/var/lib/mysql
volumes:
  db-data:

WordPress 公式イメージから :4.9.8-php7.2-fpm-alpine タグのものを選択します。web サーバが nginx なので PHP はパフォーマンス性に優れた FPM ( FastCGI Process Manager ) 実装を、ベースとなる Linux OS は極限まで軽量化を図った Alpine Linux を使います。php:7.1.9-fpm-alpine イメージをベースに自前で WordPress をインストールしても良いのですが、結局ほぼ同じものが出来上がるだけなのではじめから公式イメージを使うとしましょう。

nginx は HTTP/2 に対応させるため SSL 証明書を自己発行した状態としたいので、 ./web/Dockerfile からビルドします。SSL 証明書自己発行の手順は前回のエントリ内 にて詳しく解説しています。

MySQL は公式イメージをそのまま使い、db-data という領域をホストマシン側に作成してコンテナ内のデータ領域 (/var/lib/mysql) にマウントすることでデータを永続化します。

2017年9月現在、Compose ファイルフォーマットの最新バージョンは 3.3 ですが、今回目的とする環境は volumes_from を使う理由から 2.x のフォーマットで記述します。

コンテナを起動して動作確認

ターミナルで docker-compose.yml のあるディレクトリに移動し、以下のコマンドを実行してコンテナを起動します。

$ docker-compose up -d
Creating network "step1basic_default" with the default driver
Creating volume "step1basic_db-data" with default driver
⋮
Creating step1basic_db_1 ...
Creating step1basic_db_1 ... done
Creating step1basic_app_1 ...
Creating step1basic_app_1 ... done
Creating step1basic_web_1 ...
Creating step1basic_web_1 ... done

ブラウザから https://localhost にアクセスしてみましょう。SSL 通信に自己証明書を使用しているのでブラウザから証明書の認証エラーとの警告が出されますが、詳細情報を表示して強制的にアクセスします。すると以下のように WordPress の初期設定画面が表示されるはずです。

無事に起動できて何よりですが、ここで初期設定を済ませたところで、コンテナを停止した途端に設定した内容は全て消失してしまいます。DB のデータ自体は volumes として保持されても wp-config.php など WordPress 本体は起動前の状態しか Docker に定義されていないため、次に起動するとまた wp-config.php 生成前 ( 初期設定操作をする前 ) から始まってしまうからです。

初期設定が済んだ状態を保持する

要は初期設定が済んで生成された wp-config.php を物理ファイルとしてホストマシン側で保持しておき、コンテナ起動時に volumes としてマウントしてしまえば良いのです。同じく DB は初期設定後の状態を dump して .sql ファイルとしてホストマシン側で保持してコンテナ起動時に volumes としてマウントして実行すれば初期設定完了状態の DB が作れます。

まずは wp-config.php です。app コンテナに入って /var/www/html/wp-config.php をコピペすれば OK です。以下のコマンドを実行して app コンテナに入ります。Alpine Linux は bash がインストールされていないので /bin/sh で入ります。

$ docker exec -it step1basic_app_1 /bin/sh
/var/www/html #

コンテナに入ったら、/var/www/html/wp-config.php を開いて中身のソースコードをコピーします。当然ながら Alpine に vi なんて気の利いたものは無いので cat コマンドでファイルの中身を出力します。


<?php
/**
 * The base configuration for WordPress
 *
 * The wp-config.php creation script uses this file during the
 * installation. You don't have to use the web site, you can
 * copy this file to "wp-config.php" and fill in the values.
 *
 * This file contains the following configurations:
 *
 * * MySQL settings
 * * Secret keys
 * * Database table prefix
 * * ABSPATH
 *
 * @link https://codex.wordpress.org/Editing_wp-config.php
 *
 * @package WordPress
 */
⋮
/** Sets up WordPress vars and included files. */
require_once(ABSPATH . 'wp-settings.php');

この内容をコピーしてホストマシン側に wp-config.php ファイルとして保存します。

.
├── README.md
└── app/
    └── wordpress/
        └── wp-config.php

続いて DB を dump します。今回は MySQL なので mysqldump コマンドを使って DB に保存されたデータを .sql ファイルとしてバックアップ出来ます。DB コンテナが起動している状態で以下のコマンドを実行します。

# docker exec -it dbコンテナ名 sh -c 'mysqldump データベース名 -u データベースユーザ名 -pデータベースパスワード 2> /dev/null' > ホスト側保存先ディレクトリ/mysql.dump.sql
$ docker exec -it step1basic_db_1 sh -c 'mysqldump wordpress -u wp_user -ppassword 2> /dev/null' > db/mysql.dump.sql

成功するとホストマシン側に mysql.dump.sqlというファイルが生成されます ( ファイル名は自由 )。これで必要なデータが全て揃いました。

docker-compose.yml を編集してそれぞれのファイルを各コンテナにマウントさせます。

app:
  image: wordpress:4.9.8-php7.2-fpm-alpine
  depends_on:
    - db
  volumes:
    - ./app/wordpress/wp-config.php:/var/www/html/wp-config.php
db:
  image: mysql:5.7.23
  env_file: .env
  ports:
    - "3306:3306"
  volumes:
    - db-data:/var/lib/mysql
    - ./db/mysql.dump.sql:/docker-entrypoint-initdb.d/install_wordpress.sql

これで準備が整いました。 docker-compose restartコマンドを実行してコンテナを再起動すれば、先ほどの初期設定が全て済んだ状態となっているのがお分かりいただけるかと思います。

ファイルアップロードサイズの上限値を設定する

PHP はファイルアップロードサイズがデフォルトで 2MB とかなり小さめに制限されています。ひと昔前であればこれで事足りたかもしれませんが、今どき 2MB ではスマホで撮影した動画も満足にアップロードできやしません。

そこでこの上限値を変更すべく PHP の設定ファイルを作成してイメージにマウントしましょう。

upload.ini というファイルを作成し、以下を記述します。

file_uploads = On
memory_limit = 64M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 600

今回は上限値を 64MB まで上げてみました。これをDocker コンテナ内の /usr/local/etc/php/conf.d/ ディレクトリにマウントします。これで WordPress メディアファイルやテーマの zip ファイルのアップロードが 64MB まで許容されます。

Step.2) WordPress を日本語化対応させる

Docker 版を含め、標準で配布されている WordPress は英語版のみです。初回セットアップ時や管理画面上で言語の変更は出来るものの、この設定は DB に保存されないので Docker を再起動するとまた英語に戻ってしまいます。そこで、これに一手間を加えて日本語化対応させてみましょう。

日本語版 WordPress をインストールした Docker イメージを作る

Step.1 では公式の WordPress イメージをそのまま使用しましたが、これをベースに日本語版の Docker イメージを作るとします。まずは app/ディレクトリに Dockerfileを作成します。

FROM wordpress:4.9.8-php7.2-fpm-alpine
# 日本語化するために WordPress 日本語版をインストールして展開
ENV WORDPRESS_TAR_FILE wordpress-4.9.8-ja.tar.gz
RUN rm -fr /usr/src/wordpress \
 && curl -O https://ja.wordpress.org/${WORDPRESS_TAR_FILE} \
 && tar -xzf ${WORDPRESS_TAR_FILE} -C /usr/src/ \
 && rm ${WORDPRESS_TAR_FILE} \
 && chown -R www-data:www-data /usr/src/wordpress
# ボリューム化
VOLUME /var/www/html

ベースとするイメージを指定します。FROM 命令で Step.1 で使用してた公式イメージを指定します。

次に WordPress 日本語版をインストールします。インストールと言っても実際にやってることはこれだけです。

  1. インストール先となるディレクトリを初期化
  2. 公式の配布先から日本語版ファイル ( .tar.gz形式 ) をダウンロード
  3. インストール先ディレクトリに解凍
  4. 不要となった圧縮ファイルを削除
  5. ファイルの所有者・グループを変更する

これらを RUN命令を使って実行します。それぞれのステップごとに RUN 命令を記述することも可能ですが、ここではひとつの RUN 命令に対し複数の UNIX コマンドを ( &&で繋げて ) 定義しています。Docker はひとつの RUN 命令ごとにキャッシュしてパフォーマンスを向上させる仕様となっています。そのため個別に RUN 命令を定義すると前後の命令同士のキャッシュが悪さして予期しない挙動をするリスクが生まれてしまいます。これらのUNIXコマンドは相互に依存した一連の処理なので、一つの命令文として定義するのが正しいお作法です。

最後に/var/www/html をボリューム化して外部からマウント可能にします。こうすることでwp-config.php や WordPress テーマなどがホストマシンからマウント出来るようになります。

docker-compose.yml を編集

app サービスのイメージをいま作成した Dockerfile からビルドするものに変更します。

services:
  ︙
  app:
    build: ./app
    depends_on:
      - db
    volumes:
      - ./app/wordpress/wp-config.php:/var/www/html/wp-config.php
      - ./app/php/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
  ︙

これで準備は完了です。

コンテナを起動して動作確認

ターミナルから起動コマンドを実行します。


ブラウザから https://localhost/wp-admin にアクセスして管理者ログインしてみましょう。先程まで英語版だったのが日本語版に変わっているのがお分かりいただけるかと思います。

Step.3) WordPress テーマを開発できるようにする

WordPress テーマディレクトリを volume としてマウントすれば、そのまま Docker コンテナ内の WordPress に読み込ませることができます。更にホストマシン側でテーマファイルを編集した内容もそのままダイレクトに反映されるので、Docker 上で動作確認をしながらテーマ開発をすることができます。

マウントするテーマディレクトリを指定する

テーマも別軸で開発することを想定して、Docker 関連とはディレクトリを分けるとします。

.
├── infra/
│   ├── app/
│   ├── db/
│   ├── docker-compose.yml
│   └── web/
└── themes/
    ├── _s/
    ├── package.json
    ├── src/
    │   ├── images/
    │   ├── scripts/
    │   └── styles/
    ├── webpack.config.js
    └── yarn.lock

Docker 関連のファイルを infra/というディレクトリ配下にまとめました。テーマ関連はthemes/というディレクトリ配下にまとめ、こちらで開発を進めていくようにします。

dokcer-compose.yml を編集します。

services:
  ︙
  app:
    build: ./app
    depends_on:
      - db
    volumes:
      - ./app/wordpress/wp-config.php:/var/www/html/wp-config.php
      - ./app/php/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
      - ../themes/_s:/var/www/html/wp-content/themes/_s
  ︙

テーマディレクトリを volume としてマウントします。これで Docker を起動すれば、WordPress 管理画面からマウントしたテーマをが追加されており、これを選択できるようになります。

Step.4) 任意の WordPress プラグインがインストールされた状態で起動する

一般的に WordPress プラグイン ( 以下、プラグイン ) は管理画面からの GUI 操作によってインストールします。しかし Docker で運用してる以上、GUI 操作でインストールしたものはコンテナ停止と同時にすべて消滅してしまいます。インストールしたプラグインに関する情報が Docker のコード上に反映されないからです。

WordPress テーマを開発していくと、特定の プラグインに依存してしまうことがあります。そうなるとやはりプラグインに関してもコード上でしっかり管理する必要があります。

プラグインインストールの仕組みは実はとても単純

実は GUI 操作に頼らずともプラグインはインストールできてしまいます。というのも、WordPress は wordpress ルートディレクトリ/wp-content/plugins/ ディレクトリ以下にあるファイル群をプラグインとして認識しているだけであり、手段に関係なくこのディレクトリに追加さえしてしまえばそれでインストール完了となるのです。そして公式ライブラリにあるプラグインはすべてzip形式で配布されています。つまり、欲しいプラグインを公式ライブラリからダウンロードし、解凍して plugins/ 以下に配置すればそれでインストールは完了となるのです。仕組みを理解してしまえばなんてことありませんね。

プラグインをインストールするシェルスクリプトを書いてみよう

仕組みが理解できてしまえば、これをコードに書き起こして自動化するのはさほど難しくありません。また、これ用の特別なプラグインや機能も必要ありません。十数行の簡単なシェルスクリプトのみで実現できます。インストールしたいプラグイン名を配列として定義し、それらをループ処理にかけて順番にダウンロードしてから zip ファイルを所定のディレクトリに解凍すればいいのです。以下がその内容です。

#!/bin/bash
# WP プラグイン一覧
# インストールしたいプラグインをここに記述する
plugins=("wp-emmet" "footnotes" "jetpack-markdown")
# 一覧にあるプラグインをダウンロード
for item in "${plugins[@]}"; do
    curl -L "https://downloads.wordpress.org/plugin/${item}.zip" --create-dirs --output "tmp/wp-plugins/${item}.zip"
done
# zip を解凍してインストール
unzip -o $1/\*.zip -d "/usr/src/wordpress/wp-content/plugins"
# 使用済みとなった zip を削除する ( 後片付け )
rm -rf "tmp/wp-plugins"

公式ライブラリにあるプラグインのダウンロード URL は https://downloads.wordpress.org/plugin/プラグイン名.zip と決まっています。一般的にプラグインはバージョン管理がなされていますが、バージョン番号を明記しなければ最新版が指定されます。特に理由がなければ最新版で問題無いはずなので、plugins 配列にはプラグイン名だけを記述すれば良いでしょう。ダウンロードが完了したら unzip コマンドで zip を解答します。最後に使用済みとなった zip を削除して完了です。

Dockerfile を編集

Dockerfile から先ほど作成したシェルスクリプトを実行させます。

FROM wordpress:4.9.8-php7.2-fpm-alpine
# ツールをインストール
RUN apk --update add bash unzip
# 日本語化するために WordPress 日本語版をインストールして展開
ENV WORDPRESS_TAR_FILE wordpress-4.9.8-ja.tar.gz
RUN rm -fr /usr/src/wordpress \
 && curl -O https://ja.wordpress.org/${WORDPRESS_TAR_FILE} \
 && tar -xzf ${WORDPRESS_TAR_FILE} -C /usr/src/ \
 && rm ${WORDPRESS_TAR_FILE} \
 && chown -R www-data:www-data /usr/src/wordpress
# WP プラグイン インストール
ADD ./bin/wp-plugins.sh /wp-plugins.sh
RUN bash /wp-plugins.sh
# ボリューム化
VOLUME /var/www/html

今回記述したシェルスクリプトは Bash で実行するよう定義したので、予め Bash をインストールしておく必要があります1)本当は shell で実行できるようにしたかったのですが、記述に問題があるのか実行エラーとなってしまいました。どなたか詳しい方からのご教示お待ちしております 🙇 。同様に zip を解凍するためにunzipパッケージもインストールしなくてはなりません。

次にプラグインインストールの命令文を定義します。先ほど作成したシェルスクリプトファイルをイメージ内に追加し、Bash からこれを実行します。

コンテナを起動して動作確認

ターミナルから起動コマンドを実行します。


ブラウザから https://localhost/wp-admin/plugins.php にアクセスしてインストール済みプラグインを確認してみましょう。指定したプラグインが起動した時点ですでにインストールされているのがお分かりいただけるかと思います。

締め

これまで WordPress を使ったサイト制作には VCCW を使っていました。Vagrant が基盤になっており、VirtualBox に仮想マシンを立てて構築するというものです。製作者が日本の方ということもあり比較的導入コストは低くて良いのですが、いかんせん起動に時間がかかるのとそれなりに負荷が高いという側面もあります。仮想マシンという巨大なバイナリ形式で状態を持っているので、開発者間での受け渡しやバージョン管理を容易にすることが出来ません。

Docker は学習コストが少々高めですが2)そもそも僕自身インフラの知識が浅いというのもあります。、ローカルとサーバーとで文字通り全く同じ構成を組めるのが強みです。また、全ての構成や状態を『プログラミングコード』という形で定義することが出来るので、その全てを容易にバージョン管理することも可能です。何より動作が軽いので、手軽に試して失敗したら即やり直すというのがスピーディに出来るので学習が捗ります。

脚注

脚注
1 本当は shell で実行できるようにしたかったのですが、記述に問題があるのか実行エラーとなってしまいました。どなたか詳しい方からのご教示お待ちしております 🙇
2 そもそも僕自身インフラの知識が浅いというのもあります。