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が画像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日空けて再度内容確認してから投稿しようとしてたら、かぶることに。。。
- Troubleshoot Your Network Load Balancer - Elastic Load Balancing
- gRPC with Load Balancer or Proxy or on AWS · GitHub
- https://forums.aws.amazon.com/message.jspa?messageID=805080
- Elastic Network Interfaces - Amazon Elastic Compute Cloud
- タスクネットワーキングと awsvpc ネットワークモード - Amazon Elastic Container Service
- Amazon ECS Service Discovery がフランクフルト、ロンドン、東京、シドニー、シンガポールの各リージョンで利用可能に
- AWS Network Load Balancerの内部LBはクライアントとターゲットが同一IPアドレスのトラフィックを扱うことができない | Developers.IO