1. Qiita
  2. 投稿
  3. docker

Docker Compose で PHP 7.0 の開発環境を構築する

  • 86
    いいね
  • 0
    コメント

PHP 7.0 の開発環境を構築するために書いた Docker Compose の設定ファイルをまとめました。

Docker について

Docker はコンテナと呼ばれる単位でアプリケーションを管理するソフトウェアです。

コンテナはサーバー OS にインストールされているソフトウェアと独立しているので、サーバーの環境を壊すことなく、PHP のバージョンアップや HTTP サーバーの切り替えを手軽に試すことができます。

Linux ディストリビューションのパッケージ管理ツールの apt-get や yum をより使いやすくしたものとして見ることができるでしょう。

オーバーヘッドが少ないので、さくら VPS、DigitalOcean や Vultr などの格安の VPS ホスティングサービスで使うことができます。

DigitalOcean と Vultr はコンテナの運用に特化した CoreOS が選択肢として提供しています。CoreOS はコンテナを運用するための最小限のソフトウェアで構成されており、アップデートが自動化されています。

さくら VPS で利用するには自分で OS のイメージをアップロードしてインストールする必要があります。筆者の調査では、2GB プランでの動作を確認しました。512MB プランではインストールできませんでした。

コンテナのもとになるイメージは Docker Hub、Quay.io 、Tutum などの Docker Registory に保存して、開発環境と運用環境のあいだで共有したり、不特定多数の人に向けて配布することができます。

Docker Compose について

Docker Compose は YAML の設定ファイルを通じて複数のコンテナを管理するためのコマンドツールです。複数の設定ファイルを組み合わせることができます。

Alpine Linux を導入すべきか?

Docker 社が Alpine Linux の開発者を雇用し、公式イメージを Ubuntu から Docker に切り替えることがメディアで報道されました (Publickey の2016年2月の記事)。Alpine Linux は 5MB の軽量ディストリビューションです。

Alpine Linux を採用するメリットは転送時間やビルドにかかる時間を改善できることです。デメリットはマイナー OS なので、公開されているイメージの数はかぎられていることです。PHP や nginx などの公式イメージの一部が Alpine Linux への対応を始めています。

docker-compose.yml の例はこちらの記事をご参照ください。Alpine Linux の Vagrant Box の導入に関してはこちらの記事をご参照ください。

検証環境

  • Max OS X Yosemite
  • Virtualbox 5.0.4
  • Vagrant 1.7.4
  • Docker 1.8.2
  • Ubuntu 14.04 LTS (Docker のホスト OS)

docker-compose

使い方

docker-compose を実行するには docker-compose.yml のあるディレクトリに移動します。イメージのビルドとコンテナを起動させるには次のコマンドを実行します。

docker-compose build
docker-compose up -d

--build オプションを指定することで、コンテナのビルドとサービスの起動を同時に実行することもできます。

docker-compose up -d --build

サービスを起動している間に修正した設定ファイルを反映させるために、--force-recreate を指定してコンテナを再生成することもできます。

docker-compose up -d --force-recreate

コンテナを再起動させるには restart、コンテナを停止させるには stop を実行します。

docker-compose restart
docker-compose stop

コンテナをすべて削除するには rm を実行します。本当に削除するかどうかの確認を省略する場合、-f もしくは --force オプションを指定します。

docker-compose rm -f
docker-compose rm --force

ターミナルの対話モード

PHP のバージョンを調べてみましょう。

docker run php:7.0-cli php -v

対話モードから PHP のバージョンを調べてみましょう。

docker run -it php:7.0-cli /bin/bash
php -v
exit

ターミナルから HTTP サーバーを起動させて、ホストマシンからアクセスすることもできます。

docker run -it -p 80:80 php:7.0-cli /bin/bash
cd home
echo test > index.html
php -S 0.0.0.0:80

今度は intl エクステンションをインストールしたイメージを用意してみましょう。

Dockerfile
FROM php:7.0-cli

RUN apt-get update && apt-get install -y --no-install-recommends \
        nano \
        libicu-dev \
    && rm -rf /var/lib/apt/lists/*

RUN docker-php-ext-install intl

イメージをビルドします。

docker build -t masakielastic/php:7.0-cli ./

対話モードで入って、intl モジュールが入っていることを確認しましょう。

docker run -it php:7.0-cli /bin/bash
php -m | grep intl
exit

docker-compose で対話モード

docker-compose のサブコマンドの run で対話モードに入ることができます。PHP を試してみましょう。

> docker-compose run myapp
> php -v
> exit
myapp:
  image: php:7.0-cli
  entrypoint: /bin/bash

PHP ビルトインサーバー

最初の練習として PHP のビルトインサーバーのコンテナをつくってみましょう。

docker-compose.yml
app:
  image: debian:jessie
  volumes:
    - ./www:/usr/share/php/html
  tty: true
php:
  image: php:7.0-cli
  expose:
    - "80"
  ports:
    - "80:80"
  volumes_from:
    - app
  working_dir: /usr/share/php/html
  command: php -S 0.0.0.0:80

docker-compose.yml と同じディレクトリに www フォルダーを用意し、HTML や PHP ファイルを設置します。すでに存在するイメージを使うので、docker-compose up -d を実行するだけで、コンテナが生成されます。

ターミナルからコマンドツールを使うことができます。

docker exec -it php_container_id php -v

コンテナに入って php コマンドを実行することもできます。

docker exec -it php_container_id bash
php -v

Apache HTTP サーバー

Apache HTTP Server は v2.4.17 で HTTP/2 (mod_http2) に対応しましたが、nghttp2 の公式パッケージ (0.6.7) が古いので、PPA (Ubuntu の場合、ppa:ondrej/apache2) を利用させてもらうかソースコードを自分でビルドする必要があります (2015年時点)。

mod_php

PHP 公式リポジトリのイメージを少し修正して、TLS/SSL を有効にしてみましょう。Debian の場合、ssl-cert パッケージをインストールすれば、自己証明書をつくる手間を省くことができます。

app:
  image: debian:jessie
  volumes:
    - ./www:/var/www/html
  tty: true
php:
  build: ./
  ports:
    - "80:80"
    - "443:443"
  volumes_from:
    - app
Dockerfile
FROM php:7.0-apache

RUN apt-get update && apt-get install -y ssl-cert \
  --no-install-recommends && rm -r /var/lib/apt/lists/*

RUN a2enmod ssl
EXPOSE 443

COPY ssl-default.conf /etc/apache2/sites-enabled/
ssl-default.conf
<IfModule mod_ssl.c>
    <VirtualHost _default_:443>
        DocumentRoot /var/www/html

        SSLEngine on
        SSLCertificateFile      /etc/ssl/certs/ssl-cert-snakeoil.pem
        SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
        SSLCACertificatePath /etc/ssl/certs/
    </VirtualHost>
</IfModule>

mod_proxy_fcgi (FastCGI)

Apache の公式リポジトリのイメージを使います。

docker-compose.yml
app:
  image: debian:jessie
  volumes:
    - ./www:/usr/local/apache2/htdocs
  tty: true
php:
  image: php:7.0-fpm
  ports:
    - "9000:9000"
  volumes_from:
    - app
apache:
  build: ./
  ports:
    - "80:80"
    - "443:443"
  volumes_from:
    - app
  links:
    - php

httpd の build で指定したディレクトリに Dockerfile を設置します。今回の場合、docker-compose.yml と同じディレクトリになります。

Dockerfile
FROM httpd:2.4
ENV DEBIAN_FRONTEND noninteractive

RUN apt-get update && apt-get install -y ssl-cert ca-certificates \
  --no-install-recommends && rm -r /var/lib/apt/lists/*

EXPOSE 443

RUN sed -i '/LoadModule proxy_module/s/^#//g' /usr/local/apache2/conf/httpd.conf
RUN sed -i '/LoadModule proxy_fcgi_module/s/^#//g' /usr/local/apache2/conf/httpd.conf
RUN sed -i '/LoadModule ssl_module/s/^#//g' /usr/local/apache2/conf/httpd.conf
RUN sed -i '/LoadModule socache_shmcb_module/s/^#//g' /usr/local/apache2/conf/httpd.conf

COPY ssl-default.conf /usr/local/apache2/conf/httpd-ssl.conf

RUN { \
  echo 'DirectoryIndex disabled'; \
  echo 'DirectoryIndex index.php index.html'; \
  echo '<FilesMatch \.php$>'; \
  echo '    SetHandler "proxy:fcgi://php:9000"'; \
  echo '</FilesMatch>'; \
  echo 'Include conf/httpd-ssl.conf'; \
} >> /usr/local/apache2/conf/httpd.conf
ssl-default.conf
<IfModule mod_ssl.c>
Listen 443

    <VirtualHost *:443>
        DocumentRoot /usr/local/apache2/htdocs

        SSLEngine on
        SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
        SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
        SSLCACertificatePath /etc/ssl/certs/
    </VirtualHost>
</IfModule>

nginx

nginx の設定ファイルを PHP 対応にするため、イメージをビルドします。

docker-compose.yml
app:
  image: debian:jessie
  volumes:
    - ./www:/usr/share/nginx/html
  tty: true
php:
  image: php:7.0-fpm
  ports:
    - "9000:9000"
  volumes_from:
    - app
nginx:
  build: ./
  ports:
    - "80:80"
    - "443:443"
  links:
    - php
  volumes_from:
    - app

docker-compose.yml と同じディレクトリに nginx のための Dockerfiledefault.conf を設置します。

Dockerfile
FROM nginx:1.9
COPY default.conf /etc/nginx/conf.d/default.conf
default.conf
server {
    listen       80;
    listen       443 ssl http2;

    server_name  localhost;

    ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
    ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;

  # intermediate configuration. tweak to your needs.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    root   /usr/share/nginx/html;
    index index.php;

    location ~ \.php$ {
        fastcgi_pass   php:9000;
        fastcgi_index  index.php;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

nginx 1.9.5 から HTTP/2 モジュールが利用できるようになっています。

h2o

2015年時点では h2o の公式リポジトリは存在しないので、自分でビルドする必要があります。

docker-compose.yml
app:
  image: debian:jessie
  volumes:
    - ./www:/usr/share/h2o/html
  tty: true
php:
  image: php:7.0-fpm
  expose:
    - "9000"
  volumes_from:
    - app
h2o:
  build: ./
  ports:
    - "80:80"
    - "443:443"
  volumes_from:
    - app
  links:
    - php
Dockerfile
FROM debian:jessie

ENV DEBIAN_FRONTEND noninteractive
ENV H2O_VERSION 1.5.0

RUN apt-get update && apt-get install -y ca-certificates \
    curl build-essential cmake openssl libssl-dev \
    libyaml-dev --no-install-recommends \
    && rm -rf /var/lib/apt/lists/*

RUN curl -L "https://github.com/h2o/h2o/archive/v$H2O_VERSION.tar.gz" -o h2o.tar.gz
RUN tar xfz h2o.tar.gz
RUN mv "h2o-$H2O_VERSION" /usr/src/h2o
RUN rm -r h2o.tar.gz

WORKDIR /usr/src/h2o
RUN cmake . && make h2o
RUN mv h2o /usr/local/bin

COPY h2o.conf /usr/src/h2o/h2o.conf

RUN mkdir -p /usr/share/h2o/html
WORKDIR /usr/share/h2o/html

EXPOSE 80 443
CMD h2o -c /usr/src/h2o/h2o.conf
h2o.conf
file.custom-handler:
  extension: .php
  fastcgi.connect:
    host: php
    port: 9000
    type: tcp
file.index:
  ["index.php", "index.html"]
hosts:
  "localhost":
    listen:
      port: 443
      ssl:
        certificate-file: /usr/src/h2o/examples/h2o/server.crt
        key-file: /usr/src/h2o/examples/h2o/server.key
    paths:
      "/":
        file.dir: /usr/share/h2o/html
  "localhost":
    listen:
      port: 80
    paths:
      "/":
        file.dir: /usr/share/h2o/html

MySQL

MySQL 5.7.0 から MySQL 5.7.10 までのバージョンを導入する場合、パスワードの有効期限に注意する必要があります。くわしくは「MySQL 5.7.4で導入されたdefault_password_lifetimeがじわじわくる」をご参照ください。

デフォルトの設定で使う例を示します。

docker-compose.yml
app:
  image: debian:jessie
  volumes:
    - ./www:/var/www/html
    - /var/lib/mysql
  tty: true
php:
  build: ./
  ports:
    - "80:80"
  links:
    - mysql
  volumes_from:
    - app
mysql:
  image: mysql:5.7
  environment:
    MYSQL_ROOT_PASSWORD: mypass
  volumes_from:
    - app
Dockerfile
FROM php:7.0-apache
RUN docker-php-ext-install pdo_mysql mysqli

文字コードの設定ファイルを追加するのであれば、mysql の Dockerfile は次のようになります。

FROM mysql:5.7

RUN { \
  echo '[mysqld]'; \
  echo 'character-set-server = utf8mb4'; \
  echo '[client]'; \
  echo 'default-character-set = utf8mb4'; \
} > /etc/mysql/conf.d/charset.cnf

コマンドツールから日本語を入力できるようにするには、ロケールパッケージのインストールと設定が必要になります。

RUN apt-get update && apt-get install -y nano locales \
  --no-install-recommends && rm -rf /var/lib/apt/lists/*

RUN dpkg-reconfigure locales && \
    locale-gen C.UTF-8 && \
    /usr/sbin/update-locale LANG=C.UTF-8

ENV LC_ALL C.UTF-8
ENV TERM xterm

MariaDB

MySQL とほとんど同じですが、コマンドツールを使うために、環境変数の TERM を設定する必要があります。

docker-compose.yml
app:
  image: debian:jessie
  volumes:
    - ./www:/var/www/html
    - /var/lib/mysql
  tty: true
php:
  build: ./
  ports:
    - "80:80"
  links:
    - mariadb
  volumes_from:
    - app
mariadb:
  image: mariadb:10.0
  environment:
    MYSQL_ROOT_PASSWORD: mypass
    TERM: xterm
  volumes_from:
    - app
Dockerfile
FROM php:7.0-apache
RUN docker-php-ext-install pdo_mysql mysqli

redis

PECL モジュールを使うことを前提とします。2015年9月時点では PHP 7 対応の正式リリースが存在しないため、php7 のブランチを使う必要があります。

docker-compose.yml
app:
  image: debian:jessie
  volumes:
    - ./www:/var/www/html
  tty: true
php:
  build: ./
  ports:
    - "80:80"
  volumes_from:
    - app
  links:
    - redis
redis:
  image: redis:3.0

セッションハンドラに redis を使うには、次のようになります。

Dockerfile
FROM php:7.0-apache
ENV DEBIAN_FRONTEND noninteractive
ENV PHPREDIS_VERSION php7

RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/$PHPREDIS_VERSION.tar.gz \
    && tar xfz /tmp/redis.tar.gz \
    && rm -r /tmp/redis.tar.gz \
    && mv phpredis-$PHPREDIS_VERSION /usr/src/php/ext/redis \
    && docker-php-ext-install redis

RUN { \
  echo 'session.save_handler = redis'; \
  echo 'session.save_path = tcp://redis:6379'; \
} >> /usr/local/etc/php/conf.d/docker-php-ext-redis.ini

memcached

2015年9月の時点では PHP 7.0 対応の公式リリースがないため、php7 のブランチを使う必要があります。

docker-compose.yml
app:
  image: debian:jessie
  volumes:
    - ./www:/var/www/html
  tty: true
php:
  build: ./
  ports:
    - "80:80"
  volumes_from:
    - app
  links:
    - memcached
memcached:
  image: memcached:1.4

セッションハンドラに memcached を使うには、次のようになります。

Dockerfile
FROM php:7.0-apache
ENV DEBIAN_FRONTEND noninteractive
ENV PHP_MEMCACHED_VERSION php7

RUN apt-get update && apt-get install -y libmemcached-dev zlib1g-dev

RUN curl -L -o /tmp/memcached.tar.gz https://github.com/php-memcached-dev/php-memcached/archive/$PHP_MEMCACHED_VERSION.tar.gz \
    && tar xfz /tmp/memcached.tar.gz \
    && rm -r /tmp/memcached.tar.gz \
    && mv php-memcached-$PHP_MEMCACHED_VERSION /usr/src/php/ext/memcached \
    && docker-php-ext-install memcached

RUN { \
  echo 'session.save_handler = memcached'; \
  echo 'session.save_path = memcached:11211'; \
} >> /usr/local/etc/php/conf.d/docker-php-ext-memcached.ini

その他

公式イメージの buildpack-deps を使う

Debian/Ubuntu の公式イメージを使う場合、ビルドツールやバージョン管理ツールがあらかじめインストールされた buildpack-deps を代わりに使う選択肢があります。

コンテナの再起動

OS を再起動させたときにコンテナも再起動させるには restart:always を指定します。

docker-compose.yml
restart: always

データボリュームコンテナの選択肢

連携させるコンテナと同じ OS を使うようにしています。異なる OS を使う場合、パーミッションとオーナーが異なるためにデータが正常に生成されなかったり、読み込みができないことがあります (Data-only container madness)。

PECL のインストール

シェルスクリプトの docker-php-ext-install が用意されています。

RUN docker-php-ext-install mbstring

Bash の日本語対応

RUN apt-get update && apt-get install -y nano locales \
  --no-install-recommends && rm -rf /var/lib/apt/lists/*

RUN dpkg-reconfigure locales && \
    locale-gen C.UTF-8 && \
    /usr/sbin/update-locale LANG=C.UTF-8

ENV LC_ALL C.UTF-8
ENV TERM xterm

HTTP サーバーの SSL 設定

Apache や nginx の場合、Mozilla の SSL 設定ジェネレーターを使うとよいでしょう。Apache 2.4.10、OpenSSL 1.0.1k を対象に生成した設定ファイルは次のとおりです。

ssl-default.conf
<IfModule mod_ssl.c>
    <VirtualHost _default_:443>
        DocumentRoot /var/www/html

        SSLEngine on
        SSLCertificateFile      /etc/ssl/certs/ssl-cert-snakeoil.pem
        SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
        SSLCACertificatePath /etc/ssl/certs/

        # intermediate configuration, tweak to your needs
        SSLProtocol             all -SSLv3
        SSLCipherSuite          ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
        SSLHonorCipherOrder     on
        SSLCompression          off

        # OCSP Stapling, only in httpd 2.3.3 and later
        SSLUseStapling          on
        SSLStaplingResponderTimeout 5
        SSLStaplingReturnResponderErrors off

        # OCSP Stapling, only in httpd 2.3.3 and later
        SSLUseStapling          on
        SSLStaplingResponderTimeout 5
        SSLStaplingReturnResponderErrors off

        # HSTS (mod_headers is required) (15768000 seconds = 6 months) 
        Header always set Strict-Transport-Security "max-age=15768000"
    </VirtualHost>
</IfModule>

SSLStaplingCache        shmcb:/var/run/ocsp(128000)

nginx の場合、次のようになります。

default.conf
server {
    listen       443 ssl http2;

    ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
    ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;

    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
    ssl_prefer_server_ciphers on;

    # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
    add_header Strict-Transport-Security max-age=15768000;

    # OCSP Stapling ---
    # fetch OCSP records from URL in ssl_certificate and cache them
    # ssl_stapling on;
    # ssl_stapling_verify on;

    ## verify chain of trust of OCSP response using Root CA and Intermediate certs
    # ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;

    resolver 8.8.8.8;
}

ビルドしたイメージを Docker Hub にプッシュする

ログインした状態で docker push を実行します。

docker login
docker push masakielastic/h2o