【AWS ECS入門】ダイナミックポートマッピングとbridgeモードを図解でやさしく解説!

AWS

AWS ECS(Elastic Container Service)を学び始めると、必ず出てくるのが「ダイナミックポートマッピング」と「bridgeモード」という用語です。「同じサーバーで同じアプリのコンテナを複数動かしたい」というニーズを実現するための、ECSの中核的な仕組みです。本記事では、これら2つの概念をゼロから図解付きでやさしく解説します。読み終える頃には、ECS設計の判断軸がしっかり身につくはずです。


背景:なぜポートで悩むのか

サーバー上でWebアプリを動かすとき、「ポート番号」は通信の入り口を決める重要な要素です。たとえばWebサーバーは通常80番ポート、HTTPSは443番ポートを使います。

ところがコンテナを使うと、こんな問題に直面します。1台のEC2インスタンス上でコンテナAが80番ポートを使っている場合、コンテナBも同じ80番ポートを使おうとすると「Address already in use」というエラーが発生し、起動できません。CPUやメモリにまだ余裕があってもコンテナを増やせず、リソースの無駄遣いになってしまいます。

この「ポート競合」を解決するのが「bridgeモード + ダイナミックポートマッピング」の組み合わせです。


bridgeモードとは何か

bridgeモードとは、ホスト(EC2インスタンス)の中に仮想的なネットワーク(=橋)を作り、コンテナをそこに接続する方式です。コンテナはホストとは別のIPアドレスを持ち、外部と通信する際はホストを経由します。イメージとしては「マンションの中にもう一つ小さなロビーがあり、コンテナはその中の住人」という感じで、各部屋(コンテナ)には独立したアドレスが割り当てられます。

bridgeモードの構造は以下のとおりです。外部ネットワークからの通信はEC2インスタンスの物理NIC(eth0)を経由し、Docker が自動的に作成する仮想ブリッジ「docker0」(172.17.0.1)を介して各コンテナ(172.17.0.2、172.17.0.3…)に届きます。

用語補足
docker0:Dockerが自動的に作成する仮想ブリッジ(仮想スイッチ)。コンテナ同士の通信や、コンテナと外部の通信を中継します。
NAT(Network Address Translation):内部のプライベートIPと外部のIPを相互に変換する仕組み。コンテナが外部と通信するときは、送信元IPがホストのIPに書き換わります。

bridgeモードでは、コンテナは独自のIP(例:172.17.0.2)を持ちますが、これはホスト内部だけで通用するアドレスです。外部からコンテナへ直接アクセスはできないため、「ホストの○番ポートに来た通信を、コンテナの○番ポートに転送する」というルール=ポートマッピングを設定します。

# Docker風の例
docker run -p 80:80 nginx
#         ↑ ホスト側80番 : コンテナ側80番
"portMappings": [{
  "hostPort": 80,
  "containerPort": 80
}]

このようにホスト側ポートを固定してしまうと、最初に説明したポート競合問題に逆戻りです。そこで登場するのがダイナミックポートマッピングです。


ダイナミックポートマッピングの仕組み

ダイナミックポートマッピング(動的ポートマッピング)の核心は、ECSのタスク定義で「ホストポートを 0 に指定する」ことです。

{
  "containerDefinitions": [{
    "name": "my-app",
    "image": "nginx",
    "portMappings": [{
      "containerPort": 80,
      "hostPort": 0
    }]
  }]
}

hostPort: 0 は「OSに任せて空いているポートを自動で割り当てて」という意味です。Dockerが起動時にOSへ問い合わせて、エフェメラルポート範囲(Linuxではデフォルト32768〜60999など)から空きポートを1つ取ってきます。

これにより、1台のEC2上に同じアプリのコンテナが複数同居できます。たとえば同じNGINXコンテナがコンテナA(ホスト32768→80)、コンテナB(ホスト41523→80)、コンテナC(ホスト55891→80)のように、ホスト側ポートが毎回異なるため競合しません。

ここで疑問が湧くはずです。「ホスト側ポートが毎回ランダムに変わるなら、ロードバランサーはどうやって振り分け先のポートを知るの?」

これを解決するのがECSとALB(Application Load Balancer)の自動連携です。タスクが起動するたびに、ECSが自動的にALBのターゲットグループに「インスタンスID + 動的ポート」のペアを登録します。タスクが終了すれば自動的に登録解除もしてくれます。

つまり、ダイナミックポートマッピングは「ホスト側ポートを動的に決める」+「ECSがALBに自動連携する」のセットで初めて完成する仕組みです。


ECSのネットワークモード比較

ECS(EC2起動タイプ)にはbridge以外にもネットワークモードがあります。違いを把握しておくと、設計時に迷わなくなります。

  • bridgeモード(今回の主役):ホスト内に仮想ブリッジ(docker0)を作り、コンテナをそこに接続。コンテナはホストとは別IPを持ちます。外部アクセスにはポートマッピングが必須で、動的ポートマッピングと組み合わせれば同一インスタンスへの複数同居が可能。EC2起動タイプのデフォルトです。
  • hostモード:コンテナがホストのネットワーク空間をそのまま使うモード。ポートマッピング不要でパフォーマンスは最高ですが、同じポートを使うコンテナを複数同居できません。
  • awsvpcモード(推奨):各タスクに専用のENI(Elastic Network Interface)とVPC内のIPアドレスが割り当てられるモード。セキュリティグループをタスク単位で設定可能。Fargateでは必須ですが、ENI数の上限(インスタンスタイプ依存)に注意が必要です。
  • noneモード:ネットワークを完全に切るモード。特殊用途以外ではほぼ使いません。
