MAMPやVCCWの代わりに、Dockerを使用してローカル上に仮想環境を構築し、WordPressサイトの開発をしている人も増えてきているのではないでしょうか。
個人的にはMAMPではプロジェクト毎に環境を変えるのが大変だし、VCCWは重たすぎるため、Dockerの軽さ+使い勝手の良さに魅力を感じます。
ただ、Docker公式のWordPressイメージは現段階では、デプロイ先のルート直下にWordPressを展開することしか想定されていません。
そのため、例えば/wp/
、/blog/wp/
といった下層ディレクトリでWordPressを動かしたい、またはファイルをまとめておきたい場合は、公式のWordPressイメージは使用できません(Pull Request発生しているので、今後のアップデートでできるようになる可能性は大いにありそうですが)。
下層ディレクトリでWordPressを動作させる場合は、公式のWordPressイメージを利用しつつ、docker-endpoint.sh
に修正を加えてゆきます。
Docker公式のWordPressイメージを使用した環境構築をしたことがない方は以下の記事を参照してください。
http://necosystem.hirokihomma.com/archives/223/
docker-compose.yml
の作成
プロジェクトディレクトリにdocker-compose.yml
を作成し、以下を記述してください。
MySQLの構築は、公式のWordPressイメージを使用する場合と同じです。
docker-compose.yml
は、コンテナの設定になります。
どのイメージを利用し、どのようなオプションパラメータを使いコンテナを作成するかを指定しています。
version: '3'
services:
db:
image: mysql:5.7
volumes:
- ./mysql:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress
volumes:
- ./www:/var/www/html
- ./docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_SUBDIRECTORY: blog/wp
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
ルートディレクトリをボリューム化し、ホスト(ローカル)で編集できるように設定
今回はプロジェクトディレクトリの直下にwww
というディレクトリを作成し、そこにサイトコンテンツを展開します。
通常の設定だと、コンテナを起動するたびにサイトコンテンツがリセットされてしまいますので、ボリューム化することで永続化させます。
また、ボリュームをホスト(ローカル)をコンテナ内のディレクトリとしてマウントし、開発しやすくしています。
volumes:
- ./www:/var/www/html
docker-entrypoint.sh
の上書き
docker-compose
では、volumes
などのパラメータと同じようにentrypoint
と指定することで、使用するdocker-entrypoint.sh
を上書きすることができます。
ただ、シェルファイルを指定する場合のパスはコンテナ内のパスになるので、今回はデフォルトのdocker-entrypoint.sh
にホスト(ローカル)上のプロジェクト直下のdocker-entrypoint.sh
をマウントさせることで置き換えます。
volumes:
- ./www:/var/www/html
- ./docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh
WordPressを展開する下層ディレクトリのパスを指定
環境変数に下層ディレクトリの指定を記述します。今回はblog/wp
というディレクトリにWordPressを展開する想定です。適宜変更してください。
environment:
WORDPRESS_SUBDIRECTORY: blog/wp
docker-entrypoint.sh
の作成
コンテナが起動されるたびに実行されるシェルコマンドをまとめたdocker-entrypoint.sh
を作成します。
まずは元となるdocker-entrypoint.sh
を用意します。
Docker公式のWordPressリポジトリからダウンロードし、ホスト(ローカル)のプロジェクト直下に設置しましょう。
PHPのバージョンごとのDockerfileと同じ階層にあるdocker-entrypoint.sh
をダウンロードしてもいいですし、リポジトリ直下にあるdocker-entrypoint.sh
をダウンロードしてきても問題ありません。
今のところ、PHPのバージョンによる違いはないようです。
https://github.com/docker-library/wordpress/blob/master/docker-entrypoint.sh
公式のdocker-entrypoint.sh
のままですと、デプロイ先のルートディレクトリで動く設定になっています。
現時点ですでに下層ディレクトリで動作するように修正したPull Requestが投げられておりテストもクリアしていますが、マージされていないようです。
https://github.com/docker-library/wordpress/pull/133/files
こちらを利用して、先ほどダウンロードしたdocker-entrypoint.sh
の26行目あたりにある
if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then
の直後に、以下のコードを追加します。
# allow use subdirectory in the wordpress site url and home configs, like: http://localhost/blog
if ! [ -z "$WORDPRESS_SUBDIRECTORY" ]; then
# force relative path
WORDPRESS_SUBDIRECTORY=`echo $WORDPRESS_SUBDIRECTORY | sed 's/^\///g'`
mkdir -p $WORDPRESS_SUBDIRECTORY
cd $WORDPRESS_SUBDIRECTORY
fi
WORDPRESS_SUBDICRECTORY
という環境変数が与えられていた場合、カレントディレクトリをサブディレクトリに変更していますね。
docker-entrypoint.sh
はシェルスクリプトで書かれていますので、シェルが苦手な方はコピペで済ませましょう。
ビルド
以上で必要な設定は完了しましたので、docker-compose up -d
を実行してイメージの作成〜コンテナの作成・実行をしてみましょう。
http://localhost:8000/blog/wp/にアクセスするとWordPressのインストール画面が起動されるはずです。
docker-entrypoint.sh
の読み込みにエラーが出る場合
環境によってはdocker-compose up -d
を実行した際に、以下のようなエラーが出る場合があります。
ERROR: for wordpress Cannot start service wordpress: oci runtime error: container_linux.go:247: starting container process caused "exec: \"docker-entrypoint.sh\": executable file not found in $PATH"
これはホスト(ローカル)のアクセス権によりコンテナからのアクセスが弾かれてしまっているために起きているようです。
sudo chmod +x docker-entrypoint.sh
とすることで、全てのユーザーに実行権限を与えます。
ビルドし直すとうまくゆくかと思います。
補足の説明とwp-config.php
を書き換えない設定
Docker公式のWordPressイメージはdocker-entrypoint.sh
によって、コンテナが起動されるたびに、ルートディレクトリにWordPressが設置されていなかった場合WordPressを展開しなおすようになっています。
今回紹介した設定をしないまま、コンテナを起動しルートに展開されたファイルを全てblog/wp
に移動すれば、blog/wp
に問題なくWordPressをインストールできますが、コンテナを再起動した際に再びルートにWordpressファイルが展開されてしまいます。
公式WordPressのdocker-entrypoint.sh
は、カレント(ルート)ディレクトリからの相対パスでindex.php
、wp-include/version.php
の有無を調べ、どちらかのファイルがない場合、WordPressを展開する設定になっています。
今回の修正したdocker-entrypoint.sh
では、カレントを変えることでindex.php
、wp-include/version.php
が正しく存在することを認識させています。
また、docker-entrypoint.sh
の他の部分で、コンテナ起動ごとにwp-config.php
を自動で書き換える(DBの設定をdocker-compose.yml
で設定したものへ書き換え)ようになっています。
個人的にはこの設定が邪魔だったので、151行目あたりの部分
set_config 'DB_HOST' "$WORDPRESS_DB_HOST"
set_config 'DB_USER' "$WORDPRESS_DB_USER"
set_config 'DB_PASSWORD' "$WORDPRESS_DB_PASSWORD"
set_config 'DB_NAME' "$WORDPRESS_DB_NAME"
を修正し
if [ ! -e wp-config.php ]; then
set_config 'DB_HOST' "$WORDPRESS_DB_HOST"
set_config 'DB_USER' "$WORDPRESS_DB_USER"
set_config 'DB_PASSWORD' "$WORDPRESS_DB_PASSWORD"
set_config 'DB_NAME' "$WORDPRESS_DB_NAME"
fi
とすることで、wp-config.php
がすでに存在する場合は書き換えを行わないように設定しています。
今回の修正を全て含めた最終的なdocker-entrypoint.sh
は以下の通りです。
#!/bin/bash
set -euo pipefail
# usage: file_env VAR [DEFAULT]
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ "${!var:-}" ]; then
val="${!var}"
elif [ "${!fileVar:-}" ]; then
val="$(< "${!fileVar}")"
fi
export "$var"="$val"
unset "$fileVar"
}
if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then
# allow use subdirectory in the wordpress site url and home configs, like: http://localhost/blog
if ! [ -z "$WORDPRESS_SUBDIRECTORY" ]; then
# force relative path
WORDPRESS_SUBDIRECTORY=`echo $WORDPRESS_SUBDIRECTORY | sed 's/^\///g'`
mkdir -p $WORDPRESS_SUBDIRECTORY
cd $WORDPRESS_SUBDIRECTORY
fi
if ! [ -e index.php -a -e wp-includes/version.php ]; then
echo >&2 "WordPress not found in $PWD - copying now..."
if [ "$(ls -A)" ]; then
echo >&2 "WARNING: $PWD is not empty - press Ctrl+C now if this is an error!"
( set -x; ls -A; sleep 10 )
fi
tar cf - --one-file-system -C /usr/src/wordpress . | tar xf -
echo >&2 "Complete! WordPress has been successfully copied to $PWD"
if [ ! -e .htaccess ]; then
# NOTE: The "Indexes" option is disabled in the php:apache base image
cat > .htaccess <<-'EOF'
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
EOF
chown www-data:www-data .htaccess
fi
fi
# TODO handle WordPress upgrades magically in the same way, but only if wp-includes/version.php's $wp_version is less than /usr/src/wordpress/wp-includes/version.php's $wp_version
# allow any of these "Authentication Unique Keys and Salts." to be specified via
# environment variables with a "WORDPRESS_" prefix (ie, "WORDPRESS_AUTH_KEY")
uniqueEnvs=(
AUTH_KEY
SECURE_AUTH_KEY
LOGGED_IN_KEY
NONCE_KEY
AUTH_SALT
SECURE_AUTH_SALT
LOGGED_IN_SALT
NONCE_SALT
)
envs=(
WORDPRESS_DB_HOST
WORDPRESS_DB_USER
WORDPRESS_DB_PASSWORD
WORDPRESS_DB_NAME
"${uniqueEnvs[@]/#/WORDPRESS_}"
WORDPRESS_TABLE_PREFIX
WORDPRESS_DEBUG
)
haveConfig=
for e in "${envs[@]}"; do
file_env "$e"
if [ -z "$haveConfig" ] && [ -n "${!e}" ]; then
haveConfig=1
fi
done
# linking backwards-compatibility
if [ -n "${!MYSQL_ENV_MYSQL_*}" ]; then
haveConfig=1
# host defaults to "mysql" below if unspecified
: "${WORDPRESS_DB_USER:=${MYSQL_ENV_MYSQL_USER:-root}}"
if [ "$WORDPRESS_DB_USER" = 'root' ]; then
: "${WORDPRESS_DB_PASSWORD:=${MYSQL_ENV_MYSQL_ROOT_PASSWORD:-}}"
else
: "${WORDPRESS_DB_PASSWORD:=${MYSQL_ENV_MYSQL_PASSWORD:-}}"
fi
: "${WORDPRESS_DB_NAME:=${MYSQL_ENV_MYSQL_DATABASE:-}}"
fi
# only touch "wp-config.php" if we have environment-supplied configuration values
if [ "$haveConfig" ]; then
: "${WORDPRESS_DB_HOST:=mysql}"
: "${WORDPRESS_DB_USER:=root}"
: "${WORDPRESS_DB_PASSWORD:=}"
: "${WORDPRESS_DB_NAME:=wordpress}"
# version 4.4.1 decided to switch to windows line endings, that breaks our seds and awks
# https://github.com/docker-library/wordpress/issues/116
# https://github.com/WordPress/WordPress/commit/1acedc542fba2482bab88ec70d4bea4b997a92e4
sed -ri -e 's/\r$//' wp-config*
if [ ! -e wp-config.php ]; then
awk '/^\/\*.*stop editing.*\*\/$/ && c == 0 { c = 1; system("cat") } { print }' wp-config-sample.php > wp-config.php <<'EOPHP'
// If we're behind a proxy server and using HTTPS, we need to alert WordPress of that fact
// see also http://codex.wordpress.org/Administration_Over_SSL#Using_a_Reverse_Proxy
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
EOPHP
chown www-data:www-data wp-config.php
fi
# see http://stackoverflow.com/a/2705678/433558
sed_escape_lhs() {
echo "$@" | sed -e 's/[]\/$*.^|[]/\\&/g'
}
sed_escape_rhs() {
echo "$@" | sed -e 's/[\/&]/\\&/g'
}
php_escape() {
php -r 'var_export(('$2') $argv[1]);' -- "$1"
}
set_config() {
key="$1"
value="$2"
var_type="${3:-string}"
start="(['\"])$(sed_escape_lhs "$key")\2\s*,"
end="\);"
if [ "${key:0:1}" = '$' ]; then
start="^(\s*)$(sed_escape_lhs "$key")\s*="
end=";"
fi
sed -ri -e "s/($start\s*).*($end)$/\1$(sed_escape_rhs "$(php_escape "$value" "$var_type")")\3/" wp-config.php
}
if [ ! -e wp-config.php ]; then
set_config 'DB_HOST' "$WORDPRESS_DB_HOST"
set_config 'DB_USER' "$WORDPRESS_DB_USER"
set_config 'DB_PASSWORD' "$WORDPRESS_DB_PASSWORD"
set_config 'DB_NAME' "$WORDPRESS_DB_NAME"
fi
for unique in "${uniqueEnvs[@]}"; do
uniqVar="WORDPRESS_$unique"
if [ -n "${!uniqVar}" ]; then
set_config "$unique" "${!uniqVar}"
else
# if not specified, let's generate a random value
currentVal="$(sed -rn -e "s/define\((([\'\"])$unique\2\s*,\s*)(['\"])(.*)\3\);/\4/p" wp-config.php)"
if [ "$currentVal" = 'put your unique phrase here' ]; then
set_config "$unique" "$(head -c1m /dev/urandom | sha1sum | cut -d' ' -f1)"
fi
fi
done
if [ "$WORDPRESS_TABLE_PREFIX" ]; then
set_config '$table_prefix' "$WORDPRESS_TABLE_PREFIX"
fi
if [ "$WORDPRESS_DEBUG" ]; then
set_config 'WP_DEBUG' 1 boolean
fi
TERM=dumb php -- <<'EOPHP'
<?php
// database might not exist, so let's try creating it (just to be safe)
$stderr = fopen('php://stderr', 'w');
// https://codex.wordpress.org/Editing_wp-config.php#MySQL_Alternate_Port
// "hostname:port"
// https://codex.wordpress.org/Editing_wp-config.php#MySQL_Sockets_or_Pipes
// "hostname:unix-socket-path"
list($host, $socket) = explode(':', getenv('WORDPRESS_DB_HOST'), 2);
$port = 0;
if (is_numeric($socket)) {
$port = (int) $socket;
$socket = null;
}
$user = getenv('WORDPRESS_DB_USER');
$pass = getenv('WORDPRESS_DB_PASSWORD');
$dbName = getenv('WORDPRESS_DB_NAME');
$maxTries = 10;
do {
$mysql = new mysqli($host, $user, $pass, '', $port, $socket);
if ($mysql->connect_error) {
fwrite($stderr, "\n" . 'MySQL Connection Error: (' . $mysql->connect_errno . ') ' . $mysql->connect_error . "\n");
--$maxTries;
if ($maxTries <= 0) {
exit(1);
}
sleep(3);
}
} while ($mysql->connect_error);
if (!$mysql->query('CREATE DATABASE IF NOT EXISTS `' . $mysql->real_escape_string($dbName) . '`')) {
fwrite($stderr, "\n" . 'MySQL "CREATE DATABASE" Error: ' . $mysql->error . "\n");
$mysql->close();
exit(1);
}
$mysql->close();
EOPHP
fi
# now that we're definitely done writing configuration, let's clear out the relevant envrionment variables (so that stray "phpinfo()" calls don't leak secrets from our code)
for e in "${envs[@]}"; do
unset "$e"
done
fi
exec "$@"
コメントを残す