woshidan's blog

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

いままでのECSの起動モード(EC2起動モード)のネットワーク設定(bridge ネットワークモード)とFargate起動モードを利用した場合のネットワーク設定関連で嬉しいことについて

2017年11月14日にAWSAmazon ECSのTask Networkingを発表*1し、その恩恵を受ける Fargate 起動タイプが2018年7月4日に東京リージョンでサービス開始となりました*2

自分の職場でも一部サービスでFargate(with awsvpcモード)の採用が始まったのをレビューで見たのはいいものの、いままでのECSのネットワークモードとの違いがよくわからず、最初のPRについては「俺を信じるお前を信じろ」になってしまったため、ドキュメント見て少し調べたメモです。

目次

  • いままでのECSの起動モード( EC2 )のネットワーク設定(bridge ネットワークモード)について
    • ECSが利用しているEC2のネットワーク設定
    • Dockerのデフォルトの bridge ネットワークモードをECSで利用する場合にタスクのネットワークに関して起きること
    • ECSでEC2起動モードで利用するDockerのデフォルトの bridge ネットワークモードを利用することで起きる制限
  • Fargate起動モードで利用する awsvpc ネットワークモードについて
    • awsvpc ネットワークモードでは、 ecs-agent が利用するCNIプラグインを利用してタスクにENIをアタッチする
    • awsvpc ネットワークモードの利用で嬉しいこと
    • ECSで awsvpc ネットワークモードを利用する場合の制限の話
  • 参考

いままでのECSの起動モード( EC2 )のネットワーク設定(bridge ネットワークモード)について

実は、 Fargate 起動モードや awsvpc ネットワークモード以前に、既存の EC2 起動モードや EC2 起動モードの際に前提とされてる気がするDockerのデフォルトの bridge ネットワークモードについてよく知らなかったのでそこから確認しました。

ECSが利用しているEC2のネットワーク設定

ECSのコンテナインスタンスはかなり大雑把にいうと ecs-agent が動作し、ECSに登録されたEC2インスタンス*3のことを指しますが、ECSのネットワークに関係する設定はEC2のネットワーク設定の方法に依存しています。

なので、最初にEC2のネットワーク周りのことをおさらいしました。

EC2を起動するには、AWS ドキュメント » Amazon Elastic File System (EFS) » ユーザーガイド » Amazon Elastic File System の開始方法 » ステップ 1: EC2 リソースを作成し EC2 インスタンスを起動するに記載されている通りVPCの利用が前提となっています。

そのVPC内のEC2インスタンスは自分たちでsshした後にコマンドを叩かなくても、VPCのルーティングテーブルに設定が登録されており、VPC内の他のインスタンス + 外部エンドポイントにアクセス可能なわけですが、これを実現しているのがENI(Elastic Network Interface)と呼ばれるVPC内の仮想的なネットワーク・インタフェースとなっています。

ENIはすべてのEC2インスタンスに1つ以上アサインされ、EC2インスタンスの起動時に自動的にアサインされる1つのENIを プライマリネットワークインタフェース と呼びます。

EC2インスタンスのネットワーク設定にあたる各種属性( IPアドレスやセキュリティグループなど )は、実際はEC2インスタンスアサインされているENIに設定される属性です。

Dockerのデフォルトの bridge ネットワークモードをECSで利用する場合にタスクのネットワークに関して起きること

ECSではいままでDocker側で用意されている3種類のネットワークモード*4が利用できました。

https://aws.amazon.com/jp/blogs/news/introducing-cloud-native-networking-for-ecs-containers/ では、そのうち、bridgeモードについて説明されていたので、この記事でもそれに沿って考えることにします。

ECSでは、同じタスク定義のコンテナ一式は同じコンテナインスタンスの中で動作し、さらに1つのコンテナインスタンスの中で複数のタスクが動くことがあります*5

このとき、ECSのコンテナとして動くコンテナの間で利用するネットワークインタフェースは共通( docker0 のブリッジが接続されているインタフェース)なので、コンテナたちはすべて同じネットワークインタフェースを経由して通信を行い*6、ENIとそれに伴うネットワーク設定も共有します。

また、コンテナ同士で外部からアクセスされる際に経由するネットワークインタフェースを共有するので、外部から来た通信をそれぞれのコンテナに接続する際に同じ定義のタスクでも違うポート番号を割り振らなければいけないことに関するややこしさがありました。

ECSでEC2起動モードで利用するDockerのデフォルトの bridge ネットワークモードを利用することで起きる制限

前節で述べたことをもとに考えると、これまでのECSのネットワーク設定では

同じコンテナインスタンス上のコンテナはすべて同じENI(の裏のネットワークインタフェース)を経由する前提なので

  • 比較的ネットワークリソースの干渉が起きやすく、複数のコンテナが1つのホストに配置された場合、ネットワークが詰まってパフォーマンスが出ない場合もありそう
  • また、同じ定義のタスクが同じコンテナインスタンスで動く際のネットワーク設定がややこしかった
    • awsvpc ネットワークモードではタスク定義にポート番号とプロトコルを書くだけで完了*7ですが、 bridge 起動モードなどではポートマッピングなどの設定が必要だった*8

同じホスト上のコンテナはすべて同じENI(に紐づいたネットワーク設定)を利用する前提なので

  • セキュリティに気を使うコンテナとそうでないコンテナの設定が一緒くたになってしまう

といった問題が発生していたみたいです。

Fargate起動モードで利用する awsvpc ネットワークモードについて

EC2起動モードの際に利用するネットワークモードについて確認したところで新しい awsvpc ネットワークモードについて確認します。

awsvpc ネットワークモードでは、 ecs-agent が利用するCNIプラグインを利用してタスクにENIをアタッチする

