ECS/EKSのIPアドレス設計
目次
はじめに
こんにちは。まだまだコンテナ環境の経験が少ないCloudBuildersのじんです。
設計に関して確認することも多いので、AWSのコンテナ実行環境(ここではECSとEKS)について、ドキュメントや動作を確認した内容をまとめてみたいと思います。
ECSやEKSについて、ある程度の規模の場合は設計の初期段階でネットワーク構成やIPアドレスの範囲を考えることになると思います。
今回はECS/EKSを設計/設定するにあたって、必要なIPアドレスに関係する観点で、設定対象や考慮点、実環境の確認方法を見ていきます。なお、対象としては、IPv4のプライベートサブネットとします。
ECS:IPアドレスの設定対象
ECSで必要なIPアドレス範囲には、
- コンテナインスタンス用サブネット
- タスク用サブネット
があります。こちらは、動作環境(インフラストラクチャ)がEC2の場合です。
コンテナインスタンス(EC2)でタスクが動作しますが、コンテナインスタンス自体が使うネットワークと、タスクが使うネットワークがそれぞれ必要です。コンテナインスタンスは、キャパシティプロバイダーでAutoScalingを設定して動作するEC2です。(図のIPアドレスは設定例です。)
インフラストラクチャがFargateの場合は、
- タスク用サブネット
のみとなります。タスク個別にFargateを使用します。
Fargate自体のネットワークはAWSによって管理されているため、タスクで使用するネットワークのみを設定します。(図のIPアドレスは設定例です。)
それぞれ、下記にようなネットワークとなります。
コンテナインスタンス(キャパシティプロバイダー)用のサブネット
インフラストラクチャをEC2とする場合に指定する、コンテナインスタンス用のサブネットとなります。
Fargateで動作させる場合、Fargate自体はAWSのマネージドサービスとして動作するため、インフラストラクチャ用にVPCやサブネットを指定する必要はありません。
また、クラスターのコントロールプレーンについても、AWSのマネージドサービスとして提供されるため、ユーザー側でネットワークを意識することはありません。
なお、サブネットはもちろんパブリックサブネットとすることも可能ですが、公開する際にELBを利用して公開すれば良いので、ECS自体をパブリックサブネットに置かないほうが良いでしょう。
タスク用のサブネット
ECSでは、サービスを作成すると、タスクという単位でコンテナを動作させますが、サービス作成時にタスクで使用するサブネットを指定することになります。
インフラストラクチャをFargateとする場合も、こちらは指定が必要です。
なお、タスク定義で、インフラストラクチャをEC2とした時に、ネットワークモードを「awsvpc」とします。これ以外も設定できますが、基本的にはawsvpcを使用することが多いと思いますので、awsvpcを前提にします。ネットワークモードについては、こちらを参考にして下さい。
ECS:IPアドレス設計に必要な考慮点
上記で挙げた対象について、設計する際には、下記のような点を考慮します。
コンテナインスタンス用のサブネット
- インフラストラクチャがEC2の場合のみ設計が必要です。
- コンテナインスタンスのサブネットは3つが推奨となります(最小1つのサブネットでも作成は可能です)。
- EC2単位でアドレスが必要となり、Auto Scalingでインスタンスが増えることを想定してサブネットの大きさを決める必要があります。
- コンテナインスタンスの最大数は、想定しているサービス/タスクとレプリカ/タスク数で必要なインスタンスタイプ/インスタンス数とそのピーク想定により異なるため、そのサイジングを行う必要があります。
なお、インスタンスは、とくにメモリでオーバーヘッドがあり、インスタンススペックをフルに使えるわけではありません。アドレス数に余裕を持って設計するか、動作検証をして確認したほうが良いかと思います。
※今回の記事では、AWSのVPCのCIDRやホスト数などの部分は説明を割愛しますので、下記など参考なるかと思います。
- タスクを多数動作させる場合は、インスタンスあたりに割り当て可能なタスク数を考慮する必要があります。
※タスクが消費するメモリ、CPUに余裕があっても、可能なタスク数≒ENI(Elastic Network Interface)数が不足するとスケールする必要があります。 - インスタンスあたりのタスク数を増やす設定(VPCトランキング)はECSのアカウント単位の設定で、デフォルトでOFFになっています。
- VPCトランキングを有効にした場合、インスタンス自体がENIを使う他に、Trunk用のENIを使うため、インスタンス毎に複数のIPが必要です。
タスク用のサブネット
- 個々のタスク単位でENIが作成され、IPが割り当てられるため、専用のサブネットとしたほうが良いでしょう。
- 起動するサービス/タスク数の最大数を想定し、サブネットを割り当てます。
- インフラストラクチャをFargateにした場合も、こちらのサブネットは必要です。
- タスク単位でスケジュール等でタスクを連続して実行する場合は、ENIが解放されるまで時間がかかることもあるため、アドレスが多く使用される可能性があることを考慮します。
- タスク単位となるので、サイドカー構成等複数コンテナを含むタスクでもIPはタスク単位となります。
- インスタンスあたりのタスク数がデフォルトでは少ないので、VPCトランキングを有効にすることを検討する必要があります。
※インスタンスタイプとタスク制限については下記のリンクを参考にして下さい。
拡張設定などの考慮点
VPCトランキング
インスタンスあたりのIP数を増やす場合は、VPCトランキングを有効にします。
トランキングを有効にする方法は、下記を参考にして下さい。
トランキングを有効にした場合は、下記の図のようにTrunk用のENIがコンテナインスタンス用のネットワーク側に作成され、Branchとしてタスク用のネットワークでタスク個々にENIが割り当てられます。(図のIPアドレスは設定例です。)
AWSVPC TrunkingでサポートされているEC2のインスタンスタイプと個数は下記を参考にして下さい。
AWSVPC TrunkingをONにした場合は、インスタンス用のENIとTrunk用のENIが起動当初に確保されます。
ELB
サービス公開、負荷分散のためALBやNLBを追加する場合は、そのサブネットやIPも考慮する必要があります。
ECS:構築時の設定
上記の設定について、実際に構築する際の指定を見ておきましょう。
コンテナインスタンス用のサブネットの設定
クラスター作成時に、EC2インスタンスのネットワーク設定を行います。
CloudFormationのテンプレートでは下記の部分となります(抜粋)。
クラスタを作成する際にコンテナインスタンスをAutoScaling Groupとして設定する部分となります。
ECSAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
VPCZoneIdentifier:
"subnet-021345abcdef67890"
クラスタ全体のテンプレートは下記が参考となります。
タスク用のサブネットの設定
サービス作成時に指定します。
CloudFormationのテンプレートでは下記の部分となります(抜粋)。
ECSService:
Type: 'AWS::ECS::Service'
Properties:
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- sg-abcdef01234567890
Subnets:
- subnet-021345abcdef67890
サービスのテンプレートは下記が参考となります。
ECS:IPアドレス割り当ての確認
実際に割り当てられたプライベートIPアドレスの確認は下記で可能です。
サブネットのIPアドレスの残数
マネージメントコンソールの場合は、VPC>サブネット>利用可能なIPv4アドレスでサブネットのIP残数を確認できます。
CLIで確認する場合は下記となります。
aws ec2 describe-subnets \
--subnet-ids SubnetId \
--query "Subnets[].AvailableIpAddressCount"
246
コンテナインスタンスに割り当てられたIPアドレス
マネージメントコンソールの場合は、ECS>クラスター>(クラスター名)>インフラストラクチャ>コンテナインスタンス>(コンテナインスタンス名)>リソースとネットワーキングで確認できます。
コマンドの場合は、コンテナインスタンス名からインスタンスIDを確認し、インスタンスに割り当てられているIPを確認します。
aws ecs describe-container-instances \
--cluster ClusterName \
--container-instances a1b2c3d4-5678-90ab-cdef-11111EXAMPLE \
--query "containerInstances[].ec2InstanceId" \
--output text
i-02d696ba109c8888e
aws ec2 describe-instances \
--instance-ids i-02d696ba109c8888e \
--query "Reservations[].Instances[].PrivateIpAddress" \
--output text
10.0.0.10
タスクに割り当てられたIPアドレス
マネージメントコンソールの場合は、ECS>クラスター>(クラスター名)>サービス>(サービス名)>タスク>(タスク)>設定で確認できます。(EC2/Fargate共通)
コマンドの場合はタスクのARNから確認します。
aws ecs describe-tasks \
--cluster MyCluster \
--tasks arn:aws:ecs:us-east-1:123456789012:task/MyCluster/4d590253bb114126b7afa7b58EXAMPLE \
--query "tasks[0].containers[*].networkInterfaces" \
--output text
f4941af3-731c-4f1d-9149-ff5437307374 10.0.4.58
ECS:IPアドレス残数の監視
運用時にIPアドレスの不足について監視することを考えておく必要があります。 CloudWatchで監視する場合、直接サブネットのIP残数のメトリクスはありません。監視する場合はカスタムメトリクスを作成する必要があります。
例えば、Lambdaを作成し、定期的に実行する、といったことになりますので、下記にLambdaのサンプルコードを記載しておきます。
import boto3
import os
cloudwatch = boto3.client('cloudwatch')
ec2 = boto3.client('ec2')
def lambda_handler(event, context):
subnet_id = os.environ['SUBNET_ID']
response = ec2.describe_subnets(SubnetIds=[subnet_id])
available_ips = response['Subnets'][0]['AvailableIpAddressCount']
cloudwatch.put_metric_data(
Namespace='Custom/VPC',
MetricData=[
{
'MetricName': 'AvailableIPCount',
'Dimensions': [
{'Name': 'SubnetId', 'Value': subnet_id}
],
'Value': available_ips,
'Unit': 'Count'
}
]
)
ECS:IPアドレス不足時の障害
実際にサブネットでIPアドレスが不足した場合、どのような障害になるかについても、確認しておきましょう。
コンテナインスタンス用のサブネットのIPアドレスが不足した場合
コンテナインスタンスがスケールできない状態となり、直接的にはタスクのステータスが「プロビジョニング」のまま実行中とならない状態となります。
ECS>クラスター>(クラスター名)>サービス>(サービス名)>タスク>(タスク)の一覧で、確認できます。
インスタンスについては、Auto Scalingグループで下記のような失敗のステータスとなります。
ステータス:失敗
説明:Launching a new EC2 instance. Status Reason: insufficient free addresses to allocate 1 addresses in subnet subnet-021345abcdef67890. Launching EC2 instance failed.
タスク用のサブネットでIPアドレスが不足した場合
新たにタスクが開始できずに停止する(繰り返し実行しようとするために、前回のステータスが「停止済み」となるタスクが複数発生する)状態となります。
GUIでは、ECS>クラスター>(クラスター名)>サービス>(サービス名)>タスク>(タスク)の一覧で、前回のステータスが「停止済み」のものに下記のようなエラー表示されます。
Unexpected EC2 error while attempting to Create Network Interface in subnet 'subnet-021345abcdef67890': InsufficientFreeAddressesInSubnet
コマンドでは、下記となります。
まず、停止したタスクの一覧を表示します。
aws ecs list-tasks \
--cluster cluster_name \
--desired-status STOPPED \
--region region
{
"taskArns": [
"arn:aws:ecs:ap-northeast-1:123456789012:task/testECSCluster/02f04ae5684a438d9efa458a40718e62",
"arn:aws:ecs:ap-northeast-1:123456789012:task/testECSCluster/f56bc7c4a07944da86c6a698ad5cfc13"
]
}
タスクARNを指定し、詳細を確認します。
aws ecs describe-tasks \
--cluster testECSCluster \
--tasks arn:aws:ecs:ap-northeast-1:123456789012:task/testECSCluster/02f04ae5684a438d9efa458a40718e62 \
--region ap-northeast-1 \
--query "tasks[0].stoppedReason"
"Unexpected EC2 error while attempting to Create Network Interface in subnet 'subnet-021345abcdef67890': InsufficientFreeAddressesInSubnet"
EKS:ECSとの用語の違い
ここからは、EKSの話となります。最初にECSとEKSの用語のマッピングをしておきます。
Kubernetesの用語となり、ECSでコンテナインスタンスと呼んでいたものはここでは、ノードとなります。
個々のコンテナが動作するタスクは、ポッドと呼ばれます。
EKS:IPアドレスの設定対象
EKSで必要なIPアドレス範囲には、
- クラスタエンドポイント用サブネット(コントロールプレーン通信用)
- KubernetesサービスのIPアドレスブロック
- ノード/ポッド用のサブネット
があります。
下記の図は、ポッドの動作環境にノード(EC2インスタンス)を使う場合です。
VPCには、クラスタエンドポイント等で使用するサブネットとノードとポッドで使用するサブネットを作成します。ノードとポッドは同じサブネットを使用し、デフォルトではENIのプライマリIPとENIに対して複数のセカンダリIPが割り当てられます。
VPCとは別ですが、Kubernetesサービスで使用するアドレスブロックも上記のネットワークと重複しない範囲で決める必要があります。(IPアドレスは設定例です。)
Fargateの場合も必要なIPアドレス範囲は同じですが、Fargate自体(ノード)のネットワークの考慮は不要で、ポッド用のサブネットのみを設定します。(IPアドレスは設定例です。)
それぞれのネットワークは、下記となります。
クラスタエンドポイント用サブネット(コントロールプレーン通信用)
API通信エンドポイントのENIを作成するVPCのサブネットですが、使用する機能により他にもエンドポイントが作成されることがあります。
マスター(コントロールプレーン)部分は、AWSのマネージドサービスとなるため、指定したサブネットにエンドポイントが作成されます。
KubernetesサービスのIPアドレスブロック
Kubernetesのサービスで使用するCIDRを指定します。
これはVPCで管理されるネットワークではなく、EKS内部のネットワークとなります。
ノード/ポッド用のサブネット
ノードおよびポッドで使用されるサブネットで、こちらはVPCのサブネットです。
EKS:IPアドレス設計に必要な考慮点
それぞれの設定対象について、設計する際に下記を考慮します。
クラスタエンドポイント用サブネット
- 2つ以上のサブネットが必要となります。
- クラスター作成時に異なる2つのAZのサブネットに対して1つずつENIが作成されます。IP アドレスは 16 個以上が推奨となります。(下記参照)
- API通信用のエンドポイントは最初に作成されます。API通信用のエンドポイントはパブリック/プライベートまたはその共用が選択可能です。パブリックとした場合に外部からAPI操作をすることが可能となります。バプリックとする場合も、サブネットとしては、パブリックサブネットである必要はありません。
- クラスタエンドポイントをパブリック/プライベート共用かプライベート専用にした場合は、VPC内でエンドポイント名がENIのプライベートIPに解決されるようになります。
KubernetesサービスのIPアドレスブロック
- 指定しない場合は、/16のブロックが自動で振り当てられます(172.20.0.0/16)。
- EKSで使用するVPCのCIDRと競合しない範囲である必要があります。
- 10.0.0.0/8、172.16.0.0/12、または 192.168.0.0/16 のいずれかの範囲内である必要があります。
- 最小サイズが /24、最大サイズが /12となります。
ノード/ポッド用のサブネット
- クラスタエンドポイント用サブネットと同じVPCである必要があります。
- EKSの場合、ノード(コンテナが起動するインスタンス)に割り当てられるサブネットをポッドでも使用することになるので、このサブネットは専用のサブネットとしたほうが良いでしょう。
- 公開する場合は、ELBやCloudFront+ELBを公開すれば良いので、EKS自体はプライベートサブネットに置いたほうが良いです。
- 起動するノード、ポッド数の最大数を想定し、サブネットを割り当てます。
- アドオンで使用するサービスによって、ポッドがノード毎に起動するため、その分のオーバーヘッドを考慮する必要があります(CoreDNSなど)。
- ノード(EC2)はAutoScaling Groupとして起動します。
- Fargateを使う場合は、Frageteプロファイルを作成する際にサブネットを指定します。Fargateの場合は、ノードを意識する必要はなく、ポッドのIPアドレスだけがこのサブネットから割り当てられます。
ノードグループのスケーリングについて
デフォルトでは、ノードで動作するポッドに割り当てられるアドレスは、ノードのENIのIPアドレスか、セカンダリIPアドレスとなります。
このデフォルト設定の場合は、ノードの起動時にENI2つ分のセカンダリIPアドレスを含め確保されます。このため、スケール時にまとまってIPが確保される可能性があります。
ノード当たりのポッド台数について
インスタンスタイプによるノードで起動できるポッド台数を確認するには下記リンクにあるスクリプト(max-pods-calculator.sh)を実行して確認します。
ノード当たりのポッド台数を後述するプレフィックスモード(後述)で拡張することも可能です。
拡張設定などの考慮点
プレフィックスモード
ノード当たりのポッド台数を拡張するには、プレフィックスモードを使用する必要があります。
これにより、ワーカーノードで起動できるPodがかなり増えるので、その台数を踏まえ範囲設定を行う必要があります。この記事は、ECSとEKSの比較が目的ではないですが、IPアドレス観点でのポッド(ECSのタスク)の集積度はEKSのほうが高くできそうです。
また、これを実装する場合は、プレフィックスが/16単位で確保される(ENIのIPv4 プレフィックス委任)ので、実際のPodより先行して確保されます。下記の図のようにENIでプレフィックス委任として/28の範囲が確保され、そのアドレス範囲からポッドにIPが割り当てられる動作となります。
詳細については、下記のリンクを参照して下さい。
ELB
サービス公開、負荷分散のためALBやNLBを追加する場合は、そのサブネットやIPも考慮する必要があります。
EKS:構築時の設定
上記の設定について、実際に構築する際の指定を見ておきましょう。
クラスタエンドポイント用サブネットの設定
クラスター作成時にネットワーク設定を行います。
CloudFormationのテンプレートでは下記の部分となります(抜粋)。
EKSCluster:
Type: AWS::EKS::Cluster
Properties:
ResourcesVpcConfig:
SecurityGroupIds:
- sg-6979fe18
SubnetIds:
- subnet-6782e71e
- subnet-e7e761ac
クラスターのテンプレートは下記が参考となります。
KubernetesサービスのIPアドレスブロックの設定
クラスター作成時のネットワーク設定で、「KubernetesサービスのIPアドレスブロックを設定する」という項目で設定を行います。(画面は上で上げたクラスター作成時のネットワーク設定の最後の部分)
CloudFormationのテンプレートでは下記の部分となります(抜粋)。
EKSCluster:
Type: AWS::EKS::Cluster
Properties:
KubernetesNetworkConfig:
IpFamily: ipv4
ServiceIpv4Cidr: xxx.xxx.xxx.xxx/xx
クラスターのテンプレートは参考リンクは、前掲のものと同じとなります。
ノード/ポッド用のサブネットの設定
EC2を使用する場合、ノードグループのコンピューティング設定となります。
CloudFormationのテンプレートでは下記の部分となります(抜粋)。
EKSNodegroup:
Type: AWS::EKS::Nodegroup
Properties:
Subnets:
- subnet-6782e71e
- subnet-e7e761ac
ノードグループのテンプレートは下記が参考となります。
Fargateを使用する場合、Fargateプロファイル設定のサブネット指定となります。
CloudFormationのテンプレートでは下記の部分となります(抜粋)。
EKSFargateProfile:
Type: AWS::EKS::FargateProfile
Properties:
Subnets:
- subnet-6782e71e
- subnet-e7e761ac
Fargateプロファイルのテンプレートは下記が参考となります。
EKS:IPアドレス割り当ての確認
実際に割り当てられたプライベートIPアドレスの確認は下記で可能です。
サブネットのIPアドレスの残数
(ECSと同様のため省略します。)
クラスタエンドポイント用サブネットの設定に割り当てられたIPアドレス
マネージメントコンソールの場合は、EKS>クラスター>(クラスター名)>リソース>サービスとネットワーキング>Kubernetesでエンドポイントに割り当てられたIPとクラスターIPを確認できます。
コマンドの場合は下記となります。Endpintsでエンドポイントに割り当てられたプライベートIPを確認できます。
kubectl describe service kubernetes | grep Endpoints
Endpoints: 10.12.4.45:443,10.12.5.45:443
Kubernetesサービスに割り当てられたIPアドレス
マネージメントコンソールの場合は、クラスタエンドポイントに割り当てられたIPアドレスと同じ画面で確認ができます。(クラスターIP)
コマンドの場合は下記となります。CLUSTER-IPでプライベートIPアドレスを確認できます。
kubectl get service -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 192.168.0.1 <none> 443/TCP 98m
kube-system eks-extension-metrics-api ClusterIP 192.168.0.48 <none> 443/TCP 98m
kube-system kube-dns ClusterIP 192.168.0.10 <none> 53/UDP,53/TCP,9153/TCP 87m
ノードに割り当てられたIPアドレス
マネージメントコンソールの場合は、EKS>クラスター>(クラスター名)>コンピューティング>ノード名にIPアドレスが含まれるので、割り当てられたプライベートIPアドレスを確認できます。
コマンドの場合は下記となります。INTERNAL-IPでプライベートIPを確認できます。
kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
ip-10-1-0-53.ap-northeast-1.compute.internal Ready <none> 6m18s v1.31.2-eks-94953ac 10.1.0.53 <none> Amazon Linux 2 5.10.228-219.884.amzn2.x86_64 containerd://1.7.23
ポッドに割り当てられたIPアドレス
マネージメントコンソールの場合は、EKS>クラスター>(クラスター名)>リソース>ワークロード>ポッド>(namespace選択)>(ポッド名)でポッドに割り当てられたIP(ポッドIP)を確認できます。EC2の場合もFargateの場合も同様となります。
コマンドの場合は下記となります。(Fargateも同様です。)
kubectl get pod -n eks-sample-app -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
eks-sample-linux-deployment-f6d4ddcc4-kv2jk 1/1 Running 0 23s 10.1.0.30 ip-10-1-0-53.ap-northeast-1.compute.internal <none> <none>
eks-sample-linux-deployment-f6d4ddcc4-mmbfk 1/1 Running 0 23s 10.1.0.19 ip-10-1-0-53.ap-northeast-1.compute.internal <none> <none>
EKS:IPアドレス残数の監視
(ECSと同様のため、省略します。)
EKS:IPアドレス不足時の障害
ノードグループのサブネット
ノードがIPアドレス不足でスケールできない場合、ECSと同様AutoScalingが失敗します。
ステータス:失敗
説明:Launching a new EC2 instance. Status Reason: insufficient free addresses to allocate 1 addresses in subnet subnet-021345abcdef67890. Launching EC2 instance failed.
また、ノードグループでもヘルスの問題が発生します。
問題の種類:InsufficientFreeAddresses
説明:Amazon AutoScaling was unable to launch instances because there are not enough free addresses in the subnet associated with your AutoScaling group(s).
なお、デフォルトのセカンダリIPモードでは、ノードで使用するIPアドレスを2ENI分先に確保することになります。この時に、IPアドレスが1だけあればスケールします(AutoScalingは成功)が、ポッド分のIPアドレスが足らない(セカンダリIPが割り当てられない)場合、ポッドのステータスがPendingとなります。
ポッドのサブネット
IPが不足している状態の場合、ポッドが起動せず、Pendingのステータスとなります。describe podで表示させると下記のようなWarningが確認できます。
FailedCreatePodSandBox
Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "xxxxxxxxx": plugin type="aws-cni" name="aws-cni" failed (add): add cmd: failed to assign an IP address to container