ポート番号の競合でハマった話

この前案件でDocker＋cakePHP3の仮想環境を作っていたのですが、構築したDBサーバーに接続できない！

そして、色々調べてみるとポート番号が競合してたと判明。

今まで正直、ポート番号って無意識に仮想環境構築していたから、解決まで引くほど時間が掛かった、、、。

そこで！議事録の意味も込めてポート番号問題が発生して解決までの流れを書き留めときます。

仮想環境についてちょっと詳しくなりたい人、ちょうどポートについて調べている人の手助けになれば幸いです。

## 0.何が起きた？

まず、状況説明から、、、。

・cakePHP3(PHPのフレームワーク)
・Docker(仮想環境実行ツール)
・MySQL Workbench(MySQLの管理ツール)
・MySQLサーバーのポート番号：3306

以上の条件で開発環境を構築。

しかし、MySQL Workbenchから仮想環境のMySQLサーバーへ接続できない。なんで？

ちなみに、DockerのDBコンテナにアクセスしてSQLサーバーに繋ぐと接続できる。なんで？

そして、もしや！と思いdocker-compose.yml(Dockerの仮想環境を設定するファイル)を3306から3307で設定するとMySQL Workbenchでも接続できた！

ということは、ポートに問題がある！と言う考えに至った訳です。

## 1.そもそもポートとは何か？
まず、「①ポートって通信機器に使われるLANポートやUSBポート」、「②TCP/IP通信で使われるポート」の2種類があるんですが、ここでは②の方を取り上げます！

TCP/IPとは、インターネット通信するときの約束事のようなもの。

例えば、手紙を届けるには住所、宛先、手紙の内容が必要ですよね？インターネットでも世界共通でも通信方法が共通化されています。

そして、通信先(手紙で言う住所)を役割を担っているのがIPアドレス。

例えば、GoogleだったらIPアドレスは172.217.175.110。実際IPアドレス直接売ってもGoogleのトップページへ行けます。

もちろん、仮想環境を作るときもIPアドレスは必要です！

しかし、仮想環境を作る時、通信する相手は「自分自身」。そのため、自分自身専用のIPアドレスが設定されます。
※127.0.0.1これをlocalhostと呼びます。

でも、仮想環境って案件ごとにたくさん作るし、用意するサーバーもたくさん！

127.0.0.1で通信しても、どのサーバーにアクセスしたらいいか判断できません。

そこで、必要なのがポート番号です。

WEBサーバーなら80番、DBサーバーなら3306番と、設定されたポート番号を指定することで、

「このサーバーにアクセスしますよ！」と伝えることができるのです。
※logalhostの場合、127.0.0.1:8889(もしくはlocalhost:8889)というように「:」を付けて指定します。

そして、仮想環境を構築するときは、「このサーバーはこのポート番号だよ」という風に指定しておく必用があります！

## 2.じゃあDockerでポート番号はどう指定している？
もちろん、Dockerでもポートを指定するところはあります！

じゃあどこ？それはズバリdocker-compose.ymlというファイルです！

例えば、DBコンテナを決める部分
```
  db:
    container_name: db
    image: mysql:5.7.39
    environment:
      - MYSQL_DATABASE=｛パスワード｝
      - MYSQL_USER=｛ユーザーネーム｝
      - MYSQL_PASSWORD=｛パスワード｝
      - MYSQL_RANDOM_ROOT_PASSWORD=yes
      - TZ=Japan
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    ports:
      - "3306:3306"
    volumes:
      - db-korn:/var/lib/mysql:cached
    platform: linux/x86_64
```

この赤のportsという部分。

「3306:3306」がありますね。

これは、「ポート番号を3306で指定してアクセスしてきたら3306ポートのサーバーにアクセスしてね！」という意味です。

ちなみに、Dockerはdocker-compose.ymlファイルを変更することでポートを変更できます。
※だいたいDBサーバーはポート3306に設定することが多いですね。なんでだろう。

しかし、MAMPだとDBサーバーは3306番に設定可能です！

だから、MAMPを立ち上げてDockerを立ち上げてたら、DBサーバーのIPアドレスはどっちも「127.0.0.1:3306」。

つまり、ポートも被って競合しちゃうことがあります。

## 3.ポートが競合するとどうなる？
普通は、サーバーが起動できません。

しかし、私の場合ポート競合していたのはデーモン(元々MACに備わっているOS)のMySQLとDoker仮想環境のMySQL。

そのため、仮想環境のMySQLサーバーは立ち上がりました。

そして、MySQL Workbench(MySQLの管理ツール)でポート3306で接続しようとするとデーモン側に接続しようとして失敗する自体に！

ポート自体を理解できなかった私。

先輩のサポートなしでは到底たどり着かなかった答えでしょう。

4.ポート競合の対処方法(私の場合)
コマンドで3306番ポートを使っているか確認
まずは、コマンドで他のアプリケーションが3306番ポートを使っていないか確認。
```
sudo lsof -P -i:3306
COMMAND   PID          USER   FD   TYPE         DEVICE SIZE/OFF NODE NAME
MySQLWork 386 {ユーザー名}   14u  IPv4 0x7f67e772a605      0t0  TCP localhost:50882->localhost:3306 (CLOSE_WAIT)
```

すると、3306番を使っているプロセス(実行している処理)の一覧が表示されます。

ここの「PID」に注目！

PIDとは、プロセスIDの略でプロセスを特定するのに必用なもの。

プロセスを指定するときにもPIDが必用なので、「PIDは大事！」と覚えておきましょう！

PIDを特定してKILLする
1番簡単な方法は、今3306番で行われているプロセスを終了させる方法です。

コマンドにて
```
sudo kill -9 386←ここはプロセスID
```
先程検索したPIDを指定して削除！

もう一度「sudo lsof -P -i:3306」コマンドを打って、一覧にプロセスが表示されなければKILLできたということです。

KILLできない場合はどうする？
私の場合、なぜか3306番ポートのプロセスが消えませんでした、、、。

PIDをアクティビティモニタ(MACに備わってるプロセスを見るアプリ)で称号してみると、3306番を使っていたのはmysqldと判明。

mysqldは、デーモン(マックOSの中で常駐しているソフト)を表す表記で、これがKILLではなかなか消せない。

そこで、デーモン側のMySQLが利用しているポート番号を書き換えることにしました！

MySQLのポートを変更
MySQLの設定ファイルはmy.cnfというファイルで行われます。

そのファイルを編集！
```
vim /usr/local/etc/my.cnf
```

[mysqld]の下にポートを追加します。※今回は3300に変更

```
[mysqld]
# Only allow connections from localhost
bind-address = 127.0.0.1
port=3300
```

これでMySQL Workbenchで3306ポートに接続しても、Docker仮想環境のMySQLサーバーに接続されるようになりました！あぁ、長い戦いだった、、、。

## 5.ポート番号の競合を起こさないために
自分の反省点から、「ポートの理解」と「管理」がかなり大切だと思いました。

正直、今回のトラブルまではポートを意識したことがなったので、、、。

しかも、MAMPやXAMPはポートを自動的に振り分けて環境を作ってくれるので、かなり便利！な反面、ポート設定の知見が薄れることもあります。

そのため、仮想環境を作るときはポートの設定ができなくても、設定しているファイルくらいは知るようにします。

そして、仮想環境を複数持っている場合はポート管理も重要。

ポート番号を一覧にしたり、被らないようにチームで共有したりと、結構周りを巻き込んだ動きが必用かと思います。

でも、今回のことで結構ポートの奥深さを知れたしラッキーでした！