🍔

IPv6でECS on Fargateを動かす

2024/02/07に公開

TLDR

  • パブリックIPv6アドレスをタスクに割り当てたECSサービスをFargateで動かします。
    • パブリックIPv4アドレスは割り当てない
  • パブリックIPv4アドレスなしでもECSを利用できなくはありません。
  • ECRはIPv6に対応していないのでDocker Hubからイメージをpullする必要があります。
  • CloudWatch LogsもIPv6に対応していないのでロギングを利用するためにはFluent Bitを利用する必要があります。

はじめに

AWSがすべてのパブリックIPv4アドレスに課金し、IPv6への移行を奨励するとのことなので、パブリックIPv4アドレスを使わずにECSサービスを動かすことができるか試してみました。

https://aws.amazon.com/jp/blogs/news/new-aws-public-ipv4-address-charge-public-ip-insights/

この記事では amazon-ecs-sample を動かすサービスを構築します。デュアルスタックVPC上でECSサービスを動作させ、パブリックIPv6アドレスをタスクに割り当て、インターネットからHTTPで接続できることを目標とします。また、アプリケーションのログを収集できることも目標とします。

インフラ構成はAWS CDKで定義します。ソースコードは下記のGitHubリポジトリで参照することができます。

https://github.com/ShotaroTsuji/cdk-ipv6-subnets

インフラ構成

下記の図のような構成になります。VPCの中にデュアルスタックサブネットを1つ作成します。このサブネット上でECSサービスを動作させます。ここではECS on Fargateを利用します。サービスのタスク定義には amazon-ecs-sampleaws-for-fluent-bit の2つのコンテナがあります。Fluent Bitはログを収集しS3に出力します。

VPCとサブネット

大まかな流れは下記のAWSのドキュメントを参考にしています。

https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-migrate-ipv6.html#vpc-migrate-assign-ipv6-address

まず、VPCにIPv6 CIDRブロックをを割り当てる必要があります。IPv4ではプライベートアドレスを記述することができましたが、IPv6ではグローバルなアドレス空間を割り当てる必要があります。今回は Amazonが提供するIPv6 CIDRブロック を割り当てます。

https://github.com/ShotaroTsuji/cdk-ipv6-subnets/blob/df86c461971fa4ee89b06a65ff38d98cf2cc51f6/cdk_ipv6_subnets/ipv6_vpc.py#L40-L45

サブネットではIPv6アドレスの自動割り当てを有効にします。サブネットにもCIDRブロックを割り当てる必要がありますが、リソース作成時にAmazonが自動でVPCに割り当てるCIDRブロックをもとにしてアドレスブロックを分割する必要があります。CloudFormationのFn::Cidr関数を使うことで、分割したアドレスブロックの配列を得ることができます。ここではFn::Selectを使って、分割したアドレスブロックを1つ取り出してサブネットのCIDRブロックとして指定します。

https://github.com/ShotaroTsuji/cdk-ipv6-subnets/blob/df86c461971fa4ee89b06a65ff38d98cf2cc51f6/cdk_ipv6_subnets/ipv6_vpc.py#L48-L60

VPCとサブネットを作成したのでInternet Gatewayを作成し、デフォルトルートを持つルートテーブルを作成します。

https://github.com/ShotaroTsuji/cdk-ipv6-subnets/blob/df86c461971fa4ee89b06a65ff38d98cf2cc51f6/cdk_ipv6_subnets/ipv6_vpc.py#L70-L94

最後に、HTTPでのアクセスを受け付けるセキュリティグループを作成します。IPv6でECSサービスが動作し、HTTPでのアクセスを受け付けられることを確かめたいので、IPv6でのHTTPのingressに対する許可とIPv6での全ての通信のegressに対する許可を設定します。

https://github.com/ShotaroTsuji/cdk-ipv6-subnets/blob/df86c461971fa4ee89b06a65ff38d98cf2cc51f6/cdk_ipv6_subnets/ipv6_vpc.py#L116-L135

ECS on Fargate

ECSの設定は、

  • 先ほど作成したVPCを利用するクラスターを作成する
  • タスク定義を作成する
  • サービスを作成して適切なセキュリティグループを設定する

という通常の流れで可能ですが、いくつか落とし穴があるのでそれらについて言及します。

コンテナイメージの指定

ECRはIPv6に対応していないためECRからコンテナイメージをpullする設定にした場合、サービスが起動しなくなります。幸い、Docker HubはIPv6に対応しているためDocker Hubに登録されたイメージを利用するように設定します。

https://github.com/ShotaroTsuji/cdk-ipv6-subnets/blob/df86c461971fa4ee89b06a65ff38d98cf2cc51f6/cdk_ipv6_subnets/cdk_ipv6_subnets_stack.py#L73-L76

ロギング

CloudWatch LogsもIPv6に対応していないため、 awslogs を利用する設定にするとサービスが起動しなくなります。今回はロギングを有効にするために、Fluent Bitを利用してS3バケットにログを保存するように設定します。

S3はIPv6に対応しているものの、デュアルスタックエンドポイントを利用する必要があります。エンドポイントとして https://s3.dualstack.aws-region.amazonaws.com を指定します。

https://github.com/ShotaroTsuji/cdk-ipv6-subnets/blob/df86c461971fa4ee89b06a65ff38d98cf2cc51f6/cdk_ipv6_subnets/cdk_ipv6_subnets_stack.py#L77-L87

議論

いくつか議論しておくべき点があるのでQ&A形式で書いていきます。

IPv6をECSサービスで使うのは実用的?

個人的にはNOだと思います。ECRやCloudWatch LogsがIPv6に対応していない点はかなりツラいと思いました。最初からECRもCloudWatch Logsも使わない構成にするのであれば良いかもしれませんが、お手軽にデプロイできるわけではないので、独自のログ基盤が必要といったシチュエーションでない限り、デメリットが消えることはないと思います。

ECRやCloudWatch LogsのためにNATではなくVPCエンドポイントは使わない?

コスト面でメリットになるかわからないので使うべきかはケースバイケースだと思います。
VPCエンドポイントの料金はap-northeast-1リージョンでは1つあたり0.014 USD/hourとなっています。

https://aws.amazon.com/jp/privatelink/pricing/

ECRを利用するためには3つのVPCエンドポイントが必要になります。
さらにCloudWatch Logsのエンドポイントも加えると合計で4つのエンドポイントが必要になり、時間課金の料金は0.056 USD/hourとなります。

NATゲートウェイの料金が0.064 USD/hourなのでこれにかなり近い金額になってしまいます。
とはいえ、データ転送に対する課金は、VPCエンドポイントはECRとCloudWatch Logsを利用する場合、1GBあたり0.0335 USD[^ インターフェースタイプが0.01 USD、S3のエンドポイントはゲートウェイタイプで0.0035 USDです。]となり、NATゲートウェイの0.062 USDに比べると半額になります。したがって、トラフィックの量次第ではNATゲートウェイよりVPCエンドポイントの方が安くなる可能性があります。

さいごに

ECSでIPv6を使うことに2024年2月時点でメリットがあるかと言われると微妙な気がしています。S3とECRとCloudWatch LogsがネイティブでIPv6に対応すれば使いやすくなると思いますが、現状だとIPv6への移行はまだまだ進まないだろうと感じています。

Discussion