【Docker Network】PHP コンテナから MySQL コンテナに通信する

# はじめに
この記事では、Docker のネットワーク機能を利用して、PHP コンテナから MySQL コンテナへ通信できるようにする手順について記載します。
ネットワークへの理解促進の一環で行っています。

https://docs.docker.com/network/

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

- Windows 11
- Docker Engine 26.1.1
- MySQL 8.4.0
- PHP 8.3

# ネットワークの作成 `docker network create`

`docker network create` でネットワークを作成します。

https://docs.docker.com/reference/cli/docker/network/create/

```powershell
docker network create my-network
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/a37cd045-72f5-4658-3ef8-a93024072faf.png)

`docker network ls` で作成したネットワークを確認します。

https://docs.docker.com/reference/cli/docker/network/ls/

```powershell
docker network ls
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/e79d7782-c4b5-ec6b-df08-72afba8ffc96.png)

:::note
作成した `my-network` のほかに `bridge`、`host`、`none` というネットワークも存在します。これらは、Docker が自動で作成するデフォルトのネットワークです。この中の `bridge` は、コンテナ起動時にネットワークを指定しなかった場合にコンテナが接続するデフォルトネットワークです。デフォルトブリッジネットワークと呼ばれます。
:::

:::note
デフォルトブリッジネットワークに対し、今回のように自分で作成したネットワークをユーザー定義ブリッジネットワークと呼びます。コンテナ間通信では、ユーザー定義ブリッジネットワークを利用することが推奨されています。
:::

# ping コマンドが使える PHP イメージをビルドする
PHP コンテナから MySQL コンテナへ通信ができるようにする過程で、ネットワークの疎通確認を行いたいので、ping コマンドが使える PHP イメージをビルドします。

## Dockerfile を作成する
まずは、Dockerfile を作成します。

PHP イメージはタグが [8.3](https://hub.docker.com/layers/library/php/8.3/images/sha256-9d55083779a32244cae2d874020ce9b7d58bf915b885d2038f2d7cd0e55a2bde?context=explore) のイメージを利用します。

https://hub.docker.com/_/php

ping コマンドは `inputils-ping` というパッケージを利用します。 

https://packages.debian.org/en/sid/iputils-ping

```Dockerfile
FROM php:8.3

RUN apt-get update
RUN apt-get install -y iputils-ping
```

## イメージをビルドする
先ほど作成した Dockerfile を利用し、`docker image build` でイメージをビルドします。

```powershell
docker image build --tag my-php:ping .
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/6b8642ef-50b0-df82-1f60-d0fcc498f52a.png)

イメージが作成できたか確認します。

```powershell
docker image ls my-php
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/20badd70-84c0-b9d5-3d6d-8bbf94a0f3e8.png)

## 動作確認
イメージが作成できたので、コンテナを作成して、ping コマンドが利用できるか確認します。まだ MySQL コンテナは作成していないので、現時点では localhost と疎通確認推します。

```powershell
docker container run my-php:ping ping -c 3 -t 1 localhost
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/55be2519-dfd2-26d8-b56f-58ec8b7f0479.png)

# コンテナ起動時にネットワークに接続する `docker container run --network`
MySQL コンテナと PHP コンテナを起動し、作成した `my-network` に接続して通信できるようにします。
ネットワークへの接続は `docker container run` コマンドの `--network` オプションを指定します。

https://docs.docker.com/reference/cli/docker/container/run/#network

## MySQL コンテナ起動時にネットワークに接続する
まずは MySQL コンテナを起動して、ネットワークに接続します。

```powershell
docker container run               `
--name db                          `
--rm                               `
--detach                           `
--env MYSQL_ROOT_PASSWORD=password `
--env MYSQL_DATABASE=sample        `
--publish 3306:3306                `
--network my-network               `
mysql:8.4.0
```

コンテナが起動したか確認します。

```powershell
docker container ls
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/2e80c917-dca0-d9eb-1178-42f63936b9f9.png)

MySQL サーバーに接続します。

```powershell
mysql --host=127.0.0.1 --port=3306 --user=root --password=password sample
```

動作確認のため、users テーブルを作成し、Wyatt と Billy を追加します。

```mysql
create table users (id int, name varchar(32));
```

