DockerでPHPとMySQLのシンプルすぎる環境構築を行う

## 執筆に至った経緯
- Docker使えたらカッコよくね？
↓
- ネットに転がってる環境構築は強すぎて理解できなくね？
↓
- 最小構成の環境構築を知って、そこから自分が必要な部分を拡張していったら理解できるんじゃね？

## 想定読者
- Dockerの基礎的なことは何となく分かったが、実際に使ってみたい方。
- 他人の作ったDocker環境は使ったことがあるが、自分では構築したことがない方。

## 今回のゴール
- DockerでPHPとMySQLの環境構築を行う。
- 具体的には以下３つが実行できたら完了とする。
    - PHPコンテナ内に入り、helloworldを出力する。
    - MySQLコンテナ内に入り、適当なSQL文を実行する。
    - PHPからMySQLに接続する。

## 実行環境と筆者のレベル感
### 実行環境
- OS : Windows10 Pro
- プロセッサ : 11th Gen Intel(R) Core(TM) i7-11370H @ 3.30GHz   3.00 GHz
- 実装RAM : 16.0GB
- システムの種類 : 64 ビット オペレーティング システム、x64 ベース プロセッサ
- DockerDesktop : 4.11

### 筆者のレベル感
- 2021年10月から勤務開始した新米エンジニア。
  ともに育ちましょう。

## ファイル構成
```
.
|-- docker
|   |-- mysql
|   |   |-- Dockerfile
|   |   |-- data
|   `-- php
|       `-- Dockerfile
|-- docker-compose.yml
|-- src
    `-- index.php

```

## ステップ１　PHPのDockerfileを作成
```:Dockerfile
FROM php:7.2-apache
RUN apt-get update  \
  && docker-php-ext-install pdo_mysql mysqli mbstring

WORKDIR /var/www/html

EXPOSE 80
```
### １行目
- FROM命令でPHPのDockerイメージを引っ張ってくる。
- 「:」はタグと呼ばれるものでバージョンやオマケを指定することができる。
今回の場合は「PHPのバージョン7.2が欲しい、ついでにapacheつけといて」という意味。
DockerHubに色々載っていて楽しいので一読をおススメ。

### ２～３行目
- RUN命令で各種パッケージのインストール等を行う。
- apt-get updateでインストール可能なパッケージ一覧を更新する。
    - 実際にインストール等は行わない。
- バックスラッシュで改行できる。&&でコマンドとコマンドを繋ぐ。
- docker-php-ext-installで各種拡張機能をインストール。
    - docker-php-ext-installはPHP標準拡張機能のインストーラー。
    - pdo_mysqlはPHPからMySQLに接続するための拡張機能。
    - mysqliもPHPからMySQLに接続するための拡張機能。
   PDOかmysqliの二択っぽくて、片方だけでOKそうだが、両方学習したいのでどちらも入れておく。
    - mbstringはmb_substrみたいなマルチバイト文字列に対応したメソッドを使うための拡張機能。

### ５行目
- WORKDIR命令でこの命令以降に実行したコマンドの作業場所およびコンテナに入った際のディレクトリを指定する。
    - だいたいどの記事を見てもこのパスが書いてあるので、とりあえずあまり難しく考えない方が良いのかも。

### ７行目
- EXPOSE命令でコンテナのポート（玄関）を指定。
    - 玄関を開けているだけなので、ホストとはまだ繋がってません。玄関の鍵だけ開けたっていう感じ。
    - 実際にポートを開けているわけではない様子。コメント的役割のようですね。
 参考文献の『DockerfileのEXPOSEは実際に何かポートを開けているわけではない』参照。
    - Dockerhubを見るに、php-apacheのイメージ内でWORKDIRやEXPOSEの設定はされているっぽい。

## ステップ２　MySQLのDockerfileを作成
```:Dockerfile
FROM mysql:8.0

