XAMPP 以外の PHP 開発環境の選択肢を持ちたかったので Docker で構築することにしました。
Docker で手動で手順を確認しつつ Apache にアクセスできるようになるまで (Failed to get D-Bus connection: Operation not permitted 対処含む)で一度頓挫しかけたのですが、その後対処方法が見付かったので CentOS7 ベースで続行です。
構成
ディレクトリ構造
今回は以下のような構成にしました。
PROJECT_ROOT/
├ apache/
│ ├ confd/ // /etc/httpd/conf.d にマウント
│ ├ docker/
│ │ └ Dockerfile // Apache 用 Dockerfile
│ │
│ ├ log/ // /var/log/httpd/ にマウント
│ └ www // /var/www/<WEB_ROOT_DIRECTORY>/web/ にマウント
│
├ cert/ // /etc/ssl/private/ にマウント。SSL証明書格納用
├ php/
│ ├ error_log/ // /var/log/php にマウント
│ └ php.ini // /etc/php.ini にマウント
│
├ template/
│ ├ apache_vh.conf // 仮想サイトのディレクティブ設定を記述した conf ファイル
│ ├ apache_vh_ssl.conf // 上同。SSL版
│ ├ php.conf // /etc/httpd/conf.d/php.conf を上書きするためのテンプレート
│ └ ssl.conf // /etc/httpd/conf.d/ssl.conf を上書きするためのテンプレート
│
├ workspace/
│ └ entrypoint.sh // エントリポイント
├ .env // docker-compose.yml 用の環境変数
└ docker-compose.yml
docker-compose.yml
version: '3.8'
services:
web:
build:
context: ./apache/docker
dockerfile: Dockerfile
args:
WEB_ROOT_DIRECTORY: $WEB_ROOT_DIRECTORY
WEB_DOMAIN: $WEB_DOMAIN
WEB_HOST_PORTNUM: $WEB_HOST_PORTNUM
WEB_CONTAINER_PORTNUM: $WEB_CONTAINER_PORTNUM
WEB_HOST_PORTSSL: $WEB_HOST_PORTSSL
WEB_CONTAINER_PORTSSL: $WEB_CONTAINER_PORTSSL
labels:
lamp.docker.php: "Docker PHP"
volumes:
# workspace
- ./workspace:/workspace
# docker settings template
- ./template:/template
# apache conf.d
- ./apache/confd:/etc/httpd/conf.d
# apache log
- ./apache/log:/var/log/httpd
# apache virtual host
- ./apache/www:/var/www/$WEB_ROOT_DIRECTORY/web
# php ini
- ./php/php.ini:/etc/php.ini
# error log
- ./php/error_log:/var/log/php
# SSL
- ./cert:/etc/ssl/private/
tty: true
ports:
- "$WEB_HOST_PORTNUM:$WEB_CONTAINER_PORTNUM"
- "$WEB_HOST_PORTSSL:$WEB_CONTAINER_PORTSSL"
entrypoint: bash -c "bash /workspace/entrypoint.sh $WEB_ROOT_DIRECTORY $WEB_DOMAIN $WEB_HOST_PORTNUM $WEB_CONTAINER_PORTNUM $WEB_HOST_PORTSSL $WEB_CONTAINER_PORTSSL && /bin/bash"
args
:WEB_ROOT_DIRECTORY
,WEB_DOMAIN
,WEB_HOST_PORTNUM
,WEB_CONTAINER_PORTNUM
,WEB_HOST_PORTSSL
,WEB_CONTAINER_PORTSSL
は.env
で記述したものvolumes
: 各種ボリュームをマウント。構成にも記述しましたが以下の通り。./workspace
:/workspace
( Dockerfile でmkdir
)./template
:/template
( Dockerfile でmkdir
)./apache/confd
:/etc/httpd/conf.d
./apache/log
:/var/log/httpd
./apache/www
:/var/www/<WEB_ROOT_DIRECTORY>/web
仮想サイトのドキュメントルート。<WEB_ROOT_DIRECTORY>
は上述args
(.env
)で指定した値になります。./php/php.ini
:/etc/php.ini
./php/error_log
:/var/log/php
(php.ini
で指定。 PHP のみ Apache とは別ファイルにエラーを蓄積するように設定)./cert
:/etc/ssl/private/
ports
: ホストのポートとコンテナのポートを対応させています。それぞれargs
(.env
)で指定。entrypoint
: エントリポイント。以下の2つの仕様のため、それに関わる設定はエントリポイントに退避させることにしています。- docker-compose からビルドしている中は環境変数 (
env_file
,enviroment
) は認識されない - docker-compose からビルド中は
volumes
は認識されない
- docker-compose からビルドしている中は環境変数 (
.env
WEB_ROOT_DIRECTORY=sample_site
WEB_DOMAIN=lvh.me
WEB_HOST_PORTNUM=8080
WEB_CONTAINER_PORTNUM=80
WEB_HOST_PORTSSL=4043
WEB_CONTAINER_PORTSSL=443
docker-compose に渡す環境変数。デフォルトでは 8080
→80
, 4043
→443
にポートフォワーディングしています。
apache/docker/Dockerfile
FROM centos:centos7
# args
ARG WEB_ROOT_DIRECTORY
ARG WEB_DOMAIN
ARG WEB_HOST_PORTNUM
ARG WEB_CONTAINER_PORTNUM
ARG WEB_HOST_PORTSSL
ARG WEB_CONTAINER_PORTSSL
# timezone
RUN cp -p -f /usr/share/zoneinfo/Japan /etc/localtime
# yum install
RUN yum -y update && yum -y install \
epel-release \
sudo \
less \
# network ss (instaed of netstat)
iproute \
# apache
httpd-devel \
# zip
zip \
unzip \
# SSL
openssl \
mod_ssl
# add last
RUN echo ServerName www.example.com:${WEB_CONTAINER_PORTNUM} >> /etc/httpd/conf/httpd.conf
# RUN echo ServerTokens Prod >> /etc/httpd/conf/httpd.conf
# rpm
RUN rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm && \
rpm --import http://rpms.famillecollet.com/RPM-GPG-KEY-remi2021
# enable repository remi & remi-php74
RUN yum-config-manager --enable remi && yum-config-manager --enable remi-php74
# php
RUN yum -y install php php-devel php-pdo php-mysqlnd php-mbstring php-gd php-pear php-pecl-apc-devel zlib-devel php-xml php-mcrypt php-pecl-xdebug
# disable repository remi & remi-php74
RUN yum-config-manager --disable remi && yum-config-manager --disable remi-php74
# composer
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php composer-setup.php \
&& php -r "unlink('composer-setup.php');" \
&& mv composer.phar /usr/local/bin/composer
# php log
RUN mkdir /var/log/php
RUN chown apache /var/log/php
RUN chmod 755 /var/log/php
# SSL
RUN mkdir -p /etc/ssl/private
# volume directory
RUN mkdir /template
RUN mkdir /var/www/${WEB_ROOT_DIRECTORY}
RUN mkdir /var/www/${WEB_ROOT_DIRECTORY}/web
RUN mkdir /workspace
Apache, PHP に関わる部分を設定。 Composer, Xdebug も同梱です。リポジトリの切り替えにはCentOS で リポジトリの enable/disabled を切り替える方法を使用。
php/php.ini
事前に PHP7.4 環境からコピーしてきた ini ファイルをベースにしています。
以下、変更を加えたり、確認すべきヶ所のみ記載します。
;max_execution_time = 30
max_execution_time = 1200
最大実行時間を延長。開発環境なので。
;memory_limit = 128M
memory_limit = 4G
;post_max_size = 8M
post_max_size = 16G
;upload_max_filesize = 2M
upload_max_filesize = 16M
メモリ上限、ファイルアップロードサイズ等の上限を引き上げ。
;error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
error_reporting = E_ALL
;error_log = syslog
error_log = "/var/log/php/php_errors.log"
エラーログ周り。
;date.timezone =
date.timezone = 'Asia/Tokyo'
;mbstring.language = Japanese
mbstring.language = "Japanese"
;mbstring.internal_encoding =
mbstring.internal_encoding = "UTF-8"
タイムゾーン・ロケール・文字コード。
; XDEBUG ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
xdebug.remote_enable=1
; 192.168.1.2 ...
xdebug.remote_host=host.docker.internal
xdebug.remote_port=9000
xdebug.remote_handler="dbgp"
Xdebug 用設定。
template/apache_vh.conf
<VirtualHost *:WEB_CONTAINER_PORTNUM>
DocumentRoot "/var/www/WEB_ROOT_DIRECTORY/web"
ServerName www.WEB_DOMAIN
ServerAlias WEB_DOMAIN
ScriptAlias /cgi-bin/ /var/www/WEB_ROOT_DIRECTORY/web/cgi-bin/
RewriteEngine on
RewriteCond %{HTTP_HOST} ^WEB_DOMAIN$
RewriteRule ^(.*)$ http://www.WEB_DOMAIN/$1 [R=301,L]
<Directory "/var/www/WEB_ROOT_DIRECTORY/web">
allow from all
AllowOverride All
Options FollowSymLinks
Require all granted
</Directory>
# RewriteEngine on
# RewriteCond %{SERVER_NAME} =www.WEB_DOMAIN
# RewriteRule ^ https://%{SERVER_NAME}:WEB_HOST_PORTSSL%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
http 接続用のディレクティブのテンプレート。各種変数は args
経由で渡ってきます。
template/apache_vh_ssl.conf
<IfModule mod_ssl.c>
<VirtualHost *:WEB_CONTAINER_PORTSSL>
DocumentRoot "/var/www/WEB_ROOT_DIRECTORY/web"
ServerName www.WEB_DOMAIN
ServerAlias WEB_DOMAIN
ScriptAlias /cgi-bin/ /var/www/WEB_ROOT_DIRECTORY/web/cgi-bin/
RewriteEngine on
RewriteCond %{HTTP_HOST} ^WEB_DOMAIN$
RewriteRule ^(.*)$ https://www.WEB_DOMAIN/$1 [R=301,L]
<Directory "/var/www/WEB_ROOT_DIRECTORY/web">
allow from all
AllowOverride All
Options FollowSymLinks
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile /etc/ssl/private/server.crt
SSLCertificateKeyFile /etc/ssl/private/server.key
</VirtualHost>
</IfModule>
https 接続用のディレクティブのテンプレート。
template/php.conf, template/ssl.conf
特に変更なし。ディレクティブ単位でマウントする関係で退避させておき、上述の仮想サイトディレクティブの conf ファイルと同じタイミングでコピーして適用させるためのものです。
workspace/entrypoint.sh
#!/bin/bash
# gen key & certificate
openssl req -new -newkey rsa:2048 -nodes -out /etc/ssl/private/server.csr -keyout /etc/ssl/private/server.key -subj "/C=/ST=/L=/O=/OU=/CN=*.lvh.me"
openssl x509 -days 365 -req -signkey /etc/ssl/private/server.key -in /etc/ssl/private/server.csr -out /etc/ssl/private/server.crt
# setting file replace and copy
sed -e "s/WEB_ROOT_DIRECTORY/${1}/gi" -e "s/WEB_DOMAIN/${2}/gi" -e "s/WEB_HOST_PORTNUM/${3}/gi" -e "s/WEB_CONTAINER_PORTNUM/${4}/gi" -e "s/WEB_HOST_PORTSSL/${5}/gi" -e "s/WEB_CONTAINER_PORTSSL/${6}/gi" /template/apache_vh.conf > /etc/httpd/conf.d/${1}.conf
sed -e "s/WEB_ROOT_DIRECTORY/${1}/gi" -e "s/WEB_DOMAIN/${2}/gi" -e "s/WEB_HOST_PORTNUM/${3}/gi" -e "s/WEB_CONTAINER_PORTNUM/${4}/gi" -e "s/WEB_HOST_PORTSSL/${5}/gi" -e "s/WEB_CONTAINER_PORTSSL/${6}/gi" /template/apache_vh_ssl.conf > /etc/httpd/conf.d/${1}_ssl.conf
cp /template/php.conf /etc/httpd/conf.d/php.conf
cp /template/ssl.conf /etc/httpd/conf.d/ssl.conf
# Apache start
/usr/sbin/httpd -DFOREGROUND &
docker-compose で述べましたが、ビルド中は環境変数やボリュームが認識できないので、それらを使用したり操作したりする処理は docker-compose.yml
や Dockerfile
からここに逃がしました。
systemctl
は結局使えませんでしたが、エントリポイントの最後(仮想サイトの conf ファイルを配置して Apache 起動時にエラーにならない状態になった後)のタイミングで /usr/sbin/httpd -DFOREGROUND &
により Apache を起動しています。
検証
この構成で
> docker-compose up -d
します。コンテナが起動したら、
> cd /apache/www/
> git clone https://github.com/arm-band/test_call_throw_salt.git .
として PHPUnit + Xdebug の検証のサンプルプロジェクトをクローンしてきます。
> cd ../../
> docker-compose exec web /bin/bash
続いてコンテナに入り、
# cd /var/www/sample_site/web
# composer install
Composer が動くことを確認しつつビルド。
プロジェクトの設定などが完了したら https://lvh.me:4043
にアクセス。
アクセスできますね。
ちなみに
# composer test:coverage
とした後に https://lvh.me:4043/coverage/
にアクセス。
テストカバレッジが表示されました。OKです。
これでひとまず CentOS7 ベースで PHP 環境が作れました。
今後の課題としては以下でしょうか。
- PHP のバージョン切り替え
php.ini
をどうするのか、といった課題はありますが、なるべく PHP のバージョンは.env
等で軽率に切り替えられるようにしたいですね
- MySQL
- 今回は PHP のみなので、 MySQL も併せて使用できるようにしたいですね
参考
CentOS7ベース, PHP, Xdebug, SSL
CentOS7ベース, PHP, Xdebug, 環境変数利用 (MySQL, phpMyAdmin)
CentOS7ベース, PHP, Xdebug (MySQL)
CentOS7ベース, PHP (php-fpm, 公式イメージ), Xdebug
PHP (公式イメージ), 変数使用
PHP (公式イメージ), SSL
PHP (公式イメージ)
Docker の環境変数
- Compose における環境変数 — Docker-docs-ja 19.03 ドキュメント
- Compose ファイル・リファレンス — Docker-docs-ja 19.03 ドキュメント
- Dockerfile:環境変数の定義とその周辺 – pyてよn日記
docker-compose
docker-compose からビルド中は環境変数 ( env_file, enviroment ) は認識されない
- docker-composeのビルド中に環境変数が認識されない – Qiita
- dockerで初回起動時のみ特定の処理を行うヘルパースクリプト(docker-entrypoint.sh) – Qiita
- 【Docker】ENTRYPOINT と CMD の仕組みと小技 – 株式会社シーポイントラボ | 浜松のシステム・RTK-GNSS開発
ARG による渡しと entrypoint で回避。
docker-compose からビルド中は volumes は認識されない
entrypoint 実行時は認識されるので、 Dockerfile で RUN せずに entrypoint で bash 実行することで回避。
docker-compose → Dockerfile の変数の渡し方
sed
lvh.me
- 【Rails】ローカル環境の開発でサブドメインがある場合「localhost」ではなく「lvh.me」を使う – FujiYasuの日記
- lvh.me ドメインを使って、サブドメイン形式の WordPress マルチサイトの Docker 開発環境構築をしよう|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社
lvh.me
(サブドメイン含む) は 127.0.0.1
で名前解決できるように提供されているので、開発環境にドメインを振りたい場合は利用可能。便利。