ネットワークの名前空間には名前空間ごとにルーティングテーブルが用意できるだけでなく、ネットワークデバイスを追加することも可能*9ですが、 awsvpc ネットワークモードでは、タスクごとにネットワークの名前空間を作成し、ECSのコンテナインスタンス上にある、ホストマシンのプライマリネットワークインタフェース以外のENIをそのタスク用の名前空間に追加します*10

つまり、 awsvpc ネットワークモードではタスクごとに専用のネットワークインタフェース(ENI)が用意されます。

一方、bridge ネットワークモードの時は、ECSのコンテナインスタンス上にあるホストマシンのプライマリネットワークインタフェースにあたるネットワークデバイスしか利用してなかった*11、つまり、同じホストで動くコンテナ全体で1つのENI*12しか用意されていませんでした。

awsvpc ネットワークモードの利用で嬉しいこと

タスクごとに専用のENIを用意すると何が嬉しいのでしょうか。

ENIにネットワーク設定がアサインされているので、タスクごとにネットワーク設定ができるようになり、1つのコンテナインスタンスで複数同じタスクが動く場合の設定のややこしさも解消されました。

さらに、同じホストで動くタスクの間でネットワークリソースの干渉が減少し、ネットワークで詰まっていたアプリケーションがあったとしたら、ネットワークモードの変更だけでパフォーマンスが改善する可能性があります。

また、ENI(いままでのEC2のネットワーク関係の設定や追跡機能が紐づいているもの)とタスクが1対1で対応しているので、タスクごとの通信内容がVPCフローログなどで既存のEC2のネットワーキング機能を利用して確認できるようになります。

ECSで awsvpc ネットワークモードを利用する場合の制限の話

今までのネットワークモードより便利そうな点が多い awsvpc ネットワークモードですが、タスクごとにENIを割り当てることに関連していると思しき制限もあります。 この記事では、その中の印象的だったもの*13をメモして〆たいと思います。

  • いままで利用していた link 設定が利用できない
    • 代わりに、同じホストで複数台同じ番号で動いてもいいことになったポート番号を使う*14*15
  • タスクごとにネットワークインタフェースをアタッチしているので、クラスタで利用しているEC2インスタンスインスタンスタイプで設定されている ENIの数 - 1 以上にタスクをコンテナインスタンスに配置できない
  • タスクごとにネットワークの名前空間を作成し、ECSのコンテナインスタンス上にある、ホストマシンのプライマリネットワークインタフェース以外のENIをそのタスク用の名前空間に追加する の部分を ecs-agentawsvpc モード用に用意されたCNIプラグイン達を通して行う*16からか、手動で awsvpc ネットワークモードのタスクが動くコンテナインスタンスに対して ENIのアタッチをしていて、他に余っているENIがなければ、 awsvpc ネットワークモードのタスクは PROVISIONING => DEPROVISIONING => STOPPED に移行して動かない

制限やEC2起動モードとの違いの詳細には、 https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task-networking.html を確認してください。

参考

この記事を書き始める前に重点的に読んだもの。他の参考記事は注釈部に。

VPCとALBについてもう少し詳しくなりたいけど、業務的に火急なのはElasticCacheで自分は何をやっているんだ...、現場からは以上です。

*1:https://aws.amazon.com/jp/blogs/news/introducing-cloud-native-networking-for-ecs-containers/

*2:https://aws.amazon.com/jp/blogs/news/aws-fargate-tokyo/

*3:https://aws.amazon.com/jp/blogs/news/under-the-hood-task-networking-for-amazon-ecs/ の図など

*4:https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/create-task-definition.htmlhttps://docs.docker.com/engine/reference/run/#network-settings より

*5:http://www.slideshare.net/AmazonWebServicesJapan/aws-blackbelt-2015-ecs の20ページとかみたいに

*6:https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definition_parameters.html より今まで使えた設定でもhost ネットワークモードを使えば話は違うみたいですが、たぶん設定がややこしかったのでは

*7:https://aws.amazon.com/jp/blogs/news/introducing-cloud-native-networking-for-ecs-containers/

*8:http://www.slideshare.net/AmazonWebServicesJapan/aws-blackbelt-2015-ecs 69~70ページあたりで2015年時点のその辺の様子を確認できます。ポート番号が被らないように発展編の設定を使ったり、同一Taskは一つのコンテナインスタンスに一つまで、と制限したり

*9:https://docs.openstack.org/liberty/ja/networking-guide/intro-network-namespaces.html

*10:https://aws.amazon.com/jp/blogs/news/under-the-hood-task-networking-for-amazon-ecs/

*11:https://aws.amazon.com/jp/blogs/news/introducing-cloud-native-networking-for-ecs-containers/ の 「コンテナはこのブリッジを利用し、それが実行されているインスタンスのプライマリネットワークインタフェースを使う」 から

*12:ENIは仮想的なインタフェースで論理的に分割されている可能性もあるんでしょうが、インスタンスタイプによってENIの数変わるし、物理的にもある程度そのような気はする...

*13:主にレビューの時に「へぇ」といったり、個人的にトラブルシューティングで使いそうな目処がついたもの

*14:https://qiita.com/taishin/items/84122ad85bedf8b8a682

*15:上の記事とは関係ないんですが、もともとlinkはポート番号が使えないから利用したい機能でポート番号がアプリケーションに対応した番号で固定で使えるならいらないでしょ + アプリケーションが設定しているポートを見て設定するようになるとELBやセキュリティグループの設定で混乱がなくなる、みたいな考えでもあるのか?

*16:https://aws.amazon.com/jp/blogs/news/under-the-hood-task-networking-for-amazon-ecs/