EXPOSE 3306
```
### １～３行目
- FROM命令でMySQLイメージを引っ張ってきて、EXPOSE命令で3306番がコンテナのポートだと記載。
    - 前述のとおりコメント的意味合いの様子。
- M1 Macを使用の場合はbuild時にエラーが出るかも知れません。
  執筆時点ではMySQLコンテナはM1対応してないみたいです。
    - 回避方法ありまして、参考文献の『M1 MacでDockerを使った開発環境構築にハマった』参照。

## ステップ３　docker-compose.ymlの作成
```docker-compose.yml
version: '3'
services:
  app:
    build:
      context: ./docker/php
      dockerfile: Dockerfile
    volumes:
      - ./src:/var/www/html
    ports:
      - "80:80"
    depends_on:
      - db

  db:
    build:
      context: ./docker/mysql
      dockerfile: Dockerfile
    volumes:
      - ./docker/mysql/data:/var/lib/mysql
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=pass
```
### １行目
- docker-composeのバージョンを設定。
    - バージョンによって微妙に書き方が異なるっぽい。

### ２行目
- servicesに使用するコンテナを列挙する。
    - 今回はappコンテナとdbコンテナを作成。

### ４～６行目
- build内にdocker-compose buildをした際の挙動を記述。
- contextとはbuildコマンドを実行する場所。
    - 今回はDockerfileのある場所を指定すれば良さそう。
- dockerfileはDockerfileの場所を指定。
    - 上記で./docker/phpでbuildコマンドを実行するところまでは指定しているので、ファイル名だけ記述。

### ７～８行目
- volumesでローカルファイルをDockerコンテナ内に結び付ける。
    - ローカルのsrc以下のファイルがDockerコンテナ内の/var/www/html内にマウント（≒コピー）される。
   ローカルのsrcにファイルを置くと、Dockerコンテナ内の指定しているディレクトリにコピーされ、逆もまた然り。

### ９～１０行目
- コンテナのポート（玄関）とホストのポート（玄関）の間に道を作る。
    - これでホスト側でlocalhost:80にアクセスすると、コンテナの80にアクセスされる。
   コンテナ側の80番は決まっている値なので、Dockerhubでポートが何番かを確認する。

### １１～１２行目
- このように設定すると、先にdbコンテナが立ち上がってから、appコンテナが立ち上がるようになる。

### １４～１７行目
- 同様なので省略。

### １８～１９行目
- MySQLのデータをローカルにコピーしている。
  そうしないとコンテナを消してしまうとデータが吹き飛ぶため。

### ２０～２１行目
- 同様なので省略。

### ２２～２３行目
- MySQLの環境変数をここで定義。最低限ということでrootユーザーのパスワードのみ。
    - 別にenvファイルを作成してマウントしてしまっても良いのかも。

## 動作検証
### PHPコンテナ内に入り、helloworldを出力する。
- 何はともあれコンテナ起動

```
$ docker compose up -d
```

- index.phpにhelloworldを実装

```index.php
echo 'helloworld';
```

- PHPコンテナ内に入って、実行して無事出力を確認
 - ちなみにコンテナからはexitで出られます。

```
$ docker compose exec app bash
root@123456789123:/var/www/html# php index.php
helloworld
```

### MySQLコンテナ内に入り、適当なSQLを実行する。

```bash
$ docker compose exec db bash
root@987654321987:/# mysql -u root -p
Enter password:  #docker-compose.ymlで定義したパスワードを入力
mysql > SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)
```

### PHPからMySQLに接続する。
- 今回はmysqliを使ってデータベースへ接続。
    - new mysqliの第１引数にはデータベースのコンテナ名を記載。
    - 第２引数と第３引数にログインするユーザーの名前とパスワードを渡す。
   rootユーザーは初期状態で設定されていて、パスワードはdocker-compose.ymlで設定済。

```index.php
$mysqli = new mysqli('db', 'root', 'pass', 'mysql');
if($mysqli->connect_error) {
    echo '接続失敗'.PHP_EOL;
    exit();
} else {
    echo '接続成功'.PHP_EOL;
}
```

- PHPコンテナ内に入って接続成功を確認。
    - MySQLコンテナ内で一部設定変更しないとエラーが出ます。
参考文献の『MySQL8.0 認証方式を変更する(Laravel5)』参照のこと。

```bash
$ docker compose exec app bash
root@123456789123:/var/www/html# php index.php
接続成功
```

## 今後の課題
- php.iniとmy.cnfを設定する。
- PHPのデバッグが出来る環境を構築する。
- 実際にPHPからMySQLへデータの挿入および呼び出しを行う。
- その他必要な拡張機能を追加していく。
- Laravel環境構築編書きました。

https://qiita.com/2san/items/6e8af71f3186ad300538

# 参考文献
以下のサイトおよび記事を参考にさせていただきました。
ありがとうございます！
[・Docker初心者のDockerfileを理解する](https://www.webtomoblg.net/web/docker-beginner08/#toc6)
[・docker-compose.ymlのbuild設定はとりあえずcontextもdockerfileも埋めとけって話](https://qiita.com/sam8helloworld/items/e7fffa9afc82aea68a7a)
[・Docker Composeでphpでmysqlにアクセスする](https://zukucode.com/2019/06/docker-compose-mysql.html)
[・docker-composeでPHPとMySQLを連携させてみる](https://qiita.com/ayuzaka/items/b56122e2ecd94022a7b6)
[・【PHP】peclとは何か？pecl install と docker-php-ext-installの違い](https://qiita.com/shizen-shin/items/0d6a9cbeb16047c7d47a)
[・mysqli の忘備録 -PDOとmysqliの違い-](https://qiita.com/makimoch/items/00dd94278ed1a8f8478a)
[・[PHP] mysqli使い方まとめ（MySQL接続～SELECT実行まで）](https://qiita.com/yasumodev/items/bd2ba476f31804d527d3)
[・MySQL8.0 認証方式を変更する(Laravel5)](https://qiita.com/ucan-lab/items/3ae911b7e13287a5b917)
[・【MySQL】PHPで接続できないとき](https://motomotosukiyaki.com/mysql-from-php-server-requested-authentication-method-unknown-to-the-client)
[・M1 MacでDockerを使った開発環境構築にハマった](https://fixel.co.jp/blog/docker-m1mac/)
[・DockerfileのEXPOSEは実際に何かポートを開けているわけではない](https://shinkufencer.hateblo.jp/entry/2019/01/31/233000)