dockerでLaravel5.5環境を作ってみる

## 概要
個人的な開発でLaravel5.5環境を作りたかったので調べた内容を残しておく。

## ファイル構成
以下のような構造になります。

```:ファイル構成
├ docker
│  ├ httpd
│  │  ├ Dockerfile
│  │  └ httpd-vhosts.conf
│  ├ mysql
│  │  ├ Dockerfile
│  │  ├ example.sql
│  │  └ mysql.cnf
│  └ php
│     ├ Dockerfile
│     └ php.ini
├ src
├ .gitignore
├ docker-compose.init.yml
├ docker-compose.yml
└ README.md
```

## Laravel5.5のインストール
まずは、srcフォルダ内にLaravel5.5のコードを作成します。

### docker-compose.init.yml作成
インストール用のdocker-compose.ymlファイルを作成する

```yml:docker-compose.init.yml
version: '2'
services:
  composer:
    image: composer:1.8
    container_name: 'composer'
    volumes:
      - ./src:/app
```

### Laravelプロジェクト作成
下記コマンドでプロジェクトを作成します。

```bash:ターミナル
$ docker-compose -f docker-compose.init.yml run --rm composer create-project "laravel/laravel=5.5.*" .
```
※ srcフォルダが生成され、Laravel5.5のコードが作成されています

## サーバ構築
各サーバをDockerfileを使って構築します。
### Apache 2.4構築
Dockerfileを作成します。

```Dockerfile:./docker/httpd/Dockerfile
FROM httpd:2.4

ARG USER="www-data"
ARG UID="1000"
ARG GROUP="www-data"
ARG GID="1000"
ARG DOCUMENT_DIR="/var/www/"
ARG WORKSPACE="/usr/local/apache2/"

# timezone (Asia/Tokyo)
ENV TZ JST-9

# etc
ENV TERM xterm

# tools
RUN apt-get update && apt-get -y install git vim less

# httpd.conf
RUN sed -i -e "s/#ServerName/ServerName/g" /usr/local/apache2/conf/httpd.conf
RUN { \
    echo ''; \
    echo '# Custom Settings'; \
    echo 'LoadModule rewrite_module modules/mod_rewrite.so'; \
    echo 'LoadModule proxy_module modules/mod_proxy.so'; \
    echo 'LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so'; \
    echo 'LoadModule ssl_module modules/mod_ssl.so'; \
    echo 'LoadModule socache_shmcb_module modules/mod_socache_shmcb.so'; \
    echo '<FilesMatch \.php$>'; \
    echo '  CGIPassAuth On'; \
    echo '  SetHandler "proxy:fcgi://php:9000"'; \
    echo '</FilesMatch>'; \
    echo 'Include conf/extra/httpd-vhosts.conf'; \
    echo ''; \
} >> /usr/local/apache2/conf/httpd.conf

# virtual hosts
COPY ./httpd-vhosts.conf /usr/local/apache2/conf/extra/httpd-vhosts.conf

# document directory
RUN mkdir $DOCUMENT_DIR && chmod 705 $DOCUMENT_DIR

# user setting
WORKDIR $WORKSPACE
RUN usermod -u $UID $USER && groupmod -g $GID $GROUP
RUN chown -R $UID:$GID $DOCUMENT_DIR
RUN chown -R $UID:$GID $WORKSPACE
```
virtualhostの設定を作成します。

```conf:./docker/httpd/httpd-vhosts.conf
# www.example.com
<VirtualHost *:80>
  DocumentRoot /var/www/www.example.com/public/
  ServerName www.example.com

  CustomLog "logs/www.example.com-access_log" combined
  ErrorLog  "logs/www.example.com-error_log"

  <Directory "/var/www/www.example.com/public/">
    DirectoryIndex index.html index.php
    AllowOverride All
    Order allow,deny
    Allow from all
    Require all granted
  </Directory>
</VirtualHost>
```

### PHP-fpm 7.2構築
マルチステージビルドでDockerfileを作成する。

```Dockerfile:./docker/php/Dockerfile
# -----------------------------------------------------
# PHP Dependencies
FROM composer:1.8 as vendor
COPY ./src/ /app/
RUN composer install \
    --ignore-platform-reqs \
    --no-interaction \
    --no-plugins \
    --no-scripts \
    --prefer-dist

# -----------------------------------------------------
# Frontend
FROM node:11.11 as frontend
RUN mkdir -p /app/public
COPY src/package.json src/webpack.mix.js /app/
COPY src/resources/assets/ /app/resources/assets/
WORKDIR /app
RUN yarn install && yarn production

# -----------------------------------------------------
# Application
FROM php:7.2-fpm

ARG USER="www-data"
ARG UID="1000"
ARG GROUP="www-data"
ARG GID="1000"

# timezone (Asia/Tokyo)
ENV TZ JST-9
ENV TERM xterm

# tools
RUN apt-get -y update && apt-get install -y vim less zip unzip

# php options (mysql)
RUN docker-php-ext-install mysqli pdo_mysql

# php.ini
COPY ./docker/php/php.ini /usr/local/etc/php/php.ini

ARG APP_DIR="/var/www/www.example.com"
RUN mkdir -p $APP_DIR
WORKDIR $APP_DIR

COPY --chown=www-data:www-data ./src ./
COPY --chown=www-data:www-data --from=vendor /app/vendor/ ./vendor/
COPY --chown=www-data:www-data --from=frontend /app/public/js/ ./public/js/
COPY --chown=www-data:www-data --from=frontend /app/public/css/ ./public/css/
COPY --chown=www-data:www-data --from=frontend /app/mix-manifest.json ./mix-manifest.json
RUN cp .env.example .env && php artisan key:generate

# user setting
RUN usermod -u $UID $USER && groupmod -g $GID $GROUP
RUN chown -R $UID:$GID $APP_DIR
USER $USER
```
php.ini設定ファイルを作成する。

```ini:./docker/php/php.ini
[Date]
date.timezone = "Asia/Tokyo"

[Log]
log_errors = On
error_log = '/var/www/php_error.log'
```
### SMTPサーバ構築
ブラウザでメールが確認できて便利なMailHogというSMTPサーバを利用します。
公式コンテナをそのまま使うため、Dockerfileは作りませんが、envファイルの設定を行う。

```:.env(.env.example)
MAIL_DRIVER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
```

### MySQL 5.7構築
開発向けに上記コンテナを作成する

```Dockerfile:./docker/mysql/Dockerfile
FROM mysql:5.7

ARG USER="mysql"
ARG UID="1000"
ARG GROUP="mysql"
ARG GID="1000"
ARG WORKSPACE="/var/lib/mysql/"

# timezone (Asia/Tokyo)
ENV TZ JST-9

# etc
ENV TERM xterm

# my.conf
COPY mysql.cnf /etc/mysql/conf.d/custom.cnf

# user setting
WORKDIR $WORKSPACE
RUN usermod -u $UID $USER && groupmod -g $GID $GROUP
```
mysqlの設定ファイルを作成する。

```conf:./docker/mysql/mysql.cnf
[mysqld]
character-set-server=utf8

[client]
default-character-set=utf8
```
データベースを自動作成するSQLを作成する。

```sql:./docker/mysql/example.sql
--
-- Database: example
--
create database example;
use example;

--
-- User: hoge
--
CREATE USER `hoge` IDENTIFIED BY 'development';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON `example`.* TO `hoge` identified by 'development';
```

### docker-compose.yml作成
開発用のdocker-compose.ymlファイルを作成します。

```yml:docker-compose.yml
version: '2'
services:
  httpd:
    build: ./docker/httpd/
    image: my/apache2.4
    container_name: 'httpd'
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./src:/var/www/www.example.com/

  php:
    build:
      context: ./
      dockerfile: ./docker/php/Dockerfile
    image: my/php7.2
    container_name: 'php'
    tty: true
    ports:
      - '9000:9000'
    volumes:
      - ./src:/var/www/www.example.com/
      - /var/www/www.example.com/vendor/
      - /var/www/www.example.com/node_modules/
    depends_on:
      - mysql

  mysql:
    build: ./docker/mysql/
    image: my/mysql5.7
    container_name: 'mysql'
    environment:
      - MYSQL_DATABASE=mysql
      - MYSQL_ROOT_PASSWORD=development
    ports:
      - '3306:3306'
    volumes:
      - mysql-data:/var/lib/mysql
      - ./docker/mysql/example.sql:/docker-entrypoint-initdb.d/example.sql

  mailhog:
    image: mailhog/mailhog
    ports:
      - "8025:8025"

volumes:
  mysql-data:
    driver: local
```

## サーバ起動

```bash:ターミナル
# dockerイメージ作成
$ docker-compose build --no-cache

# 環境設定ファイル作成
$ docker-compose run --rm php cp .env.example .env
$ docker-compose run --rm php php artisan key:generate

# サーバ起動
$ docker-compose up -d

# 状況確認
$ docker-compose ps
  Name                Command               State                     Ports                  
---------------------------------------------------------------------------------------------
httpd      httpd-foreground                 Up       0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
mysql      docker-entrypoint.sh mysqld      Up       0.0.0.0:3306->3306/tcp, 33060/tcp       
php        docker-php-entrypoint php-fpm    Up       0.0.0.0:9000->9000/tcp  
```

# 動作確認
ブラウザで確認してみる。

Laravel
![test.png](https://qiita-image-store.s3.amazonaws.com/0/142279/28ef726c-2cdd-980a-e20a-b23e21830b89.png)

MailHog
![mail.png](https://qiita-image-store.s3.amazonaws.com/0/142279/215403ef-25da-308d-b17a-c80641847309.png)

以上

## 参考サイト
- [Creating Multi-Stage Docker Builds for Laravel](https://laravel-news.com/multi-stage-docker-builds-for-laravel)
- [Laravelで最適なDocker環境を作る](https://qiita.com/kotamat/items/9f2fe9cc66cf3536abe5)
- [MailHogを利用してメール送信テスト環境を作る](https://qiita.com/kobatei/items/1b8b0ca5e8737235dccd)