docker-composeでローカル環境を作った備忘録と直面した問題など


今までvagrantでローカル環境を作成していたシステムで、dockerローカル環境を入れてみました！という備忘録です。
docker-composeを使います。

# ローカル環境で用意するもの
php5.6
laravel5.1
mysql5.7
apache
phpmyadmin
workspace

~~バージョン古くね？という声は胸の中にしまっておいてください。~~

# 構築に必要な諸々のファイル

```

├── apache
│   ├── Dockerfile
│   └── php.ini
├── docker-compose.yml
├── .env
└── workspace
    ├── Dockerfile
    └── php.ini
```

## docker-compose.yml
```docker-compose.yml
version: '3'

services:

  # ローカルでartisanコマンド打ったりする時用のworkspace
  workspace:
    build: workspace
    container_name: project_workspace
    volumes:
      - ${BASE_DIR}
    command: bash
    environment:
      - STAGE=docker
    depends_on:
      - mysql
      - cache

  # webサーバー。phpもここでインストールして動かす
  apache:
    build: apache
    container_name: project_apache
    volumes:
      - ${BASE_DIR}
    ports:
      - 8080:80
    environment:
      - STAGE=docker

  # ローカルで使うDB
  mysql:
    image: mysql:5.7
    container_name: project_mysql
    ports:
      - '3307:3306'
    environment:
      - MYSQL_DATABASE=db
      - MYSQL_USER=dbuser
      - MYSQL_PASSWORD=password
      - MYSQL_ROOT_PASSWORD=root
    volumes:
      - project_mysql:/var/lib/mysql

  # GUIでDB見る時用のPhpMyadmin（MySQLworkbenchとか使うなら必要ない）
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: project_phpmyadmin
    environment:
      - PMA_HOST=project_mysql
      - PMA_USER=dbuser
      - PMA_PASSWORD=password
    ports:
      - "3333:80"

  # キャッシュサーバー
  cache:
    image: memcached
    container_name: project_cache

volumes:
    project_mysql:
        driver: "local"
```

## .env
```ini

COMPOSE_PROJECT_NAME=project

BASE_DIR=../:/var/www/html
```
## apache/Dockerfile

```dockerfile

FROM php:5-apache

RUN apt-get update && apt-get install -y unzip libmemcached-dev zlib1g-dev
RUN docker-php-ext-install mysqli pdo_mysql
RUN pecl install memcached-2.2.0 && docker-php-ext-enable memcached
RUN cp /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/
ENV APACHE_DOCUMENT_ROOT /var/www/html/public

RUN sed -ri -e 's!/var/www/html!/var/www/html/public!g' /etc/apache2/sites-enabled/*.conf
RUN sed -ri -e 's!/var/www/!/var/www/html/public!g' /etc/apache2/apache2.conf /etc/apache2/conf-enabled/*.conf
COPY php.ini /usr/local/etc/php/
RUN echo "date.timezone = Asia/Tokyo" >> /usr/local/etc/php/php.ini
```

## workspace/Dockerfile

```dockerfile

FROM php:5

RUN apt-get update && apt-get install -y unzip libmemcached-dev zlib1g-dev
RUN docker-php-ext-install mysqli pdo_mysql
RUN pecl install memcached-2.2.0 && docker-php-ext-enable memcached
RUN curl -sS https://getcomposer.org/installer | php; mv composer.phar /usr/local/bin/composer ; mkdir /var/dev

RUN mkdir /var/www && mkdir /var/www/html
COPY php.ini /usr/local/etc/php/
RUN echo "date.timezone = Asia/Tokyo" >> /usr/local/etc/php/php.ini

WORKDIR /var/www/html

CMD ["bash"]
```

# 構築手順

## docker、docker-composeを使えるようにする

参考：https://qiita.com/zembutsu/items/dd2209a663cae37dfa81

## composeディレクトリの中でコンテナを立ち上げる

```
cd (docker-compose.ymlが入ってるディレクトリ名)
docker-compose up -d apache mysql phpmyadmin cache
```

## ローカル環境でartisanコマンドを使うとき

以下のコマンドでworkspaceに入りその中で行う

```
docker-compose run workspace
```



# 構築途中で直面した問題

## php.iniのtimezone設定漏れによるエラー

php.iniのtimezoneを指定しないと、以下のようなエラーが表示され、ページが表示されません。

```
Error - date_default_timezone_get(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in COREPATH/classes/fuel.php on line 161
```

そこで、apacheのDockerfileと同じ階層にデフォルトのphp.iniを置き、Dockerfileに以下を追記しました。

```dockerfile

COPY php.ini /usr/local/etc/php/
RUN echo "date.timezone = Asia/Tokyo" >> /usr/local/etc/php/php.ini
```

これで実行時点で動作させる対象となるイメージが存在する場合は
`docker-compose up -d --build`　を、
存在しない場合は
`docker-compose up -d` を実行すると、
apacheコンテナ（サーバー）作成時にphp.iniが作成されtimezoneも追記されます。
ちなみにイメージの確認は `docker images` で可能です。

php.iniを直接書き換えてtimezoneを設定すれば
 `RUN echo "date.timezone = Asia/Tokyo" >> /usr/local/etc/php/php.ini` する必要もないですが、
私はめんどくさかったのでこっちを採用しましたー。

## ホスト名指定によるローカル環境判断ができない

vagrantでローカル環境を用意していた際はホスト名を `gethostname()` で指定して今動いてるシステムがローカル環境上のものかどうか判断する、といった処理をしていたのですが、
dockerで環境構築すると同じようなことができませんでした。
そこでdocker-compose.ymlのファイルを変え、apacheコンテナ（サーバー）のイメージ作成時に環境変数を指定し、この環境変数があればローカル環境、といった判断ができるようにしました。

```docker-compose.yml
  apache:
    build: apache
    container_name: project_apache
    volumes:
      - ${BASE_DIR}
    ports:
      - 8080:80
    environment:
      - STAGE=docker
```

environmentのところで設定しています。

ちなみにPHP側で判別するときは以下みたいな感じです。

```app.php
if (env("STAGE") == "docker") {
  echo 'ローカル環境で動いています！';
}
```


## php-apacheコンテナからmemcachedコンテナ、mysqlコンテナに繋げられない
laravelプロジェクト上の `.env` でmemcachedコンテナ、mysqlコンテナに繋ぎたくても `DB_HOST`　やら　`MEMCACHED_HOST` やらに何を記載したらいいのかよくわからず…

と路頭に迷っていたところ、

>docker-compose.ymlで宣言したサービスは、docker-compose upなどでコンテナ起動すると、同一のネットワークに所属することになる（Dockerネットワークと呼ぶ）。そして、それぞれホスト名が割り当てられる。そのホスト名はサービス名、もしくはdocker-compose.ymlで指定したコンテナ名となる。

（[docker-composeとネットワーク、コンテナのホスト名の割り当てなどを理解する](https://qiita.com/roba4coding/items/efd3a38db08eb476d412)より引用）

とのこと！
つまり、ここで立てられた `apache` , `mysql` , `cache` などのコンテナは全て同じネットワーク上に存在していることになり、
ホスト名（＝コンテナ名）とポートで指定することで同ネットワーク上の別コンテナに接続ができるそうです。

コンテナ名は `docker-compose.yml` 内で `container_name` として指定したもののことです。
 `docker ps` で `NAMES` として出てきます。
また、指定しない場合でも、サービス名を指定することで接続できます。


今回のケースでは `.env` に以下のように設定することでmemcachedコンテナ、mysqlコンテナへ繋がります。

```ini

DB_CONNECTION=mysql
DB_HOST=project_mysql
DB_PORT=3307
DB_DATABASE=db
DB_USERNAME=dbuser
DB_PASSWORD=password

MEMCACHED_HOST=project_cache
MEMCACHED_PORT=11211
```


## PhpStorm/MySQLworkbenchがMySQLコンテナに繋がらない

 `docker ps` で出てきた情報を元に、以下で接続しようとしても繋がりません。。

```
hostname: 127.0.0.1
port: 3306
username: dbuser
password: password
```

というのも、 `docker-compose.yml` の `mysql` 部分でportを指定していなかったのが原因でした。
以下みたいに設定してみることで繋がりました。

```docker-compose.yml
  mysql:
    image: mysql:5.7
    container_name: project_mysql
    ports:
      - '3307:3306'
    environment:
      - MYSQL_DATABASE=db
      - MYSQL_USER=dbuser
      - MYSQL_PASSWORD=password
      - MYSQL_ROOT_PASSWORD=root
    volumes:
      - project_mysql:/var/lib/mysql
      
volumes:
    project_mysql:
        driver: "local"
```

他のシステムのローカル環境等でMySQLのportを3306にすることが多いので、3307にしてみました。
これで以下の情報で繋がるはずです。

```
hostname: 127.0.0.1
port: 3307
username: dbuser
password: password
```

 `ports` の右側はコンテナ側のportで、左側はホストマシン側（ローカル）のポートだそうです。
ちなみに、volumesを設定しないとコンテナをdownすると同時に中身のデータまで吹っ飛びます。
忘れずに設定してローカルのディレクトリとMySQLコンテナのデータ領域をマウントしましょう。

# 導入してみて

やっぱりvagrantと比べてずっと容量も軽いし、開発もしやすくなりましたー。
面倒でも古い環境からモダンな環境に移行してみることが大事ですね。
dockerは勉強すればするほどこんなにシンプルで簡単なの！？と思わされることばかりですね。

勉強中の身なので、おかしい部分などあればご指摘いただけると幸いです！