EC-CUBE４系の公式リポジトリにDockerfileが追加されたので実際に使ってみた

EC-CUBEのVer4.0.3から公式リポジトリに**Dockerfile**が追加されていたので実際に使ってみました。

なお。ここに書いている情報はDockerを使い始めてまだ３か月の初学者が勉強を兼ねて執筆したものなので、間違いなどがあればコメント欄でご指摘いただければ幸いです。

以下のリンク先はEC-CUBE4系公式リポジトリのDockerfileになります。
[ec-cube/Dockerfile at 4.0 · EC-CUBE/ec-cube - GitHub](https://github.com/EC-CUBE/ec-cube/blob/4.0/Dockerfile)


# 実際に動かしてみる

## Dockerイメージの作成
ec-cubeのルートディレクトリ（Dockerfileがあるディレクトリ）で以下のコマンドを実行し、**Dockerイメージ**を作成します。

```
$ docker build -t eccube4-php-apache .
```

実行後、`$ docker images`コマンドで以下の２つのDockerイメージが作成されていることを確認します。

```
REPOSITORY           TAG                  IMAGE ID            CREATED
eccube4-php-apache   latest               ************        ◯ days ago
php                  7.3-apache-stretch   ************        ◯ weeks ago
```

## Dockerコンテナを起動する
作成されたDockerイメージをもとにDockerコンテナを起動します。

以下は**ローカルディレクトリをマウントする**場合のコマンドです。
**$PWD**のところはec-cubeの`ワーキングディレクトリのフルパス`を入力してください。

```
docker run --name ec-cube -p "8080:80" -p "4430:443"  -v "$PWD/html:/var/www/html/html:cached" -v "$PWD/src:/var/www/html/src:cached" -v "$PWD/app:/var/www/html/app:cached" eccube4-php-apache
```

実行後、`$ docker container ps -a`コマンドでコンテナが正常に起動していることを確認します。
以下のように表示されれば問題なくコンテナが起動しています。

```
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                                         NAMES
************        eccube4-php-apache   "docker-php-entrypoi…"   ◯ days ago          Up ◯ seconds        0.0.0.0:8080->80/tcp, 0.0.0.0:4430->443/tcp   ec-cube
```

コンテナ起動後、ブラウザで`http://127.0.0.1:8080/`にアクセスすればEC-CUBE SHOPのトップページが表示されます。
<img width="750" alt="Screen Shot 2019-10-15 at 5.07.13 AM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287365/0e2bec0e-b6d1-2026-322c-c4b8e721081c.png">

【参考】
[Dockerを使用してインストールする - EC-CUBE4.0開発ドキュメント](http://doc4.ec-cube.net/quickstart_install#docker%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B)


## MySQLを使用する
MySQLを使用したい場合はまず以下のコマンドで**MySQL用のコンテナを作成します**。

```
docker run --name container_mysql -e MYSQL_ROOT_PASSWORD=password  -d -p 3306:3306 mysql:5.7
```

コンテナが作成できたら、**cube4_dev**というDBを作成します。

```
## 以下のコマンドで作成したMySQLのコンテナに入ります。
docker exec -it container_mysql bash

## コンテナに入ったらrootユーザーでMySQLにログインします。（パスワードは"password"）
mysql -u root -p

## DBを作成します。
mysql> CREATE DATABASE cube4_dev;
```

DBを作成したら、ec-cubeコンテナがMySQLコンテナと通信できるように以下のコマンドで**ec-cubeコンテナを作り直します**。
なお、今回はローカルディレクトリをマウントする方法を紹介しています。
$PWDのところはec-cubeのワーキングディレクトリのフルパスに変えてください。

```
docker run --name ec-cube -p "8080:80" -p "4430:443"  -v "$PWD/html:/var/www/html/html:cached" -v "$PWD/src:/var/www/html/src:cached" -v "$PWD/app:/var/www/html/app:cached" --link container_mysql:db eccube4-php-apache
```

`docker container ps -a`を実行すると作成した２つのコンテナを確認できます。

```
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                                         NAMES
************        eccube4-php-apache   "docker-php-entrypoi…"   ◯  minutes ago      Up ◯  minutes       0.0.0.0:8080->80/tcp, 0.0.0.0:4430->443/tcp   ec-cube
************        mysql:5.7            "docker-entrypoint.s…"   ◯  minutes ago      Up ◯  minutes       0.0.0.0:3306->3306/tcp, 33060/tcp             container_mysql
```

２つのコンテナが作成できたらec-cubeコンテナに入り以下のコマンドを実行し**DBをSQLiteからMySQLに変更します**。
以下の**Database Url**以外はそのままEnterでOKです。

```
bin/console eccube:install

 Database Url [sqlite:///var/eccube.db]:
 > mysql://root:password@db/cube4_dev

 Mailer Url [null://localhost]:
 > 

 Auth Magic [xxxxxxxxxxxxxxxx]:
 >

 !                                                                                                                      
 ! [CAUTION] Execute the installation process. All data is initialized.                                                 
 !                                                                                                                      

 Is it OK? (yes/no) [yes]:
 > 
```

実行後、以下のように表示されれば問題ありません。
` [OK] EC-CUBE installation successful.`

EC-CUBEの管理画面にログインし、システム情報のDBサーバーが変更されていることを確認します。
なお、管理画面のログインIDは`admin`、パスワードは`password`です。
<img width="1404" alt="Screen Shot 2019-10-15 at 5.47.37 AM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287365/d45e0aae-8095-526c-f2cc-9ce49f654eaf.png">


【参考】
[Dockerを使用してインストールする（MySQLを使用する場合） - EC-CUBE4.0開発ドキュメント](http://doc4.ec-cube.net/quickstart_install#mysql-%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88)


# Dockerfileの中身を詳しく見てみる

## ベースイメージ
```docker
FROM php:7.3-apache-stretch
```
イメージは`PHP7.3`と`Apache`が一つのコンテナに含まれている`php:7.3-apache-stretch`をベースとして使用しています。なお、OSのVersionは`Debian9.0(stretch)`を指定しています。


## 環境変数の設定
```docker
ENV APACHE_DOCUMENT_ROOT /var/www/html
```
環境変数`APACHE_DOCUMENT_ROOT`を設定します。


## CDNミラーの設定
```docker
RUN echo "deb http://cdn.debian.net/debian/ stretch main contrib non-free" > /etc/apt/sources.list.d/mirror.jp.list
RUN echo "deb http://cdn.debian.net/debian/ stretch-updates main contrib" >> /etc/apt/sources.list.d/mirror.jp.list

# デフォルトで作成されるsources.listディレクトリを削除している
RUN /bin/rm /etc/apt/sources.list
```
`/etc/apt/sources.list.d/`に`mirror.jp.list`を作成し、CDNミラーを利用できるように設定を書き込んでいます。

【参考】
[CDNミラーを使うには - Debian JP Project](https://www.debian.or.jp/using/mirror.html#cdn)


## パッケージのインストール
```docker
RUN apt-get update \
  && apt-get install --no-install-recommends -y \
    apt-transport-https \
    apt-utils \
    build-essential \
    curl \
    debconf-utils \
    gcc \
    git \
    gnupg2 \
    libfreetype6-dev \
    libicu-dev \
    libjpeg62-turbo-dev \
    libpng-dev \
    libpq-dev \
    libzip-dev \
    locales \
    ssl-cert \
    unzip \
    vim \
    zlib1g-dev \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/* \
  && echo "en_US.UTF-8 UTF-8" >/etc/locale.gen \
  && locale-gen
```
主に`開発に必要なパッケージ`をインストールし、localeのコンパイルをします。

**apt-get update**：
インストール可能なパッケージの一覧を更新する。

**apt-get install**：
パッケージをインストールする。
`--no-install-recommends`オプションで必須ではない*recommends*のパッケージをインストールしないようにしている。
`-y`オプションで「Yes or No」の問い合わせはすべて「**y**」と答えるようにしている。

**apt-get clean**：
アーカイブファイルを削除する。

**rm -rf /var/lib/apt/lists/***：
パッケージのインデックスファイルが格納されている`/var/lib/apt/lists`を空にする。

**echo "en_US.UTF-8 UTF-8" >/etc/locale.gen**：
en_US.UTF-8とUTF-8を有効にする。

**locale-gen**：
localeをコンパイする。

【参考】
[debianパッケージ周りでよく使うコマンドとオプション - sonots:blog](http://blog.livedoor.jp/sonots/archives/50115456.html)
[ロケール - ArchWiki](https://wiki.archlinux.jp/index.php/%E3%83%AD%E3%82%B1%E3%83%BC%E3%83%AB)
[Linuxのlocaleについてちょっとした知識 - Unskilled](https://unskilled.site/linux%E3%81%AElocale%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%81%A1%E3%82%87%E3%81%A3%E3%81%A8%E3%81%97%E3%81%9F%E7%9F%A5%E8%AD%98/)


## PHPライブラリのインストール
```docker
RUN docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \
  && docker-php-ext-install -j$(nproc) zip gd mysqli pdo_mysql opcache intl pgsql pdo_pgsql
```

**docker-php-ext-configure**：
pgsqlの設定をカスタマイズする。

**docker-php-ext-install**：
PHPのライブラリをインストールします。
なお、`-j`オプションで同時に実行できるジョブ数を**$(nproc)**に指定します。

【参考】
[docker-php-ext-xxx-について 本番運用に備えたPHP7.3.0のコンテナを作る - Qiita](https://qiita.com/tomoyamachi/items/ff5ac4a96063a12409cd#docker-php-ext-xxx-%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)
[システム要件 - EC-CUBE 4.0 開発ドキュメント](http://doc4.ec-cube.net/quickstart_requirement)


## APCuのインストール
```docker
RUN pecl install apcu && echo "extension=apcu.so" > /usr/local/etc/php/conf.d/apc.ini
```
PHPアクセラレータの**APCu**をインストールします。
PHPアクセラレータとはPHPを高速化する拡張機能のことです。

`~/php/conf.d/apc.ini`に**extension=apcu.so**を書き込みインストールしたAPCuを有効にします。

【参考】
[PHP7をOPcacheとAPCuで高速化する - 己で解決！泣かぬなら己で鳴こうホトトギス](https://onoredekaiketsu.com/speed-up-php7-with-opcache-and-apcu/)


## Apacheの設定
```docker
RUN mkdir -p ${APACHE_DOCUMENT_ROOT}
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
```
**１行目**：
`${APACHE_DOCUMENT_ROOT}`のディレクトリを作成します。
`-p`オプションで親ディレクトリも作成します。

**２行目**：
`sed`コマンドで*/etc/apache2/sites-available/*にある**conf**ファイルに書かれている`/var/www/html`を`${APACHE_DOCUMENT_ROOT}`に書き換えます。

**３行目**：
`sed`コマンドで*/etc/apache2/***apache2.conf**と*/etc/apache2/conf-available/*にある**conf**ファイルに書かれている`/var/www/`を`${APACHE_DOCUMENT_ROOT}`に書き換えます。


【参考】
[【 sed 】 文字列の置換，行の削除を行う - 日経 xTECH](https://tech.nikkeibp.co.jp/it/article/COLUMN/20060227/230879/)
[sedでスラッシュを含む文字列を置換したい - Corredor](http://neos21.hatenablog.com/entry/2019/04/04/080000)


## Apacheモジュールの有効化
```docker
RUN a2enmod rewrite
RUN a2enmod headers
```
**１行目**：リダイレクトやURLの書き換えを行う**mod_rewrite**を有効にする。
**２行目**：HTTPのリクエストヘッダと応答ヘッダを制御する**mod_headers**を有効にする。

【参考】
[モジュール一覧 - Apache HTTPサーバ](https://httpd.apache.org/docs/2.4/ja/mod/)


## SSLの設定
```docker
RUN a2enmod ssl
RUN ln -s /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-enabled/default-ssl.conf
EXPOSE 443
```
**１行目**：
**ssl**モジュールを有効にする。

**２行目**：
`/etc/apache2/sites-available/default-ssl.conf`への**シンボリックリンク**`/etc/apache2/sites-enabled/default-ssl.conf`を作成します。

**３行目**：
ポート４４３を開放します。

【参考】
[「ls -l」コマンドの表示からファイルの属性を理解しよう (3/4) - @IT](https://www.atmarkit.co.jp/ait/articles/1605/18/news015_3.html)


## php.iniの設定
```docker
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY dockerbuild/php.ini $PHP_INI_DIR/conf.d/
```
**１行目**：
`php.ini-production`をもとに`php.ini`を作成します。

**２行目**：
dockerbuild/**php.ini**を`$PHP_INI_DIR/conf.d/`にコピーします。

【参考】
[php - Docker Hub](https://hub.docker.com/_/php)


## Composer
```docker
RUN curl -sS https://getcomposer.org/installer \
  | php \
  && mv composer.phar /usr/bin/composer \
  && composer config -g repos.packagist composer https://packagist.jp \
  && composer global require hirak/prestissimo

ENV COMPOSER_ALLOW_SUPERUSER 1
```
`curl`コマンドでPHPのパッケージ依存管理ツールである**Composer**をインストールし、**composer.phar**をPATHの通っている`/usr/bin/composer`に移動します。

`composer config`でpackagistのミラーサーバを日本国内のサーバに変更し、`composer global require`でComposerを速くするプラグインをインストールします。

**root**ユーザーで`composer install`を実行できるように設定します。

【参考】
[curlコマンド - hydroculのメモ](https://hydrocul.github.io/wiki/commands/curl.html)
[実行形式のComposerをダウンロード - Composerドキュメント日本語訳](https://kohkimakimoto.github.io/getcomposer.org_doc_jp/doc/00-intro.html)
[Composerの導入＆使い方（初心者の復習用） - Qiita](https://qiita.com/suke/items/770bccf8a43f9247daf5#packagist%E3%81%AE%E3%83%9F%E3%83%A9%E3%83%BC%E3%82%B5%E3%83%BC%E3%83%90%E3%82%92%E6%97%A5%E6%9C%AC%E5%9B%BD%E5%86%85%E3%81%AE%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AB%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B)
[composerを速くするプラグイン・prestissimoを作った - Architect Note](http://blog.tojiru.net/article/432944706.html)
[Dockerfileで質問を受けずにrootでcomposer installする - Qiita](https://qiita.com/nju33/items/fc85dc8071c99015bc92)


## EC-CUBEのファイルをコンテナにコピー
```docker
COPY . ${APACHE_DOCUMENT_ROOT}

WORKDIR ${APACHE_DOCUMENT_ROOT}
```
Dockerfileがあるカレントディレクトリ内のファイル一式をコンテナ上の`${APACHE_DOCUMENT_ROOT}`にコピーします。

その後、Dockerfileの命令実行時の作業ディレクトリを`${APACHE_DOCUMENT_ROOT}`に指定します。

【参考】
[パス名について | とほほのＷＷＷ入門](http://www.tohoho-web.com/ex/draft/path.htm)
[COPY - Dockerfileリファレンス](http://docs.docker.jp/engine/reference/builder.html#copy)
[WORKDIR - Dockerfileリファレンス](http://docs.docker.jp/engine/reference/builder.html#workdir)


## composer install
```docker
RUN composer install \
  --no-scripts \
  --no-autoloader \
  --no-dev -d ${APACHE_DOCUMENT_ROOT} \
  && chown -R www-data:www-data ${APACHE_DOCUMENT_ROOT} \
  && chown www-data:www-data /var/www
```
`composer install`でcomposer.lockに書かれている各ライブラリをインストールします。
その後、**${APACHE_DOCUMENT_ROOT}**ディレクトリ配下のファイルとディレクトリの所有者をApacheの実行ユーザー`www-data:www-data`に変更します。
また、**/var/www**ディレクトリの所有者も`www-data:www-data`に変更します。

【参考】
[コマンドラインインターフェース - Composerドキュメント日本語訳](https://kohkimakimoto.github.io/getcomposer.org_doc_jp/doc/03-cli.html)
[chownコマンドについてまとめました 【Linuxコマンド集】 - エンジニアの入り口](https://eng-entrance.com/linux-command-chown#-R--recursive)
[apacheのデフォルト実行ユーザwww-dataでgithubやbitbucketにSSH接続する - Qiita](https://qiita.com/Ouvill/items/02faa51d04b76c60d942#%E8%A7%A3%E8%AA%AC)


## composer dumpautoload

```docker
USER www-data
RUN composer dumpautoload -o --apcu --no-dev
```
Dockerfile内のコマンドを実行するユーザーを`www-data`に指定します。
そして、オートローディングに関する情報ファイルを生成する`composer dumpautoload`を実行します。
`-o`オプションでPSR-4規約のオートローディング対象となっている全クラスがクラスマップに含まれます。

【参考】
[Dockerfileについて - Qiita](https://qiita.com/tanan/items/e79a5dc1b54ca830ac21#user)
[composerについて初期学習まとめ - Qiita](https://qiita.com/eidera/items/3e0b2b41253e1563be46#dump-autoload)


## .env

```docker
RUN if [ ! -f ${APACHE_DOCUMENT_ROOT}/.env ]; then \
        cp -p .env.dist .env \
        ; fi
```
${APACHE_DOCUMENT_ROOT}に**.env**がなければ、.env.distをコピーして.envを作成します。

【参考】
[【Linux入門】if文による条件分岐の方法をわかりやすく解説！ - Samurai Blog](https://www.sejuku.net/blog/53707)
[【Linuxコマンド】cpコマンドでファイルをコピーする方法 - Samurai Blog](https://www.sejuku.net/blog/48613#-p)


## eccube.db

```docker
RUN if [ ! -f ${APACHE_DOCUMENT_ROOT}/var/eccube.db ]; then \
        composer run-script installer-scripts && composer run-script auto-scripts \
        ; fi
```

${APACHE_DOCUMENT_ROOT}/var/に**eccube.db**がなければ、composer.jsonに書かれている`installer-scripts`と`auto-scripts`を実行します。

【参考】
[Running scripts manually - Composer Documentation](https://getcomposer.org/doc/articles/scripts.md#running-scripts-manually)
[ec-cube/composer.json at 4.0 · EC-CUBE/ec-cube - GitHub](https://github.com/EC-CUBE/ec-cube/blob/4.0/composer.json)


## rootに切り替え

```docker
USER root
```

最後にユーザーを**root**に切り替えます。

【参考】
[USER - Dockerfileリファレンス](http://docs.docker.jp/engine/reference/builder.html#user)


# トラブルシューティング

## Windowsでdocker buildが上手くいかない
Windows環境で`docker build`が上手くいかない場合は、Gitの改行コードの設定を変更してみてください。

```
## 以下のコマンドでGit for Windowsの改行コード自動変換をOffにします。
git config --global core.autoCRLF false
```

コマンド実行後、もう一度`git clone`からやり直して、`docker build`を実行してください。

【参考】
[windows環境のgitで改行コードの自動変換に注意 - Qiita](https://qiita.com/yokoh9/items/1ec8099696ade0c1f36e)