Help us understand the problem. What is going on with this article?

DockerComposeでphpfpmコンテナとmysqlでコンテナ間通信する時に気をつけること

More than 1 year has passed since last update.

背景

docker-composeを使い、phpfpmコンテナとmysqlコンテナを作り、phpfpmの方からpdoでmysqlにアクセスする処理を書いたのですが、dsnやuser、passが合っているはずなのに何度やってもConnectionRefusedになってしまいました。

色々調べたり試したりしていった結果dsnで指定していたホストが間違っていたことが分かりました。個人的にとても勉強になったのでエラーを解消した手順や結果等をまとめます。

結論

・ホストPC(Mac)⇄コンテナ間通信で使われるIPとコンテナ同士の通信で使われるIPは異なる
・今回のようにphpfpmコンテナ⇄mysqlコンテナの通信の場合は、docker-compose psで表示されるホストではなく、コンテナの環境変数で確認出来るホストを使う

<追記>

・Twitterで教えて頂いたのですが、Docker-compose.ymllinksで以下のように別名を定義することで、別名をホストとして扱うことが出来るみたいです!
この別名を使えばコンテナのIPが変わっても同じ別名でコンテナ間通信が出来ます。

phpfpm:
  build: ./data/phpfpm/
  environment:
    TZ: "Asia/Tokyo"
  volumes:
    - "./htdocs:/var/www/html"
  links:
    - db:mysql

(db:mysqlのmysqlが別名)

もしくはmysqlの方でcontainer_nameを定義します。このcontainer_nameもホスト名として使えます。

db:
  image: mysql:5.7
  volumes:
    - ./data/mysql/conf.d:/etc/mysql/conf.d
  ports:
    - "3306:3306"
  container_name: mysql

解決手順

ホスト、ポート、ユーザー、パスワードを何度も確認

最初はホスト、ポート、ユーザー、パスワードのどれかが間違ってるのでは?と安易に考えました。

ユーザー、ホストの確認

ターミナルからmysqlにログイン後
> select User,Host from mysql.user;

cakeを使っているのでcakeのデバッガーでPDOに渡されている$dsnや$user,$passを確認し、齟齬がないか確認。

パスワードはcakeの設定ファイルを見てその値でターミナルからログイン出来ることを確認。

$ mysql -h 0.0.0.0 -u root -p

→ターミナルからログインする際の値と同じ値でアクセスしていることを確認
→ホスト、ポート、ユーザー、パスワードは間違ってなさそう。。

ポートの確認

可能性は低そうだけどポートが違う?
cakeからは3306にアクセスしていました。
mysqlコンテナのポートを確認

$ docker-compose ps
・・・ 0.0.0.0:3306->3306/tcp  

→ポートも間違ってなさそう。。
→php側から渡している変数は合ってそう

MySQLのユーザー権限や許容されているホストを確認

MySQL側の設定でアクセス権限が制限されているとか??

ターミナルからmysqlにログイン後
> select User,Host from mysql.user;
+---------------+-----------+
| User          | Host      |
+---------------+-----------+
| root          | %         |
| mysql.session | localhost |
| mysql.sys     | localhost |
+---------------+-----------+

'root'@'%'が許されている、ということは全てのホストに対して開かれているってこと。
(%はワイルドカード)

→MySQLのアクセス権限が原因でもない。。

コンテナ間通信がうまくいっているか確認

コンテナ間通信がやっぱりうまくいってないのか?
Macからとphpfpmのコンテナからの両者からpdoを実行して比較してみよう。
cakephpのルートに以下のようなファイルを作成しphp pdo.phpを実行してみる。

//pdo.php
<?php
$dsn = sprintf('mysql:host=%s:3306;dbname=%s',  '0.0.0.0', 'dbname');
$user = 'root';
$password = 'root';
$dbh = new PDO($dsn, $user, $password);
$sql = "SELECT version();";
foreach ($dbh->query($sql, PDO::FETCH_ASSOC) as $row) {
    print_r($row);
}

(参考)http://48n.jp/blog/2016/09/27/links-container-with-docker-compose/

MacからPDO実行

$ php pdo.php
//sql文が実行できてる
Array
(
    [version()] => 5.7.21
)

→成功

phpfpmからPDO実行

$ doc run --rm phpfpm php /var/www/html/pdo.php
Fatal error: Uncaught PDOException: SQLSTATE[HY000] [2002] Connection refused in ..

// phpfpm -> docker-compose.ymlで設定したphpfpmのサービス名
// /var/www/html/pdo.php -> pdo.phpのフルパス

→失敗><

→やっぱりphpfpm⇄mysql間の通信がうまくいってなさそう

mysqlコンテナの環境変数確認

Macから通信する時とコンテナ間で通信する時のホストが違うのかも?

mysqlコンテナのホストを確認

$ doc run --rm db(mysqlのサービス名) env | grep PORT | sort
DB_PORT=172.17.0.3

(参考)https://qiita.com/Arturias/items/75828479c1f9eb8d43fa

172.17.0.3・・・あ、これじゃね!!??

再度phpfpmからPDO実行

pdo.phpのホストを編集して再実行

$dsn = sprintf('mysql:host=%s:3306;dbname=%s',  '172.17.0.3', 'dbname');
$ doc run --rm phpfpm php /var/www/html/pdo.php
Array
(
    [version()] => 5.7.21
)

→やっとうまくいった!!!無事解決!

まとめ・感想

・ホストPC(Mac)⇄コンテナ間通信で使われるIPとコンテナ同士の通信で使われるIPは異なる
・今回のようにphpfpmコンテナ⇄mysqlコンテナの通信の場合は、docker-compose psで表示されるホストではなく、コンテナの環境変数で確認出来るホストを使う
・原因の切り分け同様、早く検証出来る方法を知ることも大事!
・原因の切り分けは検証したい条件以外同じ環境で比較する
・イライラせずに、一つずつ仮説を立ては潰すを繰り返しす

今回を学びに次はもっと効率よく解決出来るようもっと論理的に原因の切り分け、検証を行いたいと思います。

以上!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした