dockerでnginx+PHP+MySQL(+PHPMyAdmin+MailHog)の環境を作る

[こちら](https://yutaro-blog.net/2021/04/28/docker-laravel-vuejs-1/)を主軸に色々な先輩方のサイトを参考にnginx+PHP+MySQLの環境を作ってみた。
参考にしたサイトはこの記事の一番下にまとめて書きます。
また、基本的にMacで作っていきますが、Windowsでもほぼ同じ手順で作れます。

## 取り敢えずDocker Desktop for Mac (for Windows)をインストール
https://hub.docker.com/search?type=edition&offering=community&platform=desktop

上記のリンクからアカウント登録してパソコンに合うデスクトップアプリをインストール。

<details>
<summary>`参考リンク`</summary>
<div>
[【Docker Mac】Docker Desktop for MacとDockerComposeのインストール](https://www.public.ne.jp/2020/11/20/mac_docker_1/)  
[Dockerインストール手順＜Windows向け＞](https://sukkiri.jp/technologies/virtualizers/docker/docker-win_install.html)
</div>
</details>

## なんかdocker-composeが使えなかったので個別にインストール
上でインストールしたアプリにdocker-composeもついているはずなのに何故かdocker-composeコマンドが使えなかったので、ターミナルを開いて下記のコマンドを実行。

```terminal
sudo curl -L https://github.com/docker/compose/releases/download/1.27.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
```
結局原因がわからなかったが、取り敢えずdocker-composeコマンドが使えるようになったのでよしとする。(よくない)
下記のコマンドを打ってバージョンがでたらOK。

```terminal
$ docker -v
Docker version 20.10.8, build 3967b7d
$ docker-compose -v
docker-compose version 1.27.4, build 40524192
```

# ここから本格的に作っていきます
## ディレクトリを作っていく
`mkdir`や`touch`を使って、下記の構造のディレクトリを作っていく。`*`がついてるフォルダの名前はなんでもいい。

```
docker-nginx *
├── docker *
│   ├── php *
│   │   └── Dockerfile
│   ├── nginx *  
│   │   ├── Dockerfile
│   │   └── default.conf
│   └── mysql *
│       ├── Dockerfile
│       └── my.cnf
├── src *
├── .gitignore
├── .env
└── docker-compose.yml
```
## docker-compose.ymlを書く
`vi`コマンドまたはXcodeやVSCodeで上で作った空ファイルを開いてもいい。
<details>
<summary>`viコマンドの使い方`</summary>
<div>

```terminal
$ vi [ファイル名]
```
下記の状態になったら`i`を押す。

```
~ 
~ 
~ 
~ 
~ 
~ 
~ 
"[ファイル名]" 0L, 0B
```
下記の状態になったら書き込む。

```
~ 
~ 
~ 
~ 
~ 
~ 
~ 
-- INSERT --
```
書き終わったら`esc`を押し、`:wq`と打って`enter`を押す。
</div>
</details>

```yaml:完成形
version: '3.8'
# mysqlのテーブルやらなんやらを保存するやつ
volumes:
  mysql-volume: 
# 各コンテナの設定
services:
  # phpサーバー
  php:
    build: # Dockerfileで設定する
      context: .
      dockerfile: ./docker/php/Dockerfile
    volumes: # ./src/に入れたファイルが/usr/share/nginx/htmlに同期される。
      - ./src:/usr/share/nginx/html
  # nginxサーバー
  web:
    build: # Dockerfileで設定する
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    ports: # ポートの設定(後で.envでする)
      - ${WEB_PORT}:80
    depends_on: # 先にphpサーバーを立ち上げる
      - php
    volumes: # ./src/に入れたファイルが/usr/share/nginx/htmlに同期される。
      - ./src/:/usr/share/nginx/html
  # データベースサーバー
  db:
    build: # Dockerfileで設定する
      context: .
      dockerfile: ./docker/mysql/Dockerfile
    ports: # ポートの設定(後で.envでする)
      - ${DB_PORT}:3306
    environment: # ユーザとかパスワードとかの設定(後で.envでする)
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      TZ: 'Asia/Tokyo'
    volumes: # mysqlのテーブルやらなんやらを保存する
      - mysql-volume:/var/lib/mysql
  # PHPMyAdminサーバー
  phpmyadmin: # デフォルトのままで不都合ないのでDockerHubにあるイメージをそのまま使う
    image: phpmyadmin/phpmyadmin:latest
    environment:
      PMA_HOST: 'db' # ホストはデータベースサーバーのサービス名(26行目)
      PMA_USER: 'root' # デフォルトでログインしているユーザー(今回はルートユーザー)
      PMA_PASSWORD: ${DB_ROOT_PASSWORD} # 上記ユーザーのパスワード(今回はルートユーザーのパスワード)
    ports: # ポートの設定(後で.envでする)
    - ${PMA_PORT}:80
  # メール確認用サーバー
  # とりあえずメールがきちんと送信できたか見たいだけなので、簡単に導入できるMailHogを使っている。
  # 外部にメール送るにはIPアドレスの許可やらその他いろいろ面倒くさいので今回はやらない。
  mailhog:
    image: mailhog/mailhog
    ports: # ポートの設定(後で.envでする)
      - ${MAIL_PORT}:8025
      - "1025:1025"
```

## .envと.gitignoreを書く
.envにポート番号やデータベースの設定を書く。(`.`がつく隠しファイルは基本的にviなどのコマンドを使う。VSCodeとかでもいい。)

```:.env
WEB_PORT=8080
DB_PORT=3690
PMA_PORT=8081
MAIL_PORT=8025

DB_NAME=db_mydb
DB_USER=db_user
DB_PASSWORD=password
DB_ROOT_PASSWORD=rootpass
```
上記はあくまで一例。ポートはwebは80、DBは3306がよく使われるが、他のアプリ等で使っていることも考慮して適当にズラしている。
データベース関連も取り敢えず簡単な名前を使っているので、特にパスワードはもっと複雑なのにしたほうがいい。
###### 次は.gitignoreを書く
.envファイルをうっかりgithubとかに上げてしまうとやばいので、バージョン管理から外す。

``` :.gitignore
.env
```

## Dockerfileを書いていく

#### まずはPHPサーバー用のDockerfile
```Dockerfile:./docker/php/Dockerfile
FROM php:7.2-fpm
# タイムゾーン(東京)
ENV TZ Asia/Tokyo
# 色々パッケージとか追加(パッケージの操作管理などに使うツール類？とmysqliパッケージ)
RUN apt-get update \
&& apt-get install -y \
git \
zip \
unzip \
vim \
libpq-dev \
&& docker-php-ext-install pdo_mysql pdo_pgsql mysqli
# メール受信用のやつのSMTPサーバーとかポートなどいろいろ設定
RUN curl -sSLO https://github.com/mailhog/mhsendmail/releases/download/v0.2.0/mhsendmail_linux_amd64 \
&& chmod +x mhsendmail_linux_amd64 \
&& mv mhsendmail_linux_amd64 /usr/local/bin/mhsendmail \
&& echo 'sendmail_path = "/usr/local/bin/mhsendmail --smtp-addr=mailhog:1025"' > /usr/local/etc/php/conf.d/sendmail.ini
```

#### 次はnginx用
```Dockerfile:./docker/nginx/Dockerfile
FROM nginx:1.18-alpine
# タイムゾーン(東京)
ENV TZ Asia/Tokyo

# nginxのconfigファイル(default.conf)をコピー
COPY ./docker/nginx/*.conf /etc/nginx/conf.d/
# このコンテナに入った時のカレントディレクトリ
WORKDIR /usr/share/nginx/html
```

#### 最後にデータベース用
```Dockerfile:./docker/mysql/Dockerfile
FROM mysql:8.0
# 言語設定を変えるためにパッケージをインストール
RUN apt-get update
RUN apt-get -y install locales-all
# MySQLを日本語に対応させる
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
# タイムゾーン(東京)
ENV TZ Asia/Tokyo
# MySQLのconfigファイル(my.cnf)をコピー
COPY ./docker/mysql/my.cnf /etc/my.cnf
```

## nginxとMySQLの設定ファイルを書く

#### まずはnginxのdefault.conf
これに関してはちゃんと説明できないので詳しくは各自で調べてみて下さい

```conf:./docker/nginx/default.conf
server {
    index index.php index.html; # ここでトップページに表示するファイルを指定(先に書いた方が優先度が高い)
    server_name localhost;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /usr/share/nginx/html;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}
```

#### 次はMySQLのmy.cnf
設定できる項目はたくさんあるので適宜調べて書き足して下さい。

```conf:./docker/nginx/my.cnf
[mysqld]
user=mysql
character_set_server = utf8mb4
collation_server = utf8mb4_0900_ai_ci
# MySQL8.0の認証方式にPHP側では対応していないので従来の認証方式に変える
default_authentication_plugin=mysql_native_password

# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# Error Log
log-error = mysql-error.log

# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0

# General Log
general_log = 1
general_log_file = mysql-general.log

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4
```
<details>
<summary>`認証方式についてうまくいかなかった時`</summary>
<div>
#### 上記の6行目`default_authentication_plugin=mysql_native_password`を書いたにも関わらず`Warning: mysqli_connect(): The server requested authentication method unknown to the client [caching_sha2_password]`が出る時。

ファイルを書いている途中でdocker-composeで起動してみた場合などに、
`docker-compose.yml`の3,4行目に書いた`mysql-volume:`にデータが残っているから`my.cnf`を変えただけでは変更できないと思われる。

#### 対処法1：`mysql-volume:`を一度削除する
コンテナを稼働している場合は一度止めて、`docker-nginx`ディレクトリで下記のコマンドを実行する。

ボリューム一覧表示

```terminal
docker volume ls
```
指定のボリューム削除

```terminal
docker volume rm mysql-volume:
```
全部消すとき

```terminal
docker volume ls -qf dangling=true | xargs -r docker volume rm
```
この方法だと既にこのデータベースに作成してあるテーブルとかが全部消えるので注意。

#### 対処法2:MySQLのコンテナに入ってユーザごとに認証方式を変える
稼働しているMySQLのコンテナに入って下記のコマンドを実行する。  

docker-composeで起動しいる状態で
docker-nginxディレクトリでコマンドを実行してMySQLのコンテナに入る。

```terminal
docker compose exec db bash
```
下記のコマンドを順番に実行する。

```terminal
$ mysql -u root -p
[Enter password: 
~~略~~
# 現状の確認
[mysql> SELECT user, host, plugin FROM mysql.user;
+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| db_mydb          | %         | caching_sha2_password |
| root             | %         | caching_sha2_password |
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | caching_sha2_password |
+------------------+-----------+-----------------------+
6 rows in set (0.01 sec)
# rootユーザの認証方式を変える
mysql>　ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';
Query OK, 0 rows affected (0.01 sec)
```
</div>
</details>

## コンテナを起動
ここまでで一通り設定は終わったので、下記の`index.php`を`src`フォルダに突っ込む。

```php:index.php
<?php echo phpinfo(); ?>
```
#### コンテナを起動
`docker-nginx`ディレクトリで下記コマンドを実行してビルドする。

```terminal
docker compose build 
```
特に問題が起きなければ下記コマンドで起動

```terminal
docker compose up -d 
```
ブラウザを起動して`http://localhost:8080/`と打ち込むと、
PHPのバージョンとかの情報ページが表示される。(スクショの埋め込み方がわからない)
<br>
あとは`src`フォルダに`css`やら`js`やら色々突っ込んでいけば装飾とか色々反映される。

#### 各コンテナに入って作業するとき
コンテナに入って作業するとき(MySQLに初期データ追加したり、サーバーの設定をいじったりとか)は下記のコマンドを`docker-nginx`ディレクトリで実行。

```terminal
# PHPのサーバ
$ docker compose exec php bash
# nginxのサーバ
$ docker compose exec web ash
# データベースサーバ
$ docker compose exec db bash
```

#### コンテナを停止
下記コマンドで停止(MySQLのデータは`mysql-volume:`に残る)。

```terminal
docker compose -f docker-compose.yml down
```

## 参考資料
https://yutaro-blog.net/2021/04/28/docker-laravel-vuejs-1/

https://yutaro-blog.net/2021/04/29/docker-laravel-vuejs-2/

https://yutaro-blog.net/2021/04/30/docker-laravel-vuejs-3/

https://www.public.ne.jp/2020/11/20/mac_docker_1/

https://sukkiri.jp/technologies/virtualizers/docker/docker-win_install.html

https://qiita.com/mochizukikotaro/items/b398076cb57492980447

https://qiita.com/zongxiaojie/items/6b593ec4ce5e85bb342c

https://qiita.com/forests-k/items/eb0c07829cbc5e309059

https://qiita.com/Ikumi/items/b319a12d7e2c9f7b904d

https://qiita.com/kinoleaf/items/0b0002aa03993e58e2f0