woshidan's blog

そんなことよりコードにダイブ。

ALBでできるようになったコンテントベースルーティングとALBと連携したECSの動的ポートマッピングについて

ELBとCLBとALBの違い

AWSのロードバランサで検索すると、ロードバランサを指す言葉に、ELB, CLB, ALBの3種類がでてきます。

この3種類の違いは、

  • ELB ... Elastic Load Balancing. サービス名。ELBの提供しているロードバランサのリソースにCLBとALBがある
  • CLB ... Classic Load Balancing. ELBで利用出来るロードバランサの種類。ALBの提供開始まではELBと呼ばれていて、サービス名とサービスで利用出来るリソースの名前が一致していた
  • ALB ... Application Load Balancing. ELBであとから利用出来るようになったロードバランサ。 コンテントベースルーティング やECSの動的ポートマッピングの利用が可能となった

となっています。以下は コンテントベースルーティング とECSの動的ポートマッピングについて、なぜALBではできるようになったのか、あるいは、どういうことが嬉しいのか、について調べて考えたことについてメモです。

ALBのコンテントベースルーティングについて

CLBを利用する場合、リクエスト元のプロトコルとポートを見て許可されたリクエストをロードバランサに追加されたEC2インスタンスに割り振ること以外できず、リクエストの内容を見て調整することはありませんでした*1

なので、1つのロードバランサに追加するEC2インスタンスは、基本的にすべて同一のインスタンスタイプ(というより同一の働きをする同一スペックのホスト)である必要がありました。

たとえば、Appサーバと画像配信用サーバを利用するサービス(ドメイン: sample.com )があり、

  • 基本的にはAppサーバへリクエストを転送する
  • /img/ のパスだけ画像配信用サーバへリクエストを転送する

のように同じドメインに対してリクエストを処理すべきサーバが複数種類ある場合、

  • まず sample.comドメインへ来たリクエストを1段目のCLBからすべてnginxに転送する
  • nginxがリクエストのパスを見て、パスが /img/ 以下なら2段目のCLB(画像配信サーバ用)、それ以外なら2段目のCLB(Appサーバ用)へリクエストを転送
  • 2段目のCLBが、複数のAppサーバ、画像配信サーバへリクエストを割り振る
  • リクエストが増えてきたら1段目のCLB配下のnginxを増やす

f:id:woshidan:20180803144804p:plain

といった構成になり、CLB以外にリクエストの転送先を切り替えるためにリーバスプロキシを挟む必要がありました*2

一方、ALBではロードバランサ自身がリクエストのパスを読んでリクエスト先を判断することができるので*3、リクエストの分散に対するネットワーク構成をシンプルにすることができます。

f:id:woshidan:20180803144816p:plain

ALBと連携することによるECSの動的ポートマッピングについて

ALBの登場により、ALBとサービスが連携することで一つのECSインスタンス上で同じタスクが複数実行できるようになり、ECSのサービスでタスクを複数実行するのが少し簡単になりました。

これについても少しメモしておきます。

CLBの頃には、

といった形でした。

つまり、ロードバランサから受信した際のプロトコル/ポートとバックエンドのEC2インスタンスへ転送する際のプロトコル/ポートが1対1で、転送先のポートは決めてから割り振るインスタンスを選ぶ感じだったんですね。

さて、タスクというのはだいたいdocker-composeで立ち上げるコンテナ一式のことなので、一つのECSインスタンスで同じタスク定義のタスクを複数立ち上げるということは、一つのDockerホスト上でポートマッピングに関する設定まで同じ定義のコンテナを複数立ち上げる、ということになりますが。。

いくらECSのDocker側でコンテナに対応するポートがたくさん公開されたとしても、ロードバランサーに来たプロトコル/ポートに対応してEC2インスタンスが受信するときのプロトコル/ポートは一つなので、そのうち一つのタスク(コンテナ. コンテナに対応するポート)にしかリクエストは転送されません*6

このため、CLBを利用して負荷分散をすることを念頭に置いた場合、ECSでは実質1インスタンス1タスクみたいな制限となっていたのでした。

ALBになって

という形になりました。

となりました。そうすると、CLBの頃にできなかった 同じインスタンス内の別のタスク(がリクエストを待ち受けているポート)へのリクエスト振り分け が可能となります。

余談

ここからは、あらためて、 https://aws.amazon.com/jp/premiumsupport/knowledge-center/dynamic-port-mapping-ecs/ のリンクを眺めた末の妄想に近いのですが、

たぶん、

  1. ロードバランサをセットしたサービスを作成する
  2. サービスがタスク起動時にタスクのコンテナが公開したポートを控えておく
  3. 2. で控えたポートとプロトコルを使ってタスク専用のターゲットグループを作成する
  4. 3. のターゲットグループに、タスクが動いてるEC2のインスタンスのIPにより自身が動作しているインスタンスをターゲットとして追加

というようなことをしているのだと思います。

ターゲットグループはロードバランサに対して作成するので、サービスは3, 4のステップのためにロードバランサの情報を知っておく必要があり、そのためにECSの動的ポートマッピングを利用する場合はサービス作成時にロードバランサを登録しているのでしょう。

現場からは以上です。

*1:他のサービス同様、ロードバランサのセキュリティグループやAClで、分散先のホストへ飛ぶ通信においてはこのポートはふさいでおこう、といった個別のリクエストの内容とは関係ないネットワークの経路に関する設定はできます

*2:https://aws.amazon.com/jp/elasticloadbalancing/pricing/

*3:あとで触れますが、ALBのリスナーのリスナールールによってパスごとに転送するターゲットグループを指定することができます https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-listeners.html#listener-rules 。 CLBのリスナーには同等の設定はありません https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/classic/using-elb-listenerconfig-quickref.html

*4:具体的にはinstanceIDの指定により追加 https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/classic/elb-listener-config.html

*5:https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/classic/elb-listener-config.html CLBでリスナーを設定するフォームを確認してみてもそんな感じ https://cdn-ak.f.st-hatena.com/images/fotolife/w/woshidan/20180805/20180805202909.png

*6:リスナーとかいろんな設定を頑張ればいけるかもしれないが、たいがいなハックだと思います

*7:実際の設定画面が https://cdn-ak.f.st-hatena.com/images/fotolife/w/woshidan/20180805/20180805211910.png