[Docker] Docker コンテナの PHP コンテナから別コンテナ上の MySQL に接続する方法

こんにちは。最近、 Beat Saber という VR のリズムゲームにハマっている k-so16 です。リズムに合わせて腕を動かすゲームなのですが、普段の運動不足がたたって 2 日で腕が筋肉痛になりました(笑)

PHP をより理解するために、 パーフェクト PHP という参考書を手元において勉強しているのですが、その過程でローカルの環境を汚すことなく PHP と MySQL の環境を用意したくなり、 Docker を使うことにしました。それぞれのコンテナは問題なく動くことを確認したのですが、 PHP のコンテナから MySQL に接続しようとしたところ、以下のようなエラーが発生しました。

Fatal error: Uncaught PDOException: could not find driver in /path/to/php/program:2

MySQL の PDO ドライバーが入っていない ということで、ドライバーをインストールしたら解決すると思っていたのですが、次は以下のようなエラーが発生しました。

Fatal error: Uncaught PDOException: SQLSTATE[HY000] [2002] No such file or directory in /path/to/php/program:2

今度は MySQL のコンテナに接続できていない ようでした。 接続先をループバックではなく、 MySQL のコンテナに割り振られるアドレスを指定 する必要があるようです。

本記事では、 Docker の PHP コンテナから 別のコンテナ上で稼働する MySQL に接続する方法 を紹介します。

本記事で想定する読者層は以下の通りです。

  • Docker の基本的な使い方を知っている
  • PHP および MySQL の基礎的な設定の知識を有している

下準備

使用するコンテナ

本記事で使用する Docker コンテナは以下の通りです。

  • php:apache
  • mysql:5.7.27

PHP のライブラリが MySQL 8.x 系のデフォルトの認証方式 (caching_sha2_password) に対応していないので、 5.x 系のバージョンを採用することにしました。 MySQL の認証方式を手動で変更することで、 8.x 系にも対応することは可能ですが、本記事では省略します。

実行環境の準備

PHP と MySQL のコンテナをそれぞれ立ち上げます。 PHP のコンテナはポートフォワーディングとボリュームのマウントの設定を、 MySQL のコンテナはポートフォワーディングとユーザー認証およびデータベースの設定をコンテナ起動時に行うようにしました。

以降、本記事の例では、 PHP コンテナには PHP のソースコードを格納しているディレクトリ /path/to/src/dir/var/www/html にマウントし、ホスト側のポート 8080 を PHP コンテナのポート 80 にフォワーディングします。 MySQL はユーザー名とデータベース名を docker とし、ホスト側のポート 3306 を MySQL コンテナのポート 3306 にフォワーディングします。

各コンテナを立ち上げるコマンドは以下の通りです。

docker run -d --name test-php-server -v /path/to/src/dir:/var/www/html -p 8080:80 php:apache
docker run -d --name test-mysql -e MYSQL_ROOT_PASSWORD=secret -e MYSQL_USER=docker -e MYSQL_PASSWORD=secret -e MYSQL_DATABASE=docker -p 3306:3306 mysql:5.7.27

PHP のソースコードには、 MySQL コンテナに接続するための、以下のようなコード片を持つプログラムが存在するとします。

$con = new PDO('mysql:dbname=docker;host=localhost', 'docker', 'secret');

ライブラリのインストール

MySQL への接続に必要なドライバーがインストールされているかを確認 します。 phpinfo() で有効なドライバーを確認すると、 MySQL の PDO ドライバーがインストールされていませんでした。

Linux や FreeBSD などの OS では、 aptpkg などのパッケージマネージャー経由でドライバーをインストールしますが、 PHP の Docker コンテナには、 docker-php-ext-install という専用のコマンドが準備されているので、そちらを利用します。

MySQL の PDO ドライバーをインストールするコマンドは以下の通りです。コマンドはコンテナに入ってから実行します。 PDO がインストールされていない場合、 docker-php-ext-install の引数に pdo も付与して実行します。

docker exec -it test-php-server bash # PHP のコンテナに入る
docker-php-ext-install pdo_mysql

接続設定

まず、 MySQL のコンテナ ID を docker ps コマンドなどで確認 します。

以下は実行結果の例です。本記事の場合、 MySQL のコンテナ ID は ebc8580e8dd5 です。

CONTAINER ID    IMAGE           COMMAND                  CREATED        STATUS          PORTS                               NAMES
ebc8580e8dd5    mysql:5.7.27    "docker-entrypoint.s…"   5 hours ago    Up 5 hours      0.0.0.0:3306->3306/tcp, 33060/tcp   test-mysql
0d7fa361f6a2    php:apache      "docker-php-entrypoi…"   5 hours ago    Up 8 minutes    0.0.0.0:8080->80/tcp                test-php-server

次に、 MySQL のコンテナに入り、 /etc/hosts を確認 します。コンテナ ID に割り振られている IP アドレスが MySQL コンテナの IP アドレスです。本記事の場合、 172.17.0.3 が IP アドレスになります。

127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3      ebc8580e8dd5

最後に、 PDO のコンストラクタの第 1 引数の host= の箇所を、 MySQL コンテナの IP に書き換え ます。

$con = new PDO('mysql:dbname=docker;host=172.17.0.3', 'docker', 'secret');

これで PHP コンテナから MySQL コンテナに接続できるようになりました。

Docker のコンテナ間通信を用いる方法

localhost のポートを用いる以外の方法として、 コンテナ間通信 があることを kenzauros さんに教えてもらいました。

Docker のコンテナ間通信を実現するには、次の 2 通りの方法があります。

  • Docker ネットワークを用いる方法
  • --link オプションを用いる方法

--link オプションを用いる方法は、現在ではレガシーな機能であり、非推奨とされているので、本記事では Docker ネットワークを用いる方法のみを紹介します。

Docker ネットワークを用いる方法

まず、 docker network create コマンドで Docker ネットワークを作成します。

docker network create test-network

Docker ネットワークを確認するには、 docker network ls コマンドを実行します。

docker network ls
NETWORK ID      NAME            DRIVER    SCOPE
6681a32a3d6f    bridge          bridge    local
bc8ace895846    host            host      local
74bc6dce3a53    test-network    bridge    local
6f730504383f    none            null      local

コンテナを Docker ネットワークに接続するためには、 docker run コマンドでコンテナを作成する際に、 --network オプションで作成した Docker ネットワークを指定します。

本記事の例の場合、コンテナの作成コマンドは以下のようになります。

docker run -d --name test-php-server --network test-network -v /path/to/src/dir:/var/www/html -p 8080:80 php:apache
docker run -d --name test-mysql --network test-network -e MYSQL_ROOT_PASSWORD=secret -e MYSQL_USER=docker -e MYSQL_PASSWORD=secret -e MYSQL_DATABASE=docker -p 3306:3306 mysql:5.7.27

コンテナ間通信を用いて MySQL に接続する場合、 PDO のコンストラクタの第 1 引数の host= の箇所を、 MySQL のコンテナ名に書き換え ます。本記事の例の場合は、 host=test-mysql となります。

$con = new PDO('mysql:dbname=docker;host=test-mysql', 'docker', 'secret');

本セクションを記述するにあたって、以下のページを参考にしました。

Docker入門(第五回)〜コンテナ間通信〜 | さくらのナレッジ

まとめ

本記事のまとめは以下の通りです。

  • docker-php-ext-install で PDO および MySQL の PDO ドライバーをインストールする
  • MySQL コンテナの /etc/hosts から IP アドレスを確認して PDO コンストラクタに渡すホストに設定する

以上、 k-so16 でした。Docker を使いこなせるようになるには修行が必要そうですね(笑)

SNSでもご購読できます。