Dockerで「bind: address already in use」エラー

# 概要

Dockerコンテナを立ち上げようとしたときに、`bind: address already in use`エラーが出た時の対処法。

# エラー内容

* `docker-compose up --build`を実行したところ発生。

```docker:エラーメッセージ
Attaching to hoge-web
Error response from daemon: 

# ポートが使用できない
Ports are not available: 

# ポート3306が既に使用されているため、バインド（割り当て）できない
exposing port TCP 0.0.0.0:3306 -> 0.0.0.0:0: listen tcp 0.0.0.0:3306: bind: address already in use
```


# 解決方法

3つの選択肢がある。

1. 既に使っているポートを停止する
1. 今回立ち上げようとしていたポート番号を変更する
    * 👉`ホストマシン(自分のPC等)とコンテナ内のポート番号両方`
1. 今回立ち上げようとしていたポート番号を変更する
    * 👉`ホストマシン（自分のPC等）のみ`

## 1. 既に使っているポートを停止する


* 既にポート3306を使用しているプロセスを特定し、停止する。
    * MySQLなどのデータベースが既にポート3306を使用している可能性がある。

## 2.　今回立ち上げようとしていたポート番号を変更する <br>👉`ホストマシン(自分のPC等)とコンテナ内のポート番号両方`

* `docker-compose.yml`などの設定ファイルで、コンテナが使用するポートを別のポートに変更する。

### ❓別のポート番号って何番使えばいいの🤔

以下のポイントを考慮するとよい。

1. 標準ポートは避ける
    * よく使われる標準ポート（例えば、`80`番（HTTP）、`443`番（HTTPS）、`22`番（SSH）など）は避けるのが無難
1. 未使用のポートを選ぶ
    * 一般的に`1024`番以降のポート番号はユーザーが自由に使用できる範囲
        * `1024`から`49151`までのポート番号は「登録済みポート」として`IANA（Internet Assigned Numbers Authority）`によって管理されているが、カスタムアプリケーションで使用してもいい
            * 例: `8080`, `8081`, `9090`, `10000`
            * `8080`や`9090`などのポート番号は、開発者や運用者にとって馴染みがあり、覚えやすいという利点がある
        * `49152`から`65535`までのポート番号は「動的/プライベートポート」として予約されており、ユーザーが自由に使用できる
            * 例: `49152`, `49153`, `50000`, `60000`

```yaml:（例）docker-compose.yml
services:
  db:
    image: mysql:8.0
    ports:
      - '3307:3307' # ３３０６から変更！

```

|ポート番号|こんな風に使われることが多い|
|---|---|
`8080`|HTTPの代替ポートとしてよく使用される。開発環境やテスト環境で頻繁に使用されるポート。
`8081`|`8080`の代替ポートとして使用されることが多い。
`9090`|管理インターフェースやモニタリングツールなどで使用されることが多い。
`10000`|特定のカスタムアプリケーションや内部ツールで使用されることが多い。


## 3.　今回立ち上げようとしていたポート番号を変更する <br>👉`ホストマシン（自分のPC等）のみ`

このことを難しい言葉で言うと`ポートフォワーディング`という。
（ネットワーク通信において特定のポート番号を別のポート番号に転送する技術）

```yaml:（例）docker-compose.yml
services:
  db1:
    image: mysql:8.0
    ports:
      - '3307:3306'  # ホスト側のポート3307をコンテナ側のポート3306にマッピング
```

### ❓コンテナ側のポート番号は重複していていいの🤔

よい。

* ホストマシン側のポートは一意である必要がある。
    * つまり、同じポート番号を複数のサービスやコンテナで同時に使用することはできない。
    * 使用すると冒頭のエラーが発生する。
* コンテナ側のポートは、各コンテナが独立したネットワーク名前空間を持っているため、他のコンテナと重複しても問題ない。
    * 例えば、複数のコンテナがそれぞれポート3306を使用していても、それぞれのコンテナ内で独立して動作する。
        * 以下の例のように、コンテナ側のポート両方が`3306`を使っていても動作する

```yaml:（例）docker-compose.yml
services:
  db1:
    image: mysql:8.0
    ports:
      - '3307:3306'  # ホスト側のポート3307をコンテナ側のポート3306にマッピング

  db2:
    image: mysql:8.0
    ports:
      - '3308:3306'  # ホスト側のポート3308をコンテナ側のポート3306にマッピング

```
### ❓そもそもDockerコンテナってオンライン？どこと繋がっているの？🤔

* Dockerコンテナは、ホストマシン上で実行される仮想化された環境
    * コンテナはホストマシンのリソースを共有しながら、独立したネットワーク名前空間を持っている

🙋つまり、Dockerコンテナ自体はホストマシン上にあるよ

#### コンテナのネットワーク

* ホストマシンとの接続
    * コンテナはホストマシンのネットワークインターフェースを通じて外部と通信する
    * `docker-compose.yml`ファイルで指定されたポートフォワーディングにより、ホストマシンの特定のポートがコンテナ内の特定のポートにマッピングされる
* ブリッジネットワーク
    * デフォルトでは、Dockerはブリッジネットワークを使用する
    * これは、ホストマシンとコンテナ、そしてコンテナ同士を接続する仮想ネットワーク
    * ブリッジネットワーク内では、コンテナはIPアドレスを持ち、他のコンテナと通信できる

🙋名前空間を上手につかって、ホストマシン上でコンテナ間のネットワーク分離を実現しているよ

#### コンテナ間の通信

* 同じネットワーク内のコンテナ
    * 同じDockerネットワークに接続されているコンテナは、互いにホスト名（サービス名）で通信できる
        * 例えば、`docker-compose.yml` ファイルで定義された db サービスに対して、`web` サービスから `db:3306` のようにアクセスできる
* 異なるネットワーク間のコンテナ
    * 異なるネットワークに接続されているコンテナ間で通信する場合、適切なネットワーク設定やポートフォワーディングが必要


#### 外部との通信

* インターネットアクセス
    * コンテナはホストマシンのネットワークインターフェースを通じてインターネットにアクセスできる
        * 例えば、コンテナ内のアプリケーションが外部APIにアクセスする場合、ホストマシンのネットワークを通じて通信が行われる


```yaml:（例）docker-compose.yml

# ホストマシンのポート8080にアクセスすると、その通信はコンテナ内のポート80に転送する

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"  # ホストマシンのポート8080をコンテナ内のポート80にマッピング
```

🙋ブラウザで`http://localhost:8080`にアクセスすると、コンテナ内のNginxサーバーに接続されるよ


##### 補足：外部とやりとりする時は、以下の情報を設定していくことが多い

1. ポートフォワーディングの設定
    * `ports`キーを使用して、ホストマシンのポートとコンテナ内のポートをマッピング
        * （例）webサービスでは、ホストマシンのポート`8080`がコンテナ内のポート`80`にマッピング
2. 環境変数の設定
    * `environment`キーを使用して、コンテナ内のサービスに必要な環境変数を設定
        * （例）appサービスでは、`NODE_ENV` や `DATABASE_URL`などの環境変数を設定
3. ボリュームの設定
    * `volumes`キーを使用して、データの永続化や設定ファイルの共有を行う
        * （例）dbサービスでは、データベースデータを永続化するために`db_data`ボリュームを使用
4. ネットワークの設定
    * `networks`キーを使用して、カスタムネットワークを設定し、サービス間の通信を管理
        * （例）webサービスと appサービスは`frontend`ネットワークに接続され、appサービスとdbサービスは`backend`ネットワークに接続


```docker-compose.yml
services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"  # ホストマシンのポート8080をコンテナ内のポート80にマッピング
    environment:
      - NGINX_HOST=localhost
      - NGINX_PORT=80
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf  # Nginxの設定ファイルを共有
    networks:
      - frontend

  app:
    build: ./app  # アプリケーションのDockerfileがあるディレクトリ
    ports:
      - "3000:3000"  # ホストマシンのポート3000をコンテナ内のポート3000にマッピング
    environment:
      - NODE_ENV=production
      - DATABASE_URL=mysql://user:password@db:3306/mydatabase
    volumes:
      - ./app:/usr/src/app  # アプリケーションコードを共有
    networks:
      - frontend
      - backend

  db:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_DATABASE=mydatabase
      - MYSQL_USER=user
      - MYSQL_PASSWORD=password
    ports:
      - "3306:3306"  # ホストマシンのポート3306をコンテナ内のポート3306にマッピング
    volumes:
      - db_data:/var/lib/mysql  # データベースデータの永続化
    networks:
      - backend

networks:
  frontend:
  backend:

volumes:
  db_data:
```