年中アイス

いろいろつらつら

ECSのコンテナ間でNLBを経由した通信がつながらないことがある

gRPCで作った複数のサービスを、同じECS Cluster上で動かし、他のサービスを呼び出している時に、通信できなくなったことがあったので、その原因のメモです。

直接的な原因は、「NLBは、接続元と接続先が同一のIPへの通信ができない」という仕様に意図せず該当したことでした。

このドキュメントにあるように、ヘアピン、ループバックはタイムアウトしますと記載があります。

Internal load balancers do not support hairpinning or loopback.

EC2で直接動作させている場合は、同一ホストに複数のAPIなどを乗せて他のインスタンスとつなぐことは少ないのかと思います。*1しかし、ECSがBridgeモードの場合に、同じインスタンスの別ポートで、複数のコンテナが動くことがあります。その時にこの問題が発生します。*2

例えば、10.0.0.10のECS node instance上で、以下のコンテナが動いていたとします。

  • APIがport 30000で動作
  • 画像用の内部APIがport 30001で動作

この時に、APIが画像APIに対してリクエストを行うと、10.0.0.10 -> NLB -> 10.0.0.10:30001とリクエストがルーティングされることになり、前述の原因に該当します。この状態がBridgeモードでのネットワークの下で行われるのでとても気づきにくいのです。

せめてタイムアウトじゃなくて何かエラーを出してもらえると気付きやすいのですが、TCPレイヤの話なので明確にこのエラーを伝える術がないのかなと思ったりもします。

回避策はドキュメントの回答にあるように、network modeをbridgeではなく、awsvpcモードにして、コンテナごとにIPアドレスが割り当てられるようにすることです。ただ、awsvpcモードはタスクごとにENIを消費し、node instanceのtype/sizeごとに上限が決まっているので、コンテナの集積度が一気に下がります。

instance typeごとのENI上限awsvpcモードでの考慮点を見るとc4.largeでも3個です。1個はそもそもインスタンス自体が使うので、-1した数が、そのインスタンス上で動くコンテナの上限になります。そうなると、c4.largeだと2コンテナしか動かせず、1コア2GB弱/コンテナの割り当てになり、Goの薄いAPIみたいなのだとそんなにリソースいらないからもっと乗せたい、となるのです。

先日、ECS Service DiscoveryがTokyoにも来たので、それを使ってNLBを介さずに、sidecarとしてclient side balancingをやるのがいいのかなと考えています。

終わりに

こんなことを書いたんですが、一足先にクラスメソッドさんも同じことを書いていて ECSがたくさん使われ始めて顕在化するようになったのかなと思います。1日空けて再度内容確認してから投稿しようとしてたら、かぶることに。。。

*1:動かすことはあっても、同一EC2上instance上で動作させるなら、NLB介さずにlocal port経由する方がシンプルです

*2:ECS Clusterに属するnodeが少ない場合に、発生しやすくなり、数十以上あると、まれに発生する奇妙な状態になって気づきにくそうです。