```mysql
insert into users (id, name) values (1, 'Wyatt');
```

```mysql
insert into users (id, name) values (2, 'Billy');
```

作成できたか確認します。

```mysql
select * from users;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/2dedc052-49b4-0000-5ad7-8192389a816a.png)

## PHP コンテナから MySQL コンテナに疎通できるか確認する
疎通確認のため、先ほどビルドした `my-php:ping` イメージを使い、起動した db コンテナに ping コマンドを実行します。

```powershell
docker container run --network my-network my-php:ping ping -c 3 -t 1 db
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/fc55d503-9cfb-37ba-1467-90af5b37991e.png)

## PHP コンテナで MySQL サーバーに接続するためにイメージを拡張する
PHP で MySQL サーバーに接続するためには `PDO_MYSQL` という拡張モジュール（ドライバー）が必要になります。

https://www.php.net/manual/en/ref.pdo-mysql.php

また、インストールには、公式の PHP イメージが用意してくれている `docker-php-ext-install` コマンドを利用します。

https://hub.docker.com/_/php

Dockerfile に `PDO_MYSQL` をインストールするための RUN インストラクションを追加します。

```Dockerfile
FROM php:8.3

RUN apt-get update
RUN apt-get install -y iputils-ping

RUN docker-php-ext-install pdo_mysql
```

更新した Dockerfile をもとにイメージをビルドします。
今回はタグを `pdo_mysql` とします。

```powershell
docker image build --tag my-php:pdo_mysql .
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/452cf514-764b-278f-cc9a-0d844d73ef18.png)

イメージが作成できたか確認します。

```powershell
docker image ls my-php
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/8e5ab668-cdbc-d114-fc10-37a12cc3abf8.png)

## PHP コンテナで MySQL サーバーに接続するコードを実装する
PHP イメージの準備ができたので、PHP コンテナで実行する PHP ファイル `main.php` を作成します。
ファイル内では、MySQL に接続し、users テーブルの中身を出力する処理を実装します。

```main.php
<?php

// Connect to DB
$dsn = 'mysql:host=db;port=3306;dbname=sample';
$username = 'root';
$password = 'password';
$pdo = new PDO($dsn, $username, $password);

// Echo user table
$statement = $pdo->query('select * from users');
$statement->execute();
while ($row = $statement->fetch()) {
  echo '- id: ' . $row['id'] . ', name: ' . $row['name'] . PHP_EOL;
}

// Disconnect
$pdo = null;

```

## PHP コンテナを起動して MySQL コンテナへ通信する
PHP コンテナ起動時に先ほど作成した `main.php` を PHP コンテナで実行し、 PHP コンテナから MySQL コンテナへ通信します。

```powershell
docker container run                              `
--rm                                              `
--mount type=bind,source="$(pwd)",target=/my-work `
--network my-network                              `
my-php:pdo_mysql                                  `
php /my-work/main.php
```

:::note
- `--mount type=bind,source="$(pwd)",target=/my-work` : ホストマシンで作成した `main.php` をコンテナで実行したいので、	`"$(pwd)"`（作業ディレクトリの絶対パス）を `/my-work` にバインドマウントします
- `--network my-network` : MySQL コンテナと同じネットワークに接続します
- `php /my-work/main.php` : バインドマウントの `"$(pwd)"` と同じディレクトリで実行
:::

ホストマシンで作成した PHP ファイルをコンテナで実行し、MySQL コンテナのデータを取得できました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/33bed8a4-6153-0301-d4a0-f19797ab9136.png)


# 参考
- [docker container run](https://docs.docker.com/reference/cli/docker/container/run/)
- [docker network create](https://docs.docker.com/reference/cli/docker/network/create/)
- [docker network ls](https://docs.docker.com/reference/cli/docker/network/ls/)
- [MySQL Functions (PDO_MYSQL)](https://www.php.net/manual/en/ref.pdo-mysql.php)
- [Networking overview](https://docs.docker.com/network/)
- [php - Official Image](https://hub.docker.com/_/php)
- [	鈴木亮「開発系エンジニアのためのDocker絵とき入門」秀和システム、2024](https://www.shuwasystem.co.jp/book/9784798071503.html)