観点bridgehostawsvpc
コンテナ専用IPあり(172.17.x.x)なし(ホストと同じ)あり(VPC内IP)
ポートマッピング必要不要不要
同じポートのコンテナ複数同居✅(動的ポート)✅(IP違う)
パフォーマンスやや低最高
セキュリティグループ単位インスタンスインスタンスタスク単位
Fargate対応✅(必須)
動的ポートマッピング不要

動的ポートマッピングは「bridgeモード前提の機能」です。awsvpcモードでは各タスクにIPが割り振られるため、そもそもポート競合が発生しません。そのためダイナミックポートマッピングという仕組み自体が不要になります。


実際の設定とハマりどころ

必要な設定の全体像は以下のとおりです。

  1. ALBとターゲットグループを作成(ターゲットタイプは instance
  2. タスク定義で hostPort: 0containerPort: 80 を設定
  3. ECSサービスを作成し、ALBと紐付け
  4. EC2のセキュリティグループでエフェメラルポート範囲を許可
  5. ヘルスチェックのポートを traffic-port に設定

動的ポートマッピングを初めて使う人がほぼ全員ハマるのが、セキュリティグループの設定漏れです。ALBはランダムに割り当てられた動的ポートに向かって通信するため、80番だけ開けても通信できません。

# EC2のセキュリティグループ設定(誤り)
インバウンド: ALBのSGから TCP 80 を許可    ← ❌ これでは通信できない

# 正しい設定
インバウンド: ALBのSGから TCP 32768-65535 を許可  ← ✅ エフェメラルポート範囲を許可

その他の注意点も押さえておきましょう。

  • サービス作成後に、ターゲットグループのARN・コンテナ名・コンテナポートは変更できません(再作成が必要)
  • bridgeモードはセキュリティグループがインスタンス単位なので、コンテナ単位の細かいアクセス制御はできません
  • サービス間通信を厳密に制御したい場合は、awsvpcモードを検討しましょう


動作確認の方法

設定が正しく機能しているかを確認する方法を3つ紹介します。

方法1:ターゲットグループの登録状況を確認する

EC2コンソール → ターゲットグループ → ターゲットタブを開きます。同じインスタンスIDが、別々のポートで複数登録されているのが動的ポートマッピングが効いている証拠です。

インスタンスIDポートステータス
i-0abc123def45632768healthy
i-0abc123def45641523healthy
i-0xyz789uvw01238192healthy

方法2:EC2にSSHして docker ps で確認する

ssh ec2-user@<EC2のIP>
docker ps

# 出力例
CONTAINER ID  IMAGE  COMMAND     PORTS
abc123def     nginx  "nginx..."  0.0.0.0:32768->80/tcp
xyz789uvw     nginx  "nginx..."  0.0.0.0:41523->80/tcp

32768→80、41523→80 のように、ホスト側ポートだけ違って、コンテナ側はすべて80番という形で確認できます。

方法3:tcpdumpで通信を観察する

# 動的ポート範囲のトラフィックをキャプチャ
sudo tcpdump -i any -nn 'tcp and (portrange 32768-61000)'

# docker0インターフェイスのトラフィックをキャプチャ(コンテナ側のIPが見える)
sudo tcpdump -i docker0 -nn

ALBから動的ポートに向けたトラフィックがリアルタイムで確認できます。


メリット・デメリットまとめ

bridgeモード + 動的ポートマッピングのメリットは以下のとおりです。

  • インスタンスのリソースを最大限活用できる(同じEC2に同じアプリの複数コピーを配置可能)
  • デプロイ時に新旧バージョンが共存でき、ローリングアップデートが滑らか
  • スケーリングが柔軟(タスク数を増やしてもインスタンスを増やさず済む場合がある)
  • ENI数の制限を受けない(awsvpcと違ってインスタンスタイプの制約が緩い)
  • ローカル開発のDocker環境と挙動が近く、デバッグしやすい

一方、デメリット・注意点は以下のとおりです。

  • セキュリティグループでエフェメラルポート範囲を開ける必要がある(直感的でない)
  • NATが入るのでパフォーマンスがawsvpcやhostよりやや劣る
  • セキュリティグループはインスタンス単位(コンテナ単位の制御は不可)
  • コンテナ単位の細かいアクセス制御や監査が難しい
  • Fargateでは使えない(混同しやすいので要注意)


まとめ

本記事のキーポイントを振り返ります。

  • bridgeモードとは、ホスト内に仮想ブリッジ(docker0)を作り、コンテナをそこに接続する方式。コンテナはホストとは別のIPを持ちます
  • ダイナミックポートマッピングとは、bridgeモードでhostPortを0にすることでOSがエフェメラルポート範囲から空きポートを動的に割り当てる仕組みです
  • 真の価値は、ECSがALBへ動的ポート情報を自動登録してくれる連携にあります
  • これにより、同一インスタンス上に同じアプリのコンテナを複数同居させられます
  • EC2起動タイプ + bridgeモード限定の機能で、FargateやawsvpcモードはNG(不要)です
  • ハマりやすいポイントはEC2のセキュリティグループでエフェメラルポート範囲を開け忘れること

ECSを使い始めると必ず出会う概念なので、本記事の内容を押さえておけば設計時の判断軸が一段固まるはずです。実際にハンズオンで触ってみると、抽象的だった概念が一気に立体的に理解できるようになります。最小構成(ALB + ECSクラスター + 1サービス + 2タスク)から始めてみるのがおすすめです。


参考リソース

コメント

タイトルとURLをコピーしました