Symfony 2.7が動くDockerイメージを作ってみた


Symfony 2.7を使って開発しているシステムがある。今はVagrantで自作したCentOS 7.1のボックスを使って開発環境を構築しているが、それをDockerに置き換えるにはどのようにすればいいのかと思って、試しにDockerイメージを作ってみた。

試した環境はmacOS SierraのDocker for Mac。

```
$ docker version
Client:
 Version:      17.03.1-ce
 API version:  1.27
 Go version:   go1.7.5
 Git commit:   c6d412e
 Built:        Tue Mar 28 00:40:02 2017
 OS/Arch:      darwin/amd64

Server:
 Version:      17.03.1-ce
 API version:  1.27 (minimum version 1.12)
 Go version:   go1.7.5
 Git commit:   c6d412e
 Built:        Fri Mar 24 00:00:50 2017
 OS/Arch:      linux/amd64
 Experimental: true
```

# Dockerfile


```Dockerfile
FROM centos:7.3.1611

# UPDATE & EPEL
RUN yum -y update && yum install -y epel-release

# LANG
RUN localedef -v -c -i ja_JP -f UTF-8 ja_JP.UTF-8 || true
RUN unlink /etc/localtime && ln -s /usr/share/zoneinfo/Japan /etc/localtime
ENV LANG ja_JP.UTF-8

# Oracle Instant Client
COPY files/usr/local/src/oracle-instantclient /usr/local/src/oracle-instantclient/
RUN yum install -y \
        /usr/local/src/oracle-instantclient/oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm \
        /usr/local/src/oracle-instantclient/oracle-instantclient12.1-devel-12.1.0.2.0-1.x86_64.rpm \
        /usr/local/src/oracle-instantclient/oracle-instantclient12.1-jdbc-12.1.0.2.0-1.x86_64.rpm \
        /usr/local/src/oracle-instantclient/oracle-instantclient12.1-odbc-12.1.0.2.0-1.x86_64.rpm \
        /usr/local/src/oracle-instantclient/oracle-instantclient12.1-sqlplus-12.1.0.2.0-1.x86_64.rpm
COPY files/etc/profile.d/oracle.sh /etc/profile.d/oracle.sh
COPY files/etc/ld.so.conf.d/oracle12c.conf /etc/ld.so.conf.d/oracle12c.conf

# PHP
RUN yum install -y http://rpms.famillecollet.com/enterprise/remi-release-7.rpm \
 && yum install -y --enablerepo="remi,remi-php56" \
        php \
        php-intl \
        php-mbstring \
        php-oci8 \
        php-opcache \
        php-pdo \
        php-process \
        php-xml
COPY files/etc/php.d/*.ini /etc/php.d/

# CLEAN UP
RUN yum clean all

# USER/GROUP
RUN groupadd -r symfony && useradd -r -g symfony symfony
USER symfony

# RUN
WORKDIR /app
CMD ["bin/docker-run.sh"]
```

## ディレクトリ構成

```
$ tree . 
.
├── Dockerfile
└── files
    ├── etc
    │   ├── ld.so.conf.d
    │   │   └── oracle12c.conf
    │   ├── php.d
    │   │   ├── 00-timezone.ini
    │   │   └── 20-mbstring.ini
    │   └── profile.d
    │       └── oracle.sh
    └── usr
        └── local
            └── src
                └── oracle-instantclient
                    ├── oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm
                    ├── oracle-instantclient12.1-devel-12.1.0.2.0-1.x86_64.rpm
                    ├── oracle-instantclient12.1-jdbc-12.1.0.2.0-1.x86_64.rpm
                    ├── oracle-instantclient12.1-odbc-12.1.0.2.0-1.x86_64.rpm
                    └── oracle-instantclient12.1-sqlplus-12.1.0.2.0-1.x86_64.rpm

9 directories, 10 files
```

## 解説

```
FROM centos:7.3.1611
```

ベースイメージはCentOS 7.3。最初は今の環境に合わせてCentOS 7.1にしたが、`yum update`の実行で失敗したので7.3に変更した。

```
# UPDATE & EPEL
RUN yum -y update && yum install -y epel-release
```

パッケージを最新にし、EPELを利用できるようにする。

```
# LANG
RUN localedef -v -c -i ja_JP -f UTF-8 ja_JP.UTF-8 || true
RUN unlink /etc/localtime && ln -s /usr/share/zoneinfo/Japan /etc/localtime
ENV LANG ja_JP.UTF-8
```

言語設定を日本語に変更。`localedef`コマンドでは終了コードが0にならなかったため、`true`コマンドを実行して0になるようにした。

2行目のRUNでtimezoneをAsia/Tokyoに変更し、環境変数LANGをja_JP.UTF-8に変更する。

```
# Oracle Instant Client
COPY files/usr/local/src/oracle-instantclient /usr/local/src/oracle-instantclient/
RUN yum install -y \
        /usr/local/src/oracle-instantclient/oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm \
        /usr/local/src/oracle-instantclient/oracle-instantclient12.1-devel-12.1.0.2.0-1.x86_64.rpm \
        /usr/local/src/oracle-instantclient/oracle-instantclient12.1-jdbc-12.1.0.2.0-1.x86_64.rpm \
        /usr/local/src/oracle-instantclient/oracle-instantclient12.1-odbc-12.1.0.2.0-1.x86_64.rpm \
        /usr/local/src/oracle-instantclient/oracle-instantclient12.1-sqlplus-12.1.0.2.0-1.x86_64.rpm
COPY files/etc/profile.d/oracle.sh /etc/profile.d/oracle.sh
COPY files/etc/ld.so.conf.d/oracle12c.conf /etc/ld.so.conf.d/oracle12c.conf
```

DBがOracleであるため、Instant Clientのインストールと設定を行う。

```
$ cat files/etc/profile.d/oracle.sh
export ORACLE_HOME=/usr/lib/oracle/12.1/client64
export NLS_LANG=JAPANESE_JAPAN.AL32UTF8

export PATH=${PATH+"$PATH:"}$ORACLE_HOME/bin
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH+"$LD_LIBRARY_PATH:"}$ORACLE_HOME/lib
```

`files/etc/profile.d/oracle.sh`は、LinuxアカウントにORACLE_HOMEなどの環境変数を設定するためのスクリプト。

```
$ cat files/etc/ld.so.conf.d/oracle12c.conf
/usr/lib/oracle/12.1/client64/lib
```

`/etc/ld.so.conf.d/oracle12c.conf`は、Instant Clientの共有ライブラリを追加する設定ファイル。

```
# PHP
RUN yum install -y http://rpms.famillecollet.com/enterprise/remi-release-7.rpm \
 && yum install -y --enablerepo="remi,remi-php56" \
        php \
        php-intl \
        php-mbstring \
        php-oci8 \
        php-opcache \
        php-pdo \
        php-process \
        php-xml
COPY files/etc/php.d/*.ini /etc/php.d/
```

phpはremiのパッケージを使用する。remiのパッケージを使用するにはEPELが必要であるため、先にEPELをインストールした。

```
$ cat files/etc/php.d/00-timezone.ini
date.timezone = Asia/Tokyo
```

```
$ cat files/etc/php.d/20-mbstring.ini
; Enable mbstring extension module
extension=mbstring.so

mbstring.language=Japanese
```

`/etc/php.d/`にコピーするファイルはtimezoneを設定する`00-timezone.ini`とmbstringの設定をする`20-mbstring.ini`の2つ。


```
# CLEAN UP
RUN yum clean all
```

以後は`yum`を使用しないので、キャッシュをクリア。

```
# USER/GROUP
RUN groupadd -r symfony && useradd -r -g symfony symfony
USER symfony
```

ユーザを追加して、以後のコマンドを所定のアカウントで実行するように変更する。

```
# RUN
WORKDIR /app
CMD ["bin/docker-run.sh"]
```

作業ディレクトリを設定し、コンテナ起動時に所定のスクリプトを実行するようにする。

このDockerfileを使って、Symfony 2.7のプロジェクトが実行できるイメージを作成する。

```
docker build -t symfony27 ./
```

## 確認

symfonyコマンドで2.7のプロジェクトを作成する。

```
symfony new symfony27-test 2.7
```

コンテナで実行するスクリプトを作成し、実行可能にする。

```
cat <<''EOT' > symfony27-test/bin/docker-run.sh
#!/bin/sh
set -eu

php app/console server:run 0.0.0.0:8000
EOT
```

```
chmod 755 symfony27-test/bin/docker-run.sh
```

dockerコンテナを起動する。

```
docker run -d -p 8000:8000 -v $(pwd)/symfony27-test:/app symfony27
```

ブラウザでlocalhost:8000にアクセスしてページが表示されることを確認する。

# 試してみて

少し試してみて気になった点を2つ。

ひとつはキャッシュのクリア。今までのように

```
php app/console cache:clear
```

コマンドを使ってキャッシュをクリアすると、コマンドを実行した環境の絶対パスを含むキャッシュが作成されるようで、Symfonyのプロファイラでログを確認すると

```
ERROR - The template "::base.html.twig" contains an error: Warning: file_get_contents(/Users/te2u/Inbox/docker/proj/symfony27-test/app/Resources/views/base.html.twig): failed to open stream: No such file or directory
```

ファイルが見つからないというエラーになる。

これを解決するには

```
rm -rf app/cache/dev
```

ディレクトリを直接削除するか

```
php app/console cache:clear --no-warmup
```

キャッシュクリアで`no-warmup`オプションをつけて実行する。

もうひとつはMacでの実行の遅さ。

どうやらMacの環境でVolumeを設定して実行すると遅くなるらしく、それを解決するのにdocker-syncがある。ただ今回は、Dockerのイメージを作ってみることが目的だったので、こちらは試していない。

# 参考

- [dockerのコンテナ実行時にLANGなどの環境変数を設定する方法 - Qiita](http://qiita.com/yuki2006/items/6cea8c352e38f047b52a)
- [日本語環境のCentOS(Docker image)をDockerfileで書いてみた - Qiita](http://qiita.com/OHAS/items/1cc3caa8065787e8f34f)
- [Docker for Macが遅い問題をdocker-syncで解決する - Cluex Developersブログ](http://cluex-developers.hateblo.jp/entry/2016/11/02/122633)
- [Docker入門 | 末安 泰三 | OS | Kindleストア | Amazon](http://amzn.asia/4WMENvp)
- [Docker | Adrian Mouat, Sky株式会社 玉川 竜司 |本 | 通販 | Amazon](http://amzn.asia/fm6qo2B)