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

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

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

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

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

```yml
phpfpm:
  build: ./data/phpfpm/
  environment:
    TZ: "Asia/Tokyo"
  volumes:
    - "./htdocs:/var/www/html"
  links:
    - db:mysql
```
(db:mysqlのmysqlが別名)

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

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

#解決手順
##ホスト、ポート、ユーザー、パスワードを何度も確認
最初はホスト、ポート、ユーザー、パスワードのどれかが間違ってるのでは？と安易に考えました。

###ユーザー、ホストの確認

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

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

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

```console
$ mysql -h 0.0.0.0 -u root -p
```

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

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

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

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

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

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

```console
ターミナルから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`を実行してみる。

```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実行

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

###phpfpmからPDO実行

```console
$ 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コンテナのホストを確認

```console
$ 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のホストを編集して再実行

```php
$dsn = sprintf('mysql:host=%s:3306;dbname=%s',  '172.17.0.3', 'dbname');
```

```console
$ doc run --rm phpfpm php /var/www/html/pdo.php
Array
(
    [version()] => 5.7.21
)
```

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


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

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

以上！