概要
Dockerコンテナを立ち上げようとしたときに、bind: address already in use
エラーが出た時の対処法。
エラー内容
-
docker-compose up --build
を実行したところ発生。
エラーメッセージ
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つの選択肢がある。
- 既に使っているポートを停止する
- 今回立ち上げようとしていたポート番号を変更する
- 👉
ホストマシン(自分のPC等)とコンテナ内のポート番号両方
- 👉
- 今回立ち上げようとしていたポート番号を変更する
- 👉
ホストマシン(自分のPC等)のみ
- 👉
1. 既に使っているポートを停止する
- 既にポート3306を使用しているプロセスを特定し、停止する。
- MySQLなどのデータベースが既にポート3306を使用している可能性がある。
2. 今回立ち上げようとしていたポート番号を変更する
👉ホストマシン(自分のPC等)とコンテナ内のポート番号両方
-
docker-compose.yml
などの設定ファイルで、コンテナが使用するポートを別のポートに変更する。
❓別のポート番号って何番使えばいいの🤔
以下のポイントを考慮するとよい。
- 標準ポートは避ける
- よく使われる標準ポート(例えば、
80
番(HTTP)、443
番(HTTPS)、22
番(SSH)など)は避けるのが無難
- よく使われる標準ポート(例えば、
- 未使用のポートを選ぶ
- 一般的に
1024
番以降のポート番号はユーザーが自由に使用できる範囲-
1024
から49151
までのポート番号は「登録済みポート」としてIANA(Internet Assigned Numbers Authority)
によって管理されているが、カスタムアプリケーションで使用してもいい- 例:
8080
,8081
,9090
,10000
-
8080
や9090
などのポート番号は、開発者や運用者にとって馴染みがあり、覚えやすいという利点がある
- 例:
-
49152
から65535
までのポート番号は「動的/プライベートポート」として予約されており、ユーザーが自由に使用できる- 例:
49152
,49153
,50000
,60000
- 例:
-
- 一般的に
(例)docker-compose.yml
services:
db:
image: mysql:8.0
ports:
- '3307:3307' # 3306から変更!
ポート番号 | こんな風に使われることが多い |
---|---|
8080 |
HTTPの代替ポートとしてよく使用される。開発環境やテスト環境で頻繁に使用されるポート。 |
8081 |
8080 の代替ポートとして使用されることが多い。 |
9090 |
管理インターフェースやモニタリングツールなどで使用されることが多い。 |
10000 |
特定のカスタムアプリケーションや内部ツールで使用されることが多い。 |
3. 今回立ち上げようとしていたポート番号を変更する
👉ホストマシン(自分のPC等)のみ
このことを難しい言葉で言うとポートフォワーディング
という。
(ネットワーク通信において特定のポート番号を別のポート番号に転送する技術)
(例)docker-compose.yml
services:
db1:
image: mysql:8.0
ports:
- '3307:3306' # ホスト側のポート3307をコンテナ側のポート3306にマッピング
❓コンテナ側のポート番号は重複していていいの🤔
よい。
- ホストマシン側のポートは一意である必要がある。
- つまり、同じポート番号を複数のサービスやコンテナで同時に使用することはできない。
- 使用すると冒頭のエラーが発生する。
- コンテナ側のポートは、各コンテナが独立したネットワーク名前空間を持っているため、他のコンテナと重複しても問題ない。
- 例えば、複数のコンテナがそれぞれポート3306を使用していても、それぞれのコンテナ内で独立して動作する。
- 以下の例のように、コンテナ側のポート両方が
3306
を使っていても動作する
- 以下の例のように、コンテナ側のポート両方が
- 例えば、複数のコンテナがそれぞれポート3306を使用していても、それぞれのコンテナ内で独立して動作する。
(例)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
のようにアクセスできる
- 例えば、
- 同じDockerネットワークに接続されているコンテナは、互いにホスト名(サービス名)で通信できる
- 異なるネットワーク間のコンテナ
- 異なるネットワークに接続されているコンテナ間で通信する場合、適切なネットワーク設定やポートフォワーディングが必要
外部との通信
- インターネットアクセス
- コンテナはホストマシンのネットワークインターフェースを通じてインターネットにアクセスできる
- 例えば、コンテナ内のアプリケーションが外部APIにアクセスする場合、ホストマシンのネットワークを通じて通信が行われる
- コンテナはホストマシンのネットワークインターフェースを通じてインターネットにアクセスできる
(例)docker-compose.yml
# ホストマシンのポート8080にアクセスすると、その通信はコンテナ内のポート80に転送する
services:
web:
image: nginx:latest
ports:
- "8080:80" # ホストマシンのポート8080をコンテナ内のポート80にマッピング
🙋ブラウザでhttp://localhost:8080
にアクセスすると、コンテナ内のNginxサーバーに接続されるよ
補足:外部とやりとりする時は、以下の情報を設定していくことが多い
- ポートフォワーディングの設定
-
ports
キーを使用して、ホストマシンのポートとコンテナ内のポートをマッピング- (例)webサービスでは、ホストマシンのポート
8080
がコンテナ内のポート80
にマッピング
- (例)webサービスでは、ホストマシンのポート
-
- 環境変数の設定
-
environment
キーを使用して、コンテナ内のサービスに必要な環境変数を設定- (例)appサービスでは、
NODE_ENV
やDATABASE_URL
などの環境変数を設定
- (例)appサービスでは、
-
- ボリュームの設定
-
volumes
キーを使用して、データの永続化や設定ファイルの共有を行う- (例)dbサービスでは、データベースデータを永続化するために
db_data
ボリュームを使用
- (例)dbサービスでは、データベースデータを永続化するために
-
- ネットワークの設定
-
networks
キーを使用して、カスタムネットワークを設定し、サービス間の通信を管理- (例)webサービスと appサービスは
frontend
ネットワークに接続され、appサービスとdbサービスはbackend
ネットワークに接続
- (例)webサービスと appサービスは
-
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: