DockerでPHP7.4+Apache+MySQLの環境構築しようと思ったら詰まりまくった【Windows & Mac】

#"はじめに"
今回はDockerをつかって**Laravel+Apache+MySQLの開発環境**を構築したいと思います。
初心者プログラマーを含めたチームで個人開発を行うため、基本的に導入方法や手順などは視覚的にわかりやすく、簡単なものを採用する方針です。

#"Dockerの導入"
早速本題であるDockerの導入をしていきましょう。

##Hyper-vの有効か(Windowsのみ
Dockerのインストール前にHyper-vという仮想化機能を有効化する必要があります。
この機能はWindows Pro系にしか導入されていないみたいで、その他の場合はVirtualBoxなどほかの仮想化技術の導入をする必要があります。

**①Windowsボタン右の「ここに入力して検索」部分に"Windowsの機能の有効化または無効化"と入力**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758471/9d7a7ca5-f7cb-1b6c-a0f7-d2ea30cb7cb8.png)

**②検索文言の通りの設定画面が表示される**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758471/77f98e6e-ed6e-8e8a-73a6-528fc68f1a56.png)

**③その中からHyper-vという項目を探しチェックを付ける**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758471/55d5dc7c-5c23-e069-dc0e-d1f8a0eb4c22.png)

以上でインストールの準備は完了

###③の手順で、Hyper-vプラットフォームにチェックがつけられなかった場合
主に自作PCを使用している場合にチェックを付けられない可能性があって、調べてみるとBIOS画面から機能そのものを有効化しなければならないみたい。
もし同じ現象になった場合、「"マザボのメーカー名" BIOS Hyper-v」とかで検索すると、有効化の方法の記事が見つかるはず。
ちなみに僕はGIGABYTEのマザボなので以下の記事が参考になりました

・[「Hyper-Vをインストールできません」と表示された場合の対処方法](https://docs.docker.com/docker-for-mac/install/ "タイトル")


##Docker Desktopのインストール
以下のリンクから環境にあったインストーラーをダウンロードしてください

・[Macを使用している場合のインストーラー](https://docs.docker.com/docker-for-mac/install/ "タイトル")
・[Windows10(pro)を使用している場合のインストーラー](https://docs.docker.com/docker-for-mac/install/ "タイトル")

ダウンロード後、手順に従って何も考えずインストールします。
何かしら選択する画面も出てくるが基本的になにもいじらなくてOK

インストールを完了時に再起動をすることになるため、ほかに開いているアプリなどがある場合は閉じておいてください

##再起動後
Macならターミナル、Windowsならコマンドプロンプトなどで以下のコマンドを実行します。

```
docker -v
```

実行後、以下のバージョンが表示されればインストール完了です。

```
PS C:\Users\Kohei Takahashi> docker -v
Docker version 19.03.13, build 4484c46d9d
```

###【Exception】再起動時にDocker Desktopからエラーメッセージが表示される場合
出たエラーが一概に僕と同一のものであるわけではないが、僕の場合「Linuxのカーネルないで」というエラーが出ました。
基本的にエラーメッセージと一緒にリンクが表示されている場合、そのリンク先に飛べば解決方法が丁寧に記載されているためその通りに進めてください。

##Docker Composeをインストール
Dockerには[コンテナ](https://cn.teldevice.co.jp/column/10509/ "タイトル")という概念があり、Docker Composeはそれらのコンテナを扱いやすくしてくれるツールです。
インストール方法は

・[[Docker] Windows 10 Pro 64bit に Docker と Docker Compose をインストールする](https://qiita.com/ksh-fthr/items/6b1242c010fac7395a45 "タイトル")
・[MacでDockerとdocker-composeをインストールする方法](https://awesome-linus.com/2019/08/17/mac-docker-install/ "タイトル")

上記の記事がわかりやすいです。


##Dockerを使って実際に環境を構築していく
今回は前述のとおり、Laravel(PHP)+Apache+MySQLの環境を用意していきます！
もしチーム開発をする場合、ここからの作業は誰か一人が行って、他の人は「Dockerを立ち上げる」まで読み飛ばしてOKです。
フォルダ構成などに制限はないらしいけど、[参考資料](https://note.com/pocke_techblog/n/n8af813848fa0 "タイトル")の通りの構成にました。
自分の好きなディレクトリに以下の構成でフォルダ・ファイルを作成しておいてください。

```bash

├── docker/
│   ├── app/
│   │   ├ Dockerfile
│   │   ├ php.ini # PHP設定用のファイル
│   │   └ 000-default.conf  # Apacheの設定ファイル
│   └── db/
│        ├ data/ # MySQLのデータを保存しておくディレクトリ
│        └ my.cnf  # MySQLの設定ファイル
│
├── src/ # Laravelのソースを格納するディレクトリ
└── docker-compose.yml
```
とっても綺麗。

続いて今用意した設定ファイルたちに命を吹き込んでいきます。

###Dockerfile
まずはDockerfileに命を吹き込んでいきます。

```docker
# どんなdockerイメージを利用して構築をするか
FROM php:7.4-apache

# 設定ファイルをdockerコンテナ内のPHP、Apacheに読み込ませる
ADD php.ini /usr/local/etc/php/
ADD 000-default.conf /etc/apache2/sites-enabled/

# Composerのインストール
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer

# ミドルウェアインストール
RUN apt-get update \
&& apt-get install -y \
git \
zip \
unzip \
vim \
libpng-dev \
libpq-dev \
&& docker-php-ext-install pdo_mysql

# Laravelで必要になるmodRewriteを有効化する
RUN mv /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled
RUN /bin/sh -c a2enmod rewrite
```

こちらも[参考資料](https://note.com/pocke_techblog/n/n8af813848fa0 "タイトル")の丸パクリです。
このコマンド達が上から順に実行されていくようです。

###000-default.conf
つづいてApacheの設定ファイルに命を吹き込みます。

```conf
<VirtualHost *:80>
       ServerAdmin webmaster@localhost
       DocumentRoot /var/www/html/laravelapp/public
       ErrorLog ${APACHE_LOG_DIR}/error.log
       CustomLog ${APACHE_LOG_DIR}/access.log combined
       <Directory /var/www/html/laravelapp/public>
           AllowOverride All
       </Directory>
</VirtualHost>
```

ここに出てくる"laravelapp"というのは作成する予定のLaravelのアプリ名を入れてください。

###php.ini
続いてphp.iniです

```ini

[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"
```
これは説明なしでもわかると思いますが、文字コードだったり「わいは日本人なんやから、日本に対応せいや」っていうことを定義してます。

###my.cnf
設定ファイル最後はmy.cnfです

```conf

[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
[client]
default-character-set=utf8mb4
```
こちらは主に文字コードの設定です。

##コンテナ楽々管理のDocker Compose
先ほどディレクトリ構成を書いた際、docker直下には"appディレクトリ"と"dbディレクトリ"がありましたが、それらは[コンテナ](https://cn.teldevice.co.jp/column/10509/ "タイトル")を分けるためにわかりやすく区別していました。
appコンテナとdbコンテナを作るわけですが、それらを楽に管理ができるDocker Composeというものがあります。
そのDocker Composeの設定ファイルがdocker-compose.ymlです。
docker-compose.ymlに以下を記述してください。

```yml
# Compose fileのバージョン指定
version: '3'
# どんなコンテナを立ち上げるか
services:
 # laravelを動かすコンテナ
 app:
   # どのポートを開いて繋ぐか。下記はコンテナの80番ポートを開いて、ホストの8000番につなぐ
   ports:
     - "8000:80"
   # 先ほど作ったDockerfileを使って、コンテナをビルドするという指定
   build: ./docker/app
   # コンテナの名前を指定
   container_name: laravel_app
   # コンテナとホスト側のディレクトリを同期する場所の指定。laravelのソースが入る予定の場所
   volumes:
     - ./src:/var/www/html
 # MySQLを動かすコンテナ
 db:
   # Docker HubからMySQL5.7の公式イメージをダウンロードしてくる指定
   image: mysql:5.7
   container_name: laravel_db
   # コンテナ内の環境変数を指定。環境変数を渡すとビルド時に設定してくれるDockerイメージがあるので、利用の際はDocker Hubのサイトで確認すると良い
   environment:
     MYSQL_ROOT_PASSWORD: root
     MYSQL_DATABASE: laravel_db
     MYSQL_USER: laravel_user
     MYSQL_PASSWORD: laravel_pass
     TZ: 'Asia/Tokyo'
   # 起動時のコマンド
   command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
   # ディレクトリ同期。設定ファイルとMySQLのデータが保存される場所を同期している。コンテナは基本的に起動時に変更されてもコンテナ自体が止まるとデータが消えてしまうため、保存しておきたいものはホストマシンと同期しておく必要がある。
   volumes:
     - ./docker/db/data:/var/lib/mysql
     - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf
   ports:
     - 3306:330
```

以上で設定ファイルたちは完了です。
Dockerを立ち上げてみましょう！

##Dockerを立ち上げる
以下のコマンドを実行すると先ほど記述したdocker-compose.ymlの内容がコンテナとなります。

```bash
docker-compose build
```
その次は以下のコマンドでコンテナを起動しましょう

```bash
docker-compose up
```

これで起動されます。
ただ上記のコマンドで起動すると、バックグラウンドで実行されないためログが常に表示されます。
それは鬱陶しいので基本的には以下のコマンドを実行して起動します。

```bash
docker-compose up -d
```
これでバックグラウンドで実行されます。

ちなみに落としたい場合は

```bash
docker-compose down
```
これで落ちます。

##Laravelプロジェクトを作成
次は実際にLaravelのプロジェクトをコンテナ内で作成しましょう！

以下のコマンドで先ほど作ったappコンテナのshellに入りこみます

```bash
docker exec -it laravel_app bash
```
入り込んだ先で以下の慣れ親しんだコマンドを入力してプロジェクトを作成します

```bash
composer create-project "laravel/laravel=~6.0" --prefer-dist laravelapp
```
上記の"laravelapp"と書かれている部分には開発したいプロジェクト名を入力してください。

実行した後はプロジェクトが作成されるまでしばらく待ちましょう！

##Welcomeページを拝みに行く
先ほどdocker-compose.ymlで以下のような記述をしました

```yml:appコンテナの設定を抜粋
services:
 app:
   ports:
     - "8000:80" #ここ重要
   build: ./docker/app
   container_name: laravel_app
   volumes:
     - ./src:/var/www/html
```

appコンテナについての記述で"ports"というプロパティを設定していました、これはその名の通りポートで、dockerのコンテナを立ち上げると以下のURLで作成したlaravelプロジェクトを実際に見に行くことができます。

http://localhost:8000

これで確認できたら、ひとまずDocker上にlaravelのプロジェクトを作成することができています。

###【Exception】なんか知らんけどPermission deniedされる場合
最初アクセスしたらこんなこと言われました

```bash
The stream or file "/var/www/html/dooga/storage/logs/laravel.log" could not be opened in append mode: failed to open stream: Permission denied
```
調べたところ、どうやらLaravelのlogファイルにapacheユーザーが書き込み権限を持たないため、logに書き込めずに例外が発生しているらしい。

```bash
chmod -R 777 /var/www/html/laravel_app/storage
```

上記のコマンドで解決することはできたが、なんか嫌です。と数時間探しまくっていると[こんな記事発見](https://qiita.com/engulisyu/items/ad819d06ea0cd31411df "タイトル")
早速手順の通りやってみると見事表示された。
このユーザー権限がいいものなのかというと少し疑問ですが、まあ問題ない範囲であると思います。

###【Exception】artisanコマンドできない場合
artisanコマンドを打つと以下のエラーがでてきました

```bash
Failed to clear cache. Make sure you have the appropriate permissions.
```
もしこれが出る場合は以下のフォルダ構成を確認してください。

```bash
├─app # 今回はおそらく関係ない
│  └─public
├─framework
│  ├─cache
│  │  └─data
│  ├─sessions
│  └─views
└─logs # 今回はおそらく関係ない

```
なにか手順が悪かったのか、そもそも作られないのか。理由は定かでありませんが、上記の"framework"というフォルダの中身が揃ってませんでした。てかそもそもframeworkっていうフォルダ自体なかった気がする。
おそらく書き込み権限がなかった関係で作れるもんも作ってくれなかったんじゃないかなと思います。
僕の環境ではこれらの解決で無事Welcomeページにたどり着きました。

##DBの設定を行う
最後にDBの設定です。
それらの設定は.envというファイルにまとめられています。以下のように変更してください。

```bash
DB_CONNECTION=mysql
DB_HOST=laravel_db
DB_PORT=3306
DB_DATABASE=laravel_db  # docker-compose.ymlのMYSQL_DATABASE
DB_USERNAME=laravel_user  # docker-compose.ymlのMYSQL_DATABASE
DB_PASSWORD=laravel_pass  # docker-compose.ymlのMYSQL_PASSWORD
```
見覚えあるかと思いますが、これはdocker-compose.ymlにてdbコンテナの"environment"として設定した値です。起動時にDBも作成してくれているので、設定が済んだら実際にmigrateしてみましょう。

先ほどのようにappコンテナのshellに入り込みます。
リンクスタート！！ピュルルルルルーン！！（SAO風）

```bash
docker exec -it laravel_app bash
```
これで/var/www/htmlに入り込みました。
ここでlsを実行すると直下にlaravel_appがあることがわかります。

```shell
cd laravel_app # laravel_appに移動
php artisan migrate # migrate実行
```
上記コマンド実行後以下のようなログが表示されればマイグレーション成功です。一旦の環境構築完了と言えます。

```shell
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.1 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.06 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.03 seconds)
```
今回は接続確認でのマイグレーションなので僕の場合この後はもとに戻します。

```shell
php artisan migrate:refresh
```
これでマイグレーション前の状態に戻ります。

###【Exception】なんか知らんけどAccess deniedされる
上記ではスルッと終わってるように見えますがもちろんそんなことはなく、ここでも一瞬詰まりました。
理由はphp artisan migrate時にAccess deniedのエラーが表示されたためです。

```shell:エラーメッセージ
root@xxxxxxxxxxxx:/var/www/html/laravel_app# php artisan migrate # migrateの実行

   Illuminate\Database\QueryException  : SQLSTATE[HY000] [1045] Access denied for user 'laravel_user'@'172.21.0.3' 
```

なんで？と思って実際にmysqlの中身を見に行きました
その場合は一旦appコンテナから抜け出して

```shell
docker exec it laravel_db mysql -u root -p
Enter password:  # おそらくrootと入力でOK

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.31 MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```

これでmysqlにrootユーザーでログインできました。
あとは存在しているユーザーを見に行きます。

```shell
mysql> select user from mysql.user;
```
すると僕の場合、laravel_dbのユーザーが作成されていませんでした。（もしかしたら当たり前なのかもｗ）

```yml
db:
   image: mysql:5.7
   container_name: laravel_db
   environment:
     MYSQL_ROOT_PASSWORD: root
     MYSQL_DATABASE: laravel_db
     MYSQL_USER: laravel_user # このユーザーが存在しなかった
     MYSQL_PASSWORD: laravel_pass
     TZ: 'Asia/Tokyo'
   command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
   volumes:
     - ./docker/db/data:/var/lib/mysql
     - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf
   ports:
     - 3306:330
```

だったらrootを常に使うユーザーとして.envファイルを変更してもいいのですが、せっかくならlaravel_userユーザーを使いたいですし、rootを通常時に使うのは宗教上の都合で死刑になってしまいます。

ではrootと同じ権限のlaravel_userを作ればいいんだ！（白目）

はい、じゃあroot使えよって感じですが、お付き合いください。

```shell
mysql> CREATE USER 'laravel_user'@'%' IDENTIFIED BY 'laravel_pass';
```
上記のCREATE分でlaravel_userが作成されます。
今度はrootと同じ権限を与えます。

```shell
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER ON *.* TO 'laravel_user'@'%' WITH GRANT OPTION;
```
このなっげぇ文でlaravel_userに権限を付与します。
本当にrootと同じ権限があるのかどうかを確認します。

```shell
mysql> select host,user,select_priv,create_priv,insert_priv,grant_priv,account_locked from mysql.user;
+-----------+---------------+-------------+-------------+-------------+------------+----------------+
| host      | user          | select_priv | create_priv | insert_priv | grant_priv | account_locked |
+-----------+---------------+-------------+-------------+-------------+------------+----------------+
| localhost | root          | Y           | Y           | Y           | Y          | N              |
| localhost | mysql.session | N           | N           | N           | N          | Y              |
| localhost | mysql.sys     | N           | N           | N           | N          | Y              |
| %         | root          | Y           | Y           | Y           | Y          | N              |
| %         | laravel_user  | Y           | Y           | Y           | Y          | N              |
+-----------+---------------+-------------+-------------+-------------+------------+----------------+
5 rows in set (0.00 sec)
```
これでこの呪縛から解放されました。

#"まとめ"
以上でDockerでLaravel+Apache+MySQLの環境構築は完了です。
個人的に壁にぶち当たることが多すぎてなかなかスムーズにいけませんでしたが、とりあえず開発できる段階までいけたので満足です。
この環境をもう少し拡張していきたいなと考えていますので、その際はまた記事にします！
【関連記事】
・[DockerのLaravelにVue.jsも入れたらんかい！！](https://qiita.com/buzzdark1212/items/cd74065809e4d081ca2b "タイトル")

では
##参考資料
非常にお世話になった参考資料です。
・[Laravelのパーミッションを適切に設定](https://qiita.com/engulisyu/items/ad819d06ea0cd31411df "タイトル")
・[Laravel 5.7 で php artisan cache:clear を叩くと Failed to clear cache. Make sure you have the appropriate permissions. になる。。](https://qiita.com/tyamahori/items/a94b5717f1f1cab5e1b9 "タイトル")
・[ERROR 1045 (28000): Access denied for userとなったときの対応方法](https://qiita.com/ponsuke0531/items/df51a784b5ff48c97ac7 "タイトル")
・[mysqlへログインできない問題](https://qiita.com/chanP_yamazaki/items/064565e574c493a634fe "タイトル")