CentOS7 + Apache + PHP7 (Composer, Xdebug) の開発環境を Docker で作る

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 は認識されない

.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 に渡す環境変数。デフォルトでは 808080, 4043443 にポートフォワーディングしています。

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.ymlDockerfile からここに逃がしました。

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 にアクセス。

PHPのサンプルプログラムが動作することを確認
PHPのサンプルプログラムが動作することを確認

アクセスできますね。

ちなみに

# 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 の環境変数

docker-compose

docker-compose からビルド中は環境変数 ( env_file, enviroment ) は認識されない

ARG による渡しと entrypoint で回避。

docker-compose からビルド中は volumes は認識されない

entrypoint 実行時は認識されるので、 Dockerfile で RUN せずに entrypoint で bash 実行することで回避。

docker-compose → Dockerfile の変数の渡し方

sed

lvh.me

lvh.me (サブドメイン含む) は 127.0.0.1 で名前解決できるように提供されているので、開発環境にドメインを振りたい場合は利用可能。便利。

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。