Dockerを使って1コマンドで起動できるLaravel開発環境を構築する

DockerでLaravelの開発環境を構築します。
**プロジェクトをcloneしたらコマンド1つで開発が始められるようになります。**
もう環境構築に半日も工数をとる必要はありません。

## 構築する開発環境
構築する環境は以下の通りです。

- ホスト
    - Docker 19.03.5（インストール済み）
- コンテナ
    - PHP 7.3
    - Apache 2.4
    - MySQL 5.7.29
    - PHPMyAdmin（おまけ）
 
いわゆるLAMP環境です。

### ディレクトリ構成
最終的なディレクトリ構成は以下の通りです。Laravelプロジェクトはmyappに保存します。

```
project
├── docker
│   ├── docker-compose.yml
│   └── php-apache
│       ├── apache2.conf
│       └── php.ini
│       └── Dockerfile
└── myapp (Laravelプロジェクト）
    ├── app
   ・・・
    └── startup.sh
```
> startup.shは.envの作成など、Laravelプロジェクト初期化に必要なコマンドをまとめたファイルです。
> 後ほど紹介します。

## Dockerfile作成
Dockerfileは以下の通りです。

```docker:project/docker/php-apache/Dockerfile
FROM php:7.3-apache

COPY --from=composer /usr/bin/composer /usr/bin/composer
RUN apt-get update && apt-get install -y \
    unzip \
    libzip-dev \
    zlib1g-dev \
    && docker-php-ext-install \
    zip \
    pdo_mysql \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
    && a2enmod rewrite
COPY ./apache2.conf /etc/apache2/apache2.conf
COPY ./php.ini /usr/local/etc/php/php.ini

WORKDIR /var/www/myapp
```
### Dockerfile解説
Dockerfileの内容を解説します。
不要な場合は次章の**「docker-compose.yml作成」**まで読み飛ばしてください。

```docker

FROM php:7.3-apache
```

[Docker Hub](https://hub.docker.com/)を探すと、PHPとApacheが同梱されているイメージがあるので今回はそれを使用します。

```docker
COPY --from=composer /usr/bin/composer /usr/bin/composer
```
`--from=composer`でPHPコンテナからComposerコンテナを使用することができます。
マルチステージビルドという仕組みです。
Composerのコンテナから/usr/bin/composerをコピーすることでPHPコンテナ内でComposerのコマンドを使用できるようになります。

```docker
RUN apt-get update && apt-get install -y \
    unzip \
    libzip-dev \
    zlib1g-dev \
    && docker-php-ext-install \
    zip \
    pdo_mysql \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
    && a2enmod rewrite
```

環境構築後に「いざ、composer install！」とすると、やれzipコマンドがない、unzipがないなど色々言われます。
都度、apt-getで必要なものをインストールさせるとこんな感じになります。
また、`docker-php-ext-install`を使用して、MySQLのPDOドライバもインストールしています。
最後の`a2enmod rewrite`はApacheの設定です。

```docker
COPY ./apache2.conf /etc/apache2/apache2.conf
COPY ./php.ini /usr/local/etc/php/php.ini
```

ApacheやPHPの設定ファイルを変更したい場合は、ホスト側で設定ファイルを作成してコンテナの設定ファイルを上書きします。
私の場合は、コンテナ内からapache2.confをコピーしてきて以下を追記したものを使っています。

```project/docker/php-apache/apache2.conf
~省略~

<VirtualHost *:80>
	ServerName myapp.com
	ServerAdmin webmaster@localhost
	DocumentRoot /var/www/myapp/public

	<Directory "/var/www/myapp">
			AllowOverride All
	</Directory>
</VirtualHost>
```
ドキュメントルートをLaravelプロジェクトのpublicディレクトリに変更するのを忘れないようにしましょう。

※php.iniの内容は環境構築に直接関係ないので割愛します。

```docker
WORKDIR /var/www/myapp
```

最後はコンテナにアクセスした時にプロジェクトのルートがデフォルトになるようにWORKDIRを設定します。

## docker-compose.yml作成
docker-compose.ymlは以下の通り。

```project/docker/docker-compose.yml
version: "3"
services:
  mysql:
    image: mysql:5.7.29
    container_name: mysql    
    ports:
      - '3306:3306'
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: default
      MYSQL_USER: default
      MYSQL_PASSWORD: secret
      TZ: 'Asia/Tokyo'
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
  app:
    build: php-apache
    container_name: myapp
    ports:
      - '8000:80'
    volumes:
      - ../myapp.:/var/www/myapp:cached
```

### docker-compose.yml解説
docker-compose.ymlの内容を解説します。
不要な場合は次章の**「コンテナの起動」**まで読み飛ばしてください。

```project/docker/docker-compose.yml
mysql:
  image: mysql:5.7.29
  container_name: mysql    
  ports:
    - '3306:3306'
  environment:
    MYSQL_ROOT_PASSWORD: root
    MYSQL_DATABASE: default
    MYSQL_USER: default
    MYSQL_PASSWORD: secret
    TZ: 'Asia/Tokyo'
  command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
```

MySQLコンテナの設定です。
`image`：MySQLのイメージを指定します。
`ports`：ホストの3306ポートとコンテナの3306ポートを紐づけています。
`environment`：環境変数です。データベースの初期設定をしています。
`command`：文字エンコーディングの設定をしています。

```project/docker/docker-compose.yml
app:
  build: php-apache
  container_name: myapp
  ports:
    - '8000:80'
  volumes:
    - ../myapp:/var/www/myapp:cached
```
アプリケーションコンテナの設定です。
`build`：`docker/php-apache`内のDockerfileをビルドするように指定しています。
`ports`：ホストの8000ポートとコンテナの80ポートをリンクさせています。
`volumes`：ホストのLaravelプロジェクトのディレクトリをコンテナにマウントします。
ホスト側でアプリを編集すれば、コンテナ側にも編集内容が反映されるようになります。
`:cached`：Mac for Dockerを使っているとボリュームのマウントがやたらコストかかるのでキャッシュさせるようにします。

## コンテナの起動
とりあえずこれでコンテナの構成は完了です。コンテナを起動しましょう。

```sh:project/docker/
$ docker-compose up
```

数分もすればMySQLとPHPのコンテナが立ち上がります。
これで開発環境は完成です。

## Laravelプロジェクトの初期化
Laravelプロジェクトを初期化します。
.env.exampleをdocker-compose.ymlで設定したMySQLの初期設定に合わせて変更します。

```myapp/.env.example
DB_HOST=mysql
DB_DATABASE=default
DB_USERNAME=default
DB_PASSWORD=secret
```
DB_HOSTには`container_name`の値を設定します。

次にいくつか実行しなければならないコマンドをstartup.shにまとめておきます。
.envの作成からシーディングまで指定しておきます。

```sh:myapp/startup.sh
cp .env.example .env
composer install
touch /var/www/myapp/storage/logs/laravel.log
chmod -R 777 /var/www/myapp/storage
chmod -R 777 /var/www/myapp/bootstrap
php artisan key:generate
php artisan migrate
php artisan db:seed
```


作成したら以下のコマンドでPHPコンテナに接続します。

```:myapp/docker
$ docker exec -it myapp bash
```

接続したら、プロジェクトのルートディレクトリにいるので次のコマンドを実行します。

```:phpコンテナ：/var/www/myapp
$ sh startup.sh
```
> artisanコマンドなどを使用する場合もdocker execで同じようにコンテナにアクセスして実行します。

コマンドの実行が終わったらlocalhost:8000にアクセスします。
アプリケーションが稼働しているはずです。

開発者はプロジェクトをcloneしたら`docker-compose up`と`sh startup.sh`だけで開発が始められます。

### アクセスできない場合
Docker for Windowsを使用している場合は、`docker-machine ls`コマンドで表示されるIPアドレス:8000でアクセスできます。
また、指定したポート番号が他のアプリとかぶっているとコンテナが起動しません。
docker-compose.ymlのポート番号を変更して`docker-compose up`をやりなおしてみてください。



## PHPMyAdminの追加（おまけ）
簡単なので、PHPMyAdminも追加してみましょう。
PHPMyAdminはブラウザ上で使用できるDBクライアントツールです。
docker-compose.ymlに追記します。

```project/docker/docker-compose.yml
version: "3"
services:
  mysql:
    image: mysql:5.7.29
    container_name: mysql    
    ports:
      - '3306:3306'
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: default
      MYSQL_USER: default
      MYSQL_PASSWORD: secret
      TZ: 'Asia/Tokyo'
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
  app:
    build: php-apache
    container_name: myapp
    ports:
      - '8000:80'
    volumes:
      - ../myapp:/var/www/myapp:cached
  # 以下追記
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    ports:
      - 8080:80
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOST=mysql
      - PMA_USER=root
      - PMA_PASSWORD=root
    container_name: "phpmyadmin"
```

`docker-compose up`を実行してlocalhost:8080にアクセスします。
PHPMyAdminが稼働しているはずです。

## まとめ

Dockerを使えば開発環境の構築が1コマンドでできる！
（startup.shは環境ではないのでノーカン）