Docker で PHP + PHP-FPM × Nginx × MySQL の開発環境を構築

本記事では、Docker を用いて PHP の開発環境を構築します。Docker に詳しくない方でも理解しやすいように、Docker の基礎概念と用語も解説しています。

本記事は、お使いのパソコンで **Docker** が利用できる状態であることを前提としてます。

まだ Docker をインストールされてない場合は、下記のリンクから Docker の公式ウェブサイトにアクセスし、使用している OS に合った Docker をインストールしてください。

[Get Started with Docker - Docker](https://www.docker.com/get-started/)


# Docker とは
Docker は、ホストマシン上でアプリケーションやサービスを**隔離された環境で実行**するために使用されるツールです。

Docker を使用することで、ホストマシンの環境に影響を与えることなく、開発環境を構築したり、アプリケーションをデプロイしたりすることができます。

## コンテナ と イメージ と Dockerfile
Docker で開発環境の構築を進める前に、重要な用語である**コンテナ**と**イメージ**と **Dockerfile** について説明します。また、本記事で使用する **Docker Compose** についても説明します。

### コンテナ （Container）
コンテナは、ホストマシン上で実行される**隔離された環境**です。コンテナは互いに独立して実行でき、ホストマシンの環境に依存せずに動作を実行できます。

### イメージ （Image）
イメージは、**コンテナの作成に使用される設計図**です。コンテナは、イメージを元に作成されます。`docker run` コマンドを使用してイメージから新しいコンテナを作成します。

イメージには、アプリケーションやサービスを実行するために必要な OS や依存関係、設定などが含まれています。

### Dockerfile
Dockerfile は、**イメージを作成するための手順が記述されたテキストファイル**です。

イメージは、**Docker Hub** などのイメージレジストリから既存のイメージをダウンロードして取得したり、`Dockerfile` と `docker build` コマンドを使用して自分で作成したりすることができます。

### Docker Compose
Docker Compose は、YAML ファイルを使用して**複数のコンテナを定義し、一括で起動・管理**するためのツールです。これにより、複数のコンテナから構成されるアプリケーションを簡単に管理できます。

本記事では、Docker Compose を使用して、Nginx、PHP-FPM（+ PHP）、MySQL のそれぞれに対し、独立したコンテナを作成し、開発環境を構築します。

### まとめ
+ **コンテナ**は実際にアプリケーションが動作する独立した環境
+ **イメージ**はコンテナを作成するための設計図
+ **Dockerfile** はカスタムのイメージを作成するために、イメージの作成手順を記述するテキストファイル
+ **Docker Compose** は複数のコンテナを定義し、一括で起動・管理するためのツール

![Docker とイメージとコンテナの関係](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2801269/065d7ead-afc1-255c-bfa5-ccec38d1704e.png)
Docker を使用する上で、コンテナは**隔離された環境**であるという認識は非常に重要です。この後使用するネットワークやボリュームを理解する上でもこの認識は役立ちます。

# Docker を用いた開発環境の構築
## ローカル環境
macOS Ventura v13.0.1 (MacBook Air M1 2020)
Docker v20.10.23

## 構築する開発環境
Web サーバ： Nginx 1.25
データベース： MySQL 8.0
言語： PHP 8.1

### 構成の全体図
Docker を使用して、Web サーバ層（Nginx）、アプリケーション層（PHP-FPM）、データベース層（MySQL）の3層アーキテクチャを組み合わせた環境（下記の図のグレーのエリア）を構築し、Web ブラウザ (クライアント)からのリクエストに対する処理を行えるようにします。
![構築する環境](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2801269/16006617-b3f4-ad21-ee70-e24b47b6a6ea.png)
今回構築する環境おけるそれぞれの層の役割について簡単に説明します。
 + Web サーバ層（Nginx）: Web サーバ層は、クライアントとの間でリクエストとレスポンスを処理を担当します。Web サーバ層は、クライアントからのリクエストを受け取ると、必要な処理をアプリケーション層に委譲します。
+ アプリケーション層（PHP-FPM）: アプリケーション層は、Web アプリケーションの実行や動的なコンテンツの生成とデータベースとの連携を担当します。PHP-FPM は PHP の実行環境であり、Web サーバから転送されたリクエストを受け取って PHP スクリプトを処理し、結果を Web サーバに返します。
+ データベース層（MySQL）: データベース層はデータの永続化と管理を担当します。アプリケーション層からの要求に応じてデータベースからデータを取得したり、データの更新や削除を行ったりします。



## 構築するディレクトリ
```
php-docker-demo/
    ├── docker/
    │   ├── mysql/
    │   │   └── my.cnf
    │   ├── nginx/
    │   │   └── default.conf
    │   └── php/
    │       ├── Dockerfile
    │       └── php.ini
    ├── src/
    │   └── index.php
    ├── docker-compose.yml
    └── .env
```

## プロジェクトフォルダの作成
任意のディレクトリで、プロジェクトフォルダを作成します。
今回はデスクトップに `php-docker-demo` という名前のプロジェクトフォルダを作成します。

ターミナルを開き、デスクトップに移動し、`php-docker-demo` フォルダを作成します。
```bash:ターミナル
cd desktop

mkdir php-docker-demo
```
## docker-compose.yml ファイルの作成
プロジェクトフォルダに移動し、`docker-compose.yml` ファイルを作成します。
```bash:ターミナル
cd php-docker-demo

# Mac （ターミナル）の場合
touch　docker-compose.yml 
```
この `docker-compose.yml` ファイルに、PHP-FPM（+PHP）、Nginx、MySQL の各コンテナを作成するための設定を記述します。

## Nginx コンテナ
まずは、Nginx コンテナを作成するための設定を記述します。

:::note info
Nginx はクライアントからのリクエストを受け取り、静的なコンテンツ（HTML、CSS、画像など）を提供する役割を持ちます。他にもリバースプロキシ、ロードバランサーなどさまざまな役割を果たすことができます。
:::

VSCode などのテキストエディタでプロジェクトフォルダを開き、`docker-compose.yml` ファイルを編集します。

```yaml:docker-compose.yml
# 特定のバージョンを指定し、このファイル内で使用できる文法が固定
version: '3'

# 起動したいコンテナを定義。本記事では3つのコンテナ（nginx、app、mysql）を定義
services:
  # Nginxの定義
  nginx:
    # Nginxコンテナに使用するイメージを指定
    image: nginx:1.25.0
    # ホストマシンのポートをコンテナのポートにマッピング
    ports:
      # <ホストマシンのポート>:<コンテナのポート>
      - 8000:80
  # app:
  # mysql:
```
### Nginx の設定
1. `services` ディレクティブ内に `nginx` という名前で定義。
    （任意の名前で定義できる）
2. `image` 内に Nginx コンテナに使用するイメージを指定。
    `nginx:1.25.0` はバージョン 1.25.0 の Nginx イメージを使用することを意味します。
    他のバージョンを使用したい場合は [nginx - Official Image | Docker Hub](https://hub.docker.com/_/nginx) の Supported tags を参照してください
3. `ports` キーで、ホストマシンとコンテナの間でポートのマッピング。
コンテナは隔離された環境なため、ホストマシン（のブラウザ）からコンテナ内の Nginx にアクセスするには、ポートのマッピングを行う必要があります。
`8000:80`と記述することで、ホストマシン上のポート `8000` を経由するすべてのリクエストはコンテナ内のポート `80` にリダイレクトされます。詳しくは [nginx - Official Image | Docker Hub](https://hub.docker.com/_/nginx) の Exposing external port を参照してください。


### docker-compose ファイルを実行
`docker-compose up` コマンドを用いて、コンテナを起動します。

`-d` オプションを使用してバックグラウンドモードで実行します。バックグラウンドモードで実行することで、ターミナルを別の作業に使用できます。

```bash:ターミナル
docker-compose up -d


[+] Running 7/7
 ⠿ nginx Pulled
    ...
[+] Running 2/2
 ⠿ Network php-docker-demo_default    Created
 ⠿ Container php-docker-demo-nginx-1  Started
```

コマンドを実行した結果、Nginx イメージの取得（ダウンロード）、**php-docker-demo_default** という名前のネットワークの作成、**php-docker-demo-nginx-1** という名前のコンテナの起動がそれぞれ正常に行われました。

:::note info
**ネットワーク**
コンテナは独立した環境なため、コンテナ同士を通信させるためには Docker のネットワークが必要になります。
Docker Compose を使用してコンテナを起動すると、デフォルトで新しいネットワークが作成されます。同じ Compose プロジェクト内のコンテナは、このデフォルトのネットワークを介して通信できるようになります。
:::

ホストマシンの Web ブラウザから `http://localhost:8000` にアクセスすると、Nginx コンテナ内の Web サーバが応答し、`Welcome to nginx!` と書かれた Nginx のデフォルトの画面が表示されます。
![Nginx のデフォルトの画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2801269/bbc620f0-0e20-ef9a-c1d5-2090894e57df.png)

Nginx が正常に起動できているのを確認したので、一度 `docker-compose down` コマンドを用いて、コンテナを停止し、削除します。

```bash:ターミナル
docker-compose down

[+] Running 2/2
 ⠿ Container php-docker-demo-nginx-1  Removed
 ⠿ Network php-docker-demo_default    Removed
```
:::note warn
`down` コマンドはコンテナの停止、削除だけでなく、Docker Compose を使用して作成したネットワークを停止し、関連するリソース（後述するボリュームなど）を削除します。
:::


## PHP-FPM コンテナ
続いて、**PHP-FPM コンテナ**を作成するための設定を記述します。

:::note info
PHP-FPM は、Nginx と連携して動作するアプリケーションサーバであり、Nginx 単体では実現できない動的なコンテンツの提供や処理を担当します。具体的には、PHP スクリプトを実行し、その結果を HTML として出力します。
:::

### PHP-FPM の設定
Nginx コンテナを作成する際には Docker Hub から取得した既存のイメージを使用しました。PHP-FPM コンテナは、`Dockerfile` を用いて、既存のイメージをベースに作成してたカスタムのイメージを元に構築します。

#### Dockerfileの作成
まずは、`Dockerfile` を作成し、編集します。
Dockerfile は `php-docker-demo/docker/php` ディレクトリ内に作成します。
```Dockerfile:Dockerfile
# イメージを取得
FROM php:8.1.18-fpm
# 独自のphp.iniファイル(PHPの設定ファイル)を 
# コンテナ内の/usr/local/etc/php/ディレクトリにコピー
COPY php.ini /usr/local/etc/php/

# パッケージやPHPの拡張モジュールをインストールするコマンド　を実行
RUN apt-get update && apt-get install -y \
	git \
	curl \
	zip \
	unzip \
    && docker-php-ext-install pdo_mysql

# 作業ディレクトリを/var/wwwに設定
WORKDIR /var/www
```
1. `FROM` でベースとなるイメージを指定。
1. `COPY` でホストマシン上の `php.ini` ファイル(後ほど作成します)をコンテナ内の`/usr/local/etc/php/`ディレクトリにコピー。
1. `RUN` の後に、実行したいコマンドを記述。
    + `apt-get update`コマンドを実行してパッケージリストを更新
    + `apt-get install` パッケージをインストール
        + `-y` オプションはインストール時に確認プロンプトが表示されないようにするためのオプション
    + `docker-php-ext-install` で PHP アプリケーションが MySQL に接続するための拡張モジュールをインストール
    + `\` はコマンドを複数行で記述するときに使用
    + `&&` は前のコマンドが成功した場合のみ、次のコマンドを実行するようにしたいときに使用
1. `WORKDIR` で作業ディレクトリを `/var/www` に設定。
   `WORKDIR` を指定すると、それ以降、コンテナ内のすべてのコマンド（ Dockerfile 内の `CMD` や `COPY` など）は、指定されたディレクトリ（この場合は `/var/www` ）で実行されます。

#### php.ini ファイルを作成
PHP の動作や機能の設定を記述するファイルである `php.ini` ファイルを作成します。ファイル内の記述は、Laravel Sail の [php.ini ファイル](https://github.com/laravel/sail/blob/1.x/runtimes/8.2/php.ini)を参考にしています。
`php.ini` ファイルに関して、詳しくは [PHP: 設定ファイル - Manual](https://www.php.net/manual/ja/configuration.file.php)や[PHP: php.ini ディレクティブのリスト - Manual](https://www.php.net/manual/ja/ini.list.php)を参照してください。

```ini:php.ini
[PHP]
post_max_size = 100M
upload_max_filesize = 100M
variables_order = EGPCS
```

#### php ファイルを作成
プロジェクトフォルダ内に`src` ディレクトリを作成し、`index.php` を配置します。
```php:index.php
<?php

echo '<h1>Hello World</h1>';
```

#### docker-compose.yml ファイルの編集
```diff_yaml:docker-compose.yml
version: '3'
services:
  nginx:
    image: nginx:1.25.0
    ports:
      - 8000:80
    volumes:
      # ./srcフォルダをコンテナ内の/var/www/htmlにマウント
      - ./src:/var/www/html
      # ./docker/nginxフォルダをコンテナ内の/etc/nginx/conf.dにマウント
      - ./docker/nginx:/etc/nginx/conf.d
    # 依存関係を設定
    depends_on:
      - app
  # PHP-FPMの定義
  app:
    build:
      # Dockerfileを格納するフォルダのパス
      context: ./docker/php
      # Dockerfileのファイル名
      dockerfile: Dockerfile
    volumes:
      # ./srcフォルダをコンテナ内の/var/www/htmlにマウント
     - ./src:/var/www/html   
  # mysql:
```
1. **nginx の設定**
    1. `nginx` 内に `volumes` を追加。
        * `./src:/var/www/hml`
           `ホストのパス:コンテナのパス`という書式で記述することで、**バインドマウント**(ホストマシン上のファイルやディレクトリを、コンテナ内に紐付ける)することができる。
        * `./docker/nginx:/etc/nginx/conf.d`
           独自に作成した `default.conf` ファイル（後ほど作成）を配置している nginx ディレクトリを `conf.d` ディレクトリに紐付けるための記述。
   2. `nginx` 内に `depends_on` を追加し、niginx コンテナが起動する前に app コンテナが起動するように依存関係を設定。
        nginx コンテナは app コンテナが起動していないと、（ app コンテナ内にある）Web アプリケーションにリクエストを転送できません。

2. **app の設定**
    1. `services` ディレクティブ内に `app` という名前で定義。  （任意の名前で定義できる）
    2. `app` キー内に `build` を追加し、`build` キー内に `Dockerfile` を格納するフォルダのパスと`Dockerfile`のファイル名を定義
    3. `app` キー内に `volumes` を追加
        * `./src:/var/www/hml`

:::note info
**バインドマウント**
Docker コンテナが独立した環境で実行されるため、コンテナ内に作成されたファイルは、コンテナが削除されると削除されます。また、コンテナ内のファイルにはコンテナ外部から直接アクセスすることができず、ホストマシン上のエディタ(VSCode など)を使用して編集することもできません。

Dockerでは**バインドマウント**という機能を利用することで、ホストマシン上のファイルやディレクトリをコンテナ内に紐付け、ホストマシン上のエディタでコンテナ内のファイルを編集したり、他のコンテナやホストマシンとデータを共有したりすることができます。
:::

#### nginx の設定ファイルの作成
初期の設定ファイルは、Nginx コンテナ内の`/etc/nginx/nginx.conf`と`/etc/nginx/conf.d/default.conf` です。

:::note info
ファイルの内容を確認したい場合は、`docker-compose exec nginx cat [ファイルパス]`コマンドを利用してください。
:::

`nginx.conf`には、Nginx の基本的な設定が記述されており、`default.conf`を読み込む設定がされています。

`default.conf` には、デフォルトで動作する Web サーバーの設定などが記述されていますが、PHP-FPM に接続するための設定がなされていません。

そこで、独自の `default.conf` を作成します。`docker` ディレクトリ内に `nginx` ディレクトリを作成し、`default.conf` ファイルを配置します。( `./docker/nginx` フォルダをコンテナ内の `/etc/nginx/conf.d` にマウントするように `docker-compose.yml` で設定済み)

`default.conf` ファイルには NGINX を PHP-FPM に接続するための設定を [PHP FastCGI Example | NGINX](https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/#connecting-nginx-to-php-fpm) を参考にしながら記述します。

```conf:default.conf
server {
  # port 80 で listen
  # docker-compose.ymlでホストマシン上のポート8000を経由するすべてのリクエスト
  # がコンテナ内のポート80にリダイレクトするように設定済み
  listen 80;
  # ドキュメントルートを設定
  # /var/www/htmlはソースコードを配置しているフォルダ
  root /var/www/html;
  # インデックスファイルを設定
  index index.php;

  location / {
    root /var/www/html;
    index index.php;
    try_files $uri $uri/ /index.php?$query_string;
  }

  location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    if (!-f $document_root$fastcgi_script_name) {
        return 404;
    }
    # https://httpoxy.org/ の脆弱性を緩和する
    fastcgi_param HTTP_PROXY "";
    # TCPソケットを使用してPHP-FPMと通信するための設定
    fastcgi_pass app:9000; 
    # スラッシュで終わるURIの後に追加されるファイル名を設定
    fastcgi_index index.php;
    # fastcgi_paramsファイルに含まれるFastCGIパラメータの設定を読み込む
    include fastcgi_params;
    # SCRIPT_FILENAME パラメータは、PHP-FPM がスクリプト名を決定する際に使用する
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  }
}
```

Docker 特有の設定である `fastcgi_pass app:9000;`について説明します。

Nginx と PHP-FPM は、TCP ソケットまたは Unix ドメインソケットを使用して通信することができ、今回は TCP ソケットを使用します。

`fastcgi_pass app:9000;` は TCP ソケットを使用して通信するための設定です。ポート番号が 9000 である FastCGI サーバ（PHP-FPM）にリクエストを送信することを意味します。

Docker を用いるとき、`fastcgi_pass` は ` 127.0.0.1:9000` ではなく、`サービス名:9000` とします。本記事では、`docker-compose.yml` ファイルで　`PHP-FPM`のサービスの名前を`app` としています。


他の設定は詳しく説明しませんが、以下を参考してください。
+ [Module ngx_http_index_module - NGINX](https://nginx.org/en/docs/http/ngx_http_index_module.html)
+ [Module ngx_http_core_module - NGINX](https://nginx.org/en/docs/http/ngx_http_core_module.html)
+ [Alphabetical index of variables - NGINX](https://nginx.org/en/docs/varindex.html)
+ [Module ngx_http_fastcgi_module - NGINX](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html)
+ [How to set up PHP, PHP-FPM and NGINX on Docker in Windows 10 + [Tutorial Into] | pascallandau.com](https://www.pascallandau.com/blog/php-php-fpm-and-nginx-on-docker-in-windows-10/)

### docker-compose ファイルを実行
`docker-compose up` コマンドを用いて、コンテナを起動します。

`--build` オプションを使用すると、コンテナを起動する前に `docker-compose.yml` ファイル内で定義されたサービスのイメージをビルドし直します。
```bash:ターミナル
docker-compose up -d --build
```
ホストマシンの Web ブラウザから `http://localhost:8000` にアクセスし、**Hello World** と書かれた画面が表示されていることを確認してください。

**Nginx** と **PHP-FPM** が正常に連携できているのを確認したので、一度 `docker-compose down` コマンドを用いて、コンテナを停止し、削除します。
```bash:ターミナル
docker-compose down
```

## MySQL コンテナ
最後に、MySQL コンテナを作成するための設定を記述します。

:::note info
**MySQL データベース**
MySQL は、リレーショナルデータベース管理システム（RDBMS）の一つです。
Web アプリケーションにおいて、リレーショナルデータベースは、データの永続化と管理を担当します。データベース内にテーブルや関係を作成し、データの格納・検索・更新・削除などの操作を行います。
:::
### MySQL の設定
Nginx コンテナを作成する時と同様に Docker Hub から取得した既存のイメージを使用します。

#### docker-compose.yml ファイルの編集
```diff_yaml:docker-compose.yml
version: '3'
services:
  nginx:
  # ...
  app:
    build:
      context: ./docker/php
      dockerfile: Dockerfile
    # コンテナ内で使用される環境変数を定義
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - ./src:/var/www/html
    # 依存関係を設定
    depends_on:
      - mysql
  # MySQLの定義
  mysql:
    # MySQL コンテナに使用するイメージを指定
    image: mysql:8.0
    # コンテナ内で使用される環境変数を定義
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      # 名前付きボリュームを MySQL コンテナに紐づける
      - mysqldata:/var/lib/mysql
      - ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
      # - ./docker/mysql/sql:/docker-entrypoint-initdb.d
    ports:
      - 3306:3306
volumes:
  # 名前付きボリュームの作成
  mysqldata:
```
1. app の設定
    1. `environment` キーで、コンテナ内で使用される環境変数を定義します。ここでは、PHP ファイル内で使用する MySQL のルートパスワード、データベース名、ユーザー名、パスワードを環境変数として指定しています。`${環境変数}`で別の場所（`.env` ファイル）で定義された変数の値を参照しています。
    2. app 内に `depends_on` を追加し、app コンテナが起動する前に mysql コンテナが起動するように依存関係を設定。app コンテナは mysql コンテナが起動していないと、（ app コンテナ内にある）Web アプリケーションはデータベースへの要求できない。
    
2. MySQL の設定
    1. services ディレクティブ内に `mysql` という名前で定義。 
    （任意の名前で定義できる）
    2. image 内に MySQL コンテナに使用するイメージを指定。
    3. `environment` キーで、コンテナ内で使用される環境変数を定義します。ここでは MySQL のルートパスワード、データベース名、ユーザー名、パスワードを環境変数として指定しています。
    4. app キー内に `volumes` を追加
       + `mysqldata:/var/lib/mysql`
         名前付きボリューム（データを永続化する）
       + `./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf`
         バインドマウント（ホストマシンの `my.cnf` とコンテナ内の `my.cnf` を紐づける）
    5. `ports` キーで、ポートの設定

3. 名前付きボリュームを設定
    1. `volumes` を追加。
    2. ボリューム名（今回は`mysqldata`）を記述。

:::note info
**名前付きボリューム**
Dockerコンテナは独立した環境で実行されるため、コンテナが削除されるとコンテナ内に保存されたデータは失われます。そのため、データを永続化する必要がある場合は、コンテナの外部にデータを配置する必要があります。

**名前付きボリューム** は Docker 上に確保した領域（ボリューム）をコンテナ内の特定のパスにマウント（関連付け）します。名前付きボリュームを使用すると、コンテナ内に保存されたデータが、コンテナを削除しても削除されません。また、複数のコンテナ間でデータを共有したりする際にも便利です。

関連：匿名ボリューム
:::

#### my.cnf ファイルの作成
**my.cnf** は、MySQL の設定ファイルです。
`docker` フォルダに `mysql` フォルダを作成し、`my.cnf` ファイルを作成。
```my.cnf
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
```

MySQL サーバーのデフォルトの文字セットを `utf8mb4` に、照合順序を  `utf8mb4_general_ci` に設定しています。

詳しくは、[MySQL :: MySQL 8.0 Reference Manual :: 4.2.2.2 Using Option Files](https://dev.mysql.com/doc/refman/8.0/en/option-files.html)を参照してください。

#### .env ファイルの作成
プロジェクトのルートディレクトリに `.env` ファイルを作成し、MySQL コンテナで使用する環境変数を記述します。`MYSQL_ROOT_PASSWORD` は必須の変数です。
MySQL コンテナで使用する環境変数について詳しくは、[mysql - Official Image | Docker Hub ](https://hub.docker.com/_/mysql)の Environment Variables を参照してください。

:::note info
Docker Compose はデフォルトで、`docker-compose.yml` ファイルと同じディレクトリにある `.env` ファイルを自動的に読み込みます。
:::

```.env
MYSQL_ROOT_PASSWORD=password
MYSQL_DATABASE=php-docker-db
MYSQL_USER=user
MYSQL_PASSWORD=password
```

### docker-compose ファイルを実行
`docker-compose up` コマンドを用いて、コンテナを起動します。

```bash:ターミナル
docker-compose up -d --build
```

MySQL コンテナが正常に稼働してるか `bash` を使用して確認します。
```bash:ターミナル
# 上記のコマンドを実行すると、MySQLコンテナのBashシェルが起動
# これにより、コンテナ内部でコマンドを実行することができます
docker-compose exec mysql /bin/bash

# コンテナ内にMySQLがインストールされているか確認
bash-4.4# mysql --version
mysql  Ver 8.0.33 for Linux on aarch64 (MySQL Community Server - GPL)

# mysql -u root -p　もしくは
#　mysql -u <ユーザ名> -p （<ユーザ名>はMYSQL_USERで設定した値）
bash-4.4# mysql -u root -p
# rootの場合はMYSQL_ROOT_PASSWORD、
# <ユーザ名>の場合はMYSQL_PASSWORDで設定した値を入力
Enter password: 

Welcome to the MySQL monitor.  Commands end with ; or \g.
...
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

#　データベースの一覧を表示
mysql> SHOW databases;
# MYSQL_DATABASEで設定した値(今回はphp-docker-db)があるか確認
+---------------------+
| Database            |
+---------------------+
| information_schema  |
| performance_schema  |
| php-docker-db　　　　　　　　　　　　 |
+---------------------+
3 rows in set (0.04 sec)

# USE データベース名; でデータベースを切り替え
mysql> USE php-docker-db;
Database changed

# 現在のデータベース内のテーブルの一覧を表示
mysql> SHOW TABLES;
# まだテーブルを作成していないので空
Empty set (0.01 sec)
```

MySQL コンテナが正常に稼働してるのを確認したので、次は PHP-FPM と MySQL が正常に連携できているのを確認します。

データベースに新しいテーブルを作成し、レコードを挿入します。
```bash:ターミナル
# データベースに新しいテーブルを作成
# CREATE TABLE テーブル名 ( 列名1 データ型1, 列名2 データ型2, ...);
mysql> CREATE TABLE mytable (id int, name varchar(255));
Query OK, 0 rows affected (0.07 sec)

# テーブルが作成されているか確認
mysql> SHOW TABLES;
+-------------------------+
| Tables_in_php-docker-db |
+-------------------------+
| mytable                 |
+-------------------------+
1 row in set (0.01 sec)

# レコードを挿入
mysql> INSERT INTO mytable (id, name) values (1, 'John Doe');
Query OK, 1 row affected (0.11 sec)

# レコードを取得
mysql> SELECT * FROM mytable;
+------+----------+
| id   | name     |
+------+----------+
|    1 | John Doe |
+------+----------+
1 row in set (0.01 sec)
```

`src` フォルダ に `db.php` ファイルを作成し、MySQL からデータを取得する処理を記述します。
```php:db.php
<?php

$host = 'mysql'; // MySQLコンテナのサービス名
$dbname = $_ENV['MYSQL_DATABASE'];
$username = 'root';
$password = $_ENV['MYSQL_ROOT_PASSWORD'];

# 新しいPDOオブジェクトを作成し、MySQLデータベースに接続
$db = new PDO("mysql:host={$host};dbname={$dbname};charset=utf8", $username, $password);

# SQL　文を実行
$stmt = $db->prepare('SELECT * FROM mytable');
$stmt->execute();

$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

foreach ($results as $result) {
    echo $result['id'] . '. ' . $result['name'] . PHP_EOL;
}
```

ホストマシンの Web ブラウザから `http://localhost:8000/db.php` にアクセスし、 **1. John Doe** と書かれた画面が表示されることを確認してください。

これで、PHP-FPM と MySQL が正常に連携できているのを確認できました。

```bash:ターミナル
# exitコマンドでMySQLへの接続を終了
mysql> exit
Bye

# exitコマンドでBashシェルセッションを終了
bash-4.4# exit
exit
```

**PHP-FPM** と **MySQL** が正常に連携できているのを確認したので、 `docker-compose down` コマンドを用いて、コンテナを停止し、削除します。
```bash:ターミナル
docker-compose down
```

# おわりに
本記事では、Docker Compose を用いて、Nginx コンテナ、PHP-FPM（+ PHP） コンテナ、MySQL コンテナを作成し、PHP アプリケーションの開発環境を構築しました。

今回の環境を構築する上で最低限必要な Docker の用語は解説しましたが、Docker について詳しく学びたい方は以下の記事が参考になるかもしれません。

https://zenn.dev/suzuki_hoge/books/2022-03-docker-practice-8ae36c33424b59

また、Nginx、PHP、MySQL それぞれの設定ファイルも作成しました。設定ファイルをカスタマイズしたい場合は、それぞれの設定ファイルの作成時に紹介した公式サイトのドキュメトを参照してください。

# 参考記事

https://youtu.be/-nRRZSsgEGc

https://youtu.be/l0jb-N5H52A

https://youtu.be/qv-P_rPFw4c