Amazon VPC CNI@Amazon EKSアドオン¶
はじめに¶
本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。
01. Amazon VPC CNIとは¶
Amazon VPC CNIがAmazon EKS Cluster内に無い場合、EC2ワーカーNodeにアタッチされるはずのAWS ENIを作成できない。
そのため、何も通信ができなくなるため、PodやServiceにIPアドレスが自動的に割り当てられないため、必須である。

02. Amazon VPC CNIの仕組み¶
アーキテクチャ¶
Amazon EKS Cluster内に L3 を提供する。
Amazon VPC CNIは、L-IPAMデーモン (aws-node という名前のDaemonSet) 、CNIバイナリ、といったコンポーネントから構成されている。
Amazon EKS Cluster内にネットワークを作成する。
CNIバイナリ¶
CNIバイナリは、L-IPAMデーモンからIPアドレスを取得する。
Podを新しく作成するときに、kubeletからのリクエストによって、新しいPodをNode内のClusterネットワークに参加させる。
L-IPAMデーモン:Local IP Address Manager Daemon¶
▼ L-IPAMデーモンとは¶
L-IPAMデーモンは、割り当てモードに応じて、IPアドレスをPodに割り当てる。
▼ ログ¶
L-IPAMデーモンは、var/log/aws-routed-eni/ipamd.log ファイルと /var/log/aws-routed-eni/plugin.log ファイルにログを出力する。
Amazon VPC CNIが正しく動作しない場合、L-IPAMデーモンのログを確認する。
03. セットアップ¶
マニフェスト¶
kind: DaemonSet
apiVersion: apps/v1
metadata:
name: aws-node
namespace: kube-system
labels:
app.kubernetes.io/name: aws-node
app.kubernetes.io/instance: aws-vpc-cni
k8s-app: aws-node
app.kubernetes.io/version: "v1.15.3"
spec:
updateStrategy:
rollingUpdate:
maxUnavailable: 10%
type: RollingUpdate
selector:
matchLabels:
k8s-app: aws-node
template:
metadata:
labels:
app.kubernetes.io/name: aws-node
app.kubernetes.io/instance: aws-vpc-cni
k8s-app: aws-node
spec:
priorityClassName: "system-node-critical"
serviceAccountName: aws-node
hostNetwork: true
initContainers:
- name: aws-vpc-cni-init
image: 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni-init:v1.15.3
env:
- name: DISABLE_TCP_EARLY_DEMUX
value: "false"
- name: ENABLE_IPv6
value: "false"
securityContext:
privileged: true
resources:
requests:
cpu: 25m
volumeMounts:
- mountPath: /host/opt/cni/bin
name: cni-bin-dir
terminationGracePeriodSeconds: 10
tolerations:
- operator: Exists
securityContext: {}
containers:
# CNIの実体
- name: aws-node
image: 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni:v1.15.3
ports:
- containerPort: 61678
name: metrics
livenessProbe:
exec:
command:
- /app/grpc-health-probe
- -addr=:50051
- -connect-timeout=5s
- -rpc-timeout=5s
initialDelaySeconds: 60
timeoutSeconds: 10
readinessProbe:
exec:
command:
- /app/grpc-health-probe
- -addr=:50051
- -connect-timeout=5s
- -rpc-timeout=5s
initialDelaySeconds: 1
timeoutSeconds: 10
env:
- name: ADDITIONAL_ENI_TAGS
value: "{}"
- name: AWS_VPC_CNI_NODE_PORT_SUPPORT
value: "true"
- name: AWS_VPC_ENI_MTU
value: "9001"
- name: AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG
value: "false"
- name: AWS_VPC_K8S_CNI_EXTERNALSNAT
value: "false"
- name: AWS_VPC_K8S_CNI_LOGLEVEL
value: "DEBUG"
- name: AWS_VPC_K8S_CNI_LOG_FILE
value: "/host/var/log/aws-routed-eni/ipamd.log"
- name: AWS_VPC_K8S_CNI_RANDOMIZESNAT
value: "prng"
- name: AWS_VPC_K8S_CNI_VETHPREFIX
value: "eni"
- name: AWS_VPC_K8S_PLUGIN_LOG_FILE
value: "/var/log/aws-routed-eni/plugin.log"
- name: AWS_VPC_K8S_PLUGIN_LOG_LEVEL
value: "DEBUG"
- name: DISABLE_INTROSPECTION
value: "false"
- name: DISABLE_METRICS
value: "false"
- name: DISABLE_NETWORK_RESOURCE_PROVISIONING
value: "false"
- name: ENABLE_IPv4
value: "true"
- name: ENABLE_IPv6
value: "false"
- name: ENABLE_POD_ENI
value: "false"
- name: ENABLE_PREFIX_DELEGATION
value: "false"
- name: VPC_CNI_VERSION
value: "v1.15.3"
- name: WARM_ENI_TARGET
value: "1"
- name: WARM_PREFIX_TARGET
value: "1"
- name: MY_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
- name: MY_POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
resources:
requests:
cpu: 25m
securityContext:
capabilities:
add:
- NET_ADMIN
- NET_RAW
volumeMounts:
- mountPath: /host/opt/cni/bin
name: cni-bin-dir
- mountPath: /host/etc/cni/net.d
name: cni-net-dir
- mountPath: /host/var/log/aws-routed-eni
name: log-dir
- mountPath: /var/run/aws-node
name: run-dir
- mountPath: /run/xtables.lock
name: xtables-lock
# Node Agent (aws-network-policy-agent)
# NetworkPolicyをCluster全体に適用する
- name: aws-eks-nodeagent
image: 602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-network-policy-agent:v1.0.5
env:
- name: MY_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
args:
- --enable-ipv6=false
- --enable-network-policy=false
- --enable-cloudwatch-logs=false
- --enable-policy-event-logs=false
- --metrics-bind-addr=:8162
- --health-probe-bind-addr=:8163
resources:
requests:
cpu: 25m
securityContext:
capabilities:
add:
- NET_ADMIN
privileged: true
volumeMounts:
- mountPath: /host/opt/cni/bin
name: cni-bin-dir
- mountPath: /sys/fs/bpf
name: bpf-pin-path
- mountPath: /var/log/aws-routed-eni
name: log-dir
- mountPath: /var/run/aws-node
name: run-dir
volumes:
- name: bpf-pin-path
hostPath:
path: /sys/fs/bpf
- name: cni-bin-dir
hostPath:
path: /opt/cni/bin
- name: cni-net-dir
hostPath:
path: /etc/cni/net.d
- name: log-dir
hostPath:
path: /var/log/aws-routed-eni
type: DirectoryOrCreate
- name: run-dir
hostPath:
path: /var/run/aws-node
type: DirectoryOrCreate
- name: xtables-lock
hostPath:
path: /run/xtables.lock
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
- key: kubernetes.io/arch
operator: In
values:
- amd64
- arm64
- key: eks.amazonaws.com/compute-type
operator: NotIn
values:
- fargate
03-02. 設定¶
バージョン¶
Kubernetesのバージョンに応じて、異なるアドオンのバージョンを使用する必要がある。
環境変数¶
| 環境変数 | 説明 | 設定例 |
|---|---|---|
ADDITIONAL_ENI_TAGS |
{} |
|
ANNOTATE_POD_IP |
true |
|
AWS_VPC_CNI_NODE_PORT_SUPPORT |
true |
|
AWS_VPC_ENI_MTU |
9001 |
|
AWS_VPC_K8S_CNI_CONFIGURE_RPFILTER |
false |
|
AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG |
false |
|
AWS_VPC_K8S_CNI_EXTERNALSNAT |
false |
|
AWS_VPC_K8S_CNI_LOGLEVEL |
Amazon VPC CNIのログレベルを設定する。 | DEBUG |
AWS_VPC_K8S_CNI_LOG_FILE |
Amazon VPC CNIのログファイルの保管先を設定する。 | /host/var/log/aws-routed-eni/ipamd.log |
AWS_VPC_K8S_CNI_RANDOMIZESNAT |
prng |
|
AWS_VPC_K8S_CNI_VETHPREFIX |
eni |
|
AWS_VPC_K8S_PLUGIN_LOG_FILE |
Amazon VPC CNIのプラグインのログファイルの保管先を設定する。 | /var/log/aws-routed-eni/plugin.log |
AWS_VPC_K8S_PLUGIN_LOG_LEVEL |
Amazon VPC CNIのプラグインのログレベルを設定する。 | DEBUG |
CLUSTER_ENDPOINT |
Amazon EKS ClusterのエンドポイントのURLを設定する。 | https://*****.sk1.ap-northeast-1.eks.amazonaws.com |
CLUSTER_NAME |
Amazon EKS Clusterの名前を設定する。 | foo-cluster |
DISABLE_INTROSPECTION |
false |
|
DISABLE_METRICS |
false |
|
DISABLE_NETWORK_RESOURCE_PROVISIONING |
false |
|
ENABLE_IPv4 |
true |
|
ENABLE_IPv6 |
false |
|
ENABLE_POD_ENI |
Podにセキュリティグループを紐づける機能 (Security groups for Pods) を有効化するかどうかを設定する。 | false |
ENABLE_PREFIX_DELEGATION |
Prefix delegationモードを有効化するかを設定する。 | false |
MAX_ENI |
Amazon EC2/FargateワーカーNode当たりで最大で紐づけるENI数を設定する。 | 20 |
MINIMUM_IP_TARGET |
WARM_ENI_TARGET と競合するため、デフォルトでは設定されていない。Amazon EC2/FargateワーカーNode当たりで最低限確保するセカンダリープライベートIPアドレス数を設定する。 |
20 |
MY_NODE_NAME |
ワーカーNode名が設定されているマニフェストのキーを設定する。 | "fieldRef": {"apiVersion": "v1","fieldPath": "spec.nodeName"}} |
MY_POD_NAME |
Pod名が設定されているマニフェストのキーを設定する。 | "fieldRef": {"apiVersion": "v1","fieldPath": "metadata.name"}} |
POD_SECURITY_GROUP_ENFORCING_MODE |
Podのセキュリティグループの適用方法を設定する。注意点として、Podの送信元IPアドレスにも影響を与える。 | standard (standard の場合は、プライマリーENIのセキュリティグループを適用する) |
VPC_ID |
Amazon VPCのIDを設定する。 | vpc-***** |
WARM_ENI_TARGET |
Amazon EC2/FargateワーカーNode当たりで最低限確保するAWS ENI数を設定する。 | 1 |
WARM_PREFIX_TARGET |
1 |
|
WARM_IP_TARGET |
WARM_ENI_TARGET と競合するため、デフォルトでは設定されていない。Amazon EC2/FargateワーカーNode当たりでウォーム状態にしておくセカンダリープライベートIPアドレス数を設定する。WARM_ENI_TARGET の値が小さすぎると、EC2-APIのコール回数が増え、リクエスト数制限にひっかかる可能性がある。 |
2 |
- https://github.com/aws/amazon-vpc-cni-k8s#cni-configuration-variables
- https://aws.github.io/aws-eks-best-practices/networking/vpc-cni/#configure-ip-and-eni-target-values-in-address-constrained-environments
- https://repost.aws/ja/knowledge-center/eks-configure-cni-plugin-use-ip-address
- https://dunkshoot.hatenablog.com/entry/eks_reduce_number_of_ipaddress
- https://zenn.dev/nshmura/articles/fbb53aaf6fed8c
- https://www.grugrut.net/posts/202107250958/
確認方法¶
L-IPAMデーモンが aws-node というDaemonSetとして稼働している。
これのコンテナの環境変数で、アドオンの設定が管理されている。
$ kubectl get daemonset aws-node \
-n kube-system -o \
jsonpath='{.spec.template.spec.containers[*].env}' \
| jq .
04. Podの上限数を上げる¶
上限数の決まり方¶
Nodeのインスタンスタイプごとに、紐付けられるセカンダリーIPアドレス数に制限がある。
そのため、Node上でスケジューリングさせるPod数がインスタンスタイプに依存する。
t3.nano |
t3.micro |
t3.small |
t3.medium |
t3.large |
t3.xlarge |
t3.2xlarge |
|
|---|---|---|---|---|---|---|---|
Node1 個 |
4 | 4 | 11 | 17 | 35 | 58 | 58 |
2 個 |
8 | 8 | 22 | 34 | 70 | 116 | 116 |
3 個 |
12 | 12 | 33 | 51 | 105 | 174 | 174 |
4 個 |
16 | 16 | 44 | 68 | 140 | 232 | 232 |
現在の上限数¶
kubectl describe コマンドの Capacity 項目で、現在のPodの上限数を確認できる。
$ kubectl describe node <Node名>
...
Capacity:
cpu: 2
ephemeral-storage: 20959212Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 8022992Ki
pods: 35 # Podの上限数
...
04-02. セカンダリーIP割り当てモードの場合¶
セカンダリーIPアドレス割り当てモードとは¶
AWSのENIには、セカンダリーIPアドレス割り当てという機能がある。
L-IPAMデーモンは、元からあるこの機能を利用し、NodeのAWS ENIに紐づけられたセカンダリープライベートIPアドレスをPodに割り当てる。
この時、Nodeのインスタンスタイプごとに、紐付けられるセカンダリープライベートIPアドレス数に制限がある。このため、Node上でスケジューリングさせるPod数はインスタンスタイプに依存する。
執筆時点 (2022/09/24) のFargateでは、インスタンスタイプに限らずNode当たり 1 個しかPodをスケジューリングさせられない。

- https://aws.github.io/aws-eks-best-practices/networking/vpc-cni/
- https://itnext.io/kubernetes-is-hard-why-eks-makes-it-easier-for-network-and-security-architects-ea6d8b2ca965
- https://medium.com/elotl-blog/kubernetes-networking-on-aws-part-ii-47906de2921d
- https://github.com/awslabs/amazon-eks-ami/blob/master/files/eni-max-pods.txt
IPアドレス割り当ての仕組み¶
(1)-
L-IPAMデーモンは、ENIとセカンダリープライベートIPアドレスの情報を、CNIバイナリにプールする。
プールのENIとセカンダリープライベートIPアドレスの数は、
MINIMUM_IP_TARGETとWARM_IP_TARGET(またはWARM_ENI_TARGET) の合計数で決まる。 (2)-
kubeletは、kube-apiserverからPodの
ADD/DELリクエストを受信する。Podの
ADD/DELリクエストをCNIバイナリに送信する。 (3)-
L-IPAMデーモンはCNIバイナリを参照する。
また、CNIバイナリ上の情報に応じてEC2-APIをコールし、ENIをNodeに割り当てる。
反対に、NodeのENIを解放し、ENIのプールに戻す。

(3)-
kubeletは、セカンダリープライベートIPアドレスの
ADD/DELリクエストを、CNIバイナリに送信する。 (4)-
CNIバイナリは、L-IPAMデーモンのプールからIPアドレスを取得し、Podにこれを割り当てる。
反対に、PodからIPアドレスを解放し、L-IPAMデーモンのプールに戻す。
必要に応じて、

セットアップ¶
▼ 環境変数¶
MINIMUM_IP_TARGET (Node当たり最低限のセカンダリープライベートIPアドレス数) または WARM_IP_TARGET (Node当たりのウォーム状態のセカンダリープライベートIPアドレス数) で、Node当たりのPod数を設定する。
他にも設定可能な変数があるが、ここではこの2つを使用する。
MINIMUM_IP_TARGET には、Podの冗長化数も加味して予想されるPod数分プラスアルファを設定する。
また、WARM_IP_TARGET には、ウォーム状態のセカンダリープライベートIPアドレスを設定する。
Podの上限数を上げる場合、Amazon EKSが属するAmazon VPCサブネットで確保するセカンダリープライベートIPアドレス数も考慮すること。
シナリオ¶
例えば、Node当たりにスケジューリングされるPod数の最大数が、Podの冗長化の数も考慮して、10 個だとする。
(1)-
Nodeで小さめのインスタンスサイズを選びつつ、
MINIMUM_IP_TARGET=10+2とWARM_IP_TARGET=2を設定する。 (2)-
Node当たり
12個のセカンダリープライベートIPアドレスを確保する。さらに追加で、常に
2個のセカンダリープライベートIPアドレスをウォーム状態にしておくようになる。結果、最初
12個のPodをスケジューリングできる。 (3)-
Podが
12個を超えた段階で、合計のセカンダリープライベートIPアドレス数は2個のウォーム状態数を維持しながら増えていく。
- https://github.com/aws/amazon-vpc-cni-k8s/blob/master/docs/eni-and-ip-target.md
- https://github.com/awslabs/amazon-eks-ami/blob/master/files/eni-max-pods.txt
- https://dunkshoot.hatenablog.com/entry/eks_reduce_number_of_ipaddress
- https://qiita.com/hkame/items/1378f9176a26e39d93c7#%E3%83%8E%E3%83%BC%E3%83%89%E3%81%AE%E7%A2%BA%E4%BF%9Dip%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%82%92%E6%B8%9B%E3%82%89%E3%81%99
- https://zenn.dev/nshmura/articles/fbb53aaf6fed8c#minimum_ip_target-%E3%81%A8-warm_ip_target%E3%81%AB%E3%82%88%E3%82%8Bip%E7%A2%BA%E4%BF%9D%E3%81%AE%E4%BE%8B
04-03. Prefix delegationモードの場合¶
Prefix delegationモード (プレフィクス委譲モード) とは¶
AWSのENIには、Prefix delegation (プレフィクス委譲) という機能がある。
L-IPAMデーモンは、元からあるこの機能を利用し、NodeのENIにCIDR (サブネット内の *.*.*.*/28) を割り当て、これから取得したIPアドレスをPodに割り当てる。
ENIの個数を増やすごとに、16 個分のIPアドレス (サブネット内の *.*.*.*/28) を確保できる。
Prefix delegationモードを使用する場合、Nodeを配置するAWSサブネットのCIDRを *.*.*.*/28 よりも大きくしておく必要がある。

セットアップ¶
▼ Amazon VPC CNI¶
Prefix delegationモードを採用可能なインスタンスタイプを選ぶ。
Amazon VPC CNIの環境変数の ENABLE_PREFIX_DELEGATION に true を設定する。
resource "aws_eks_addon" "vpc_cni" {
cluster_name = aws_eks_cluster.foo.name
addon_version = "<バージョン>"
addon_name = "vpc-cni"
resolve_conflicts_on_update = "OVERWRITE"
# 環境変数を設定する
configuration_values = jsonencode(
{
env = {
ENABLE_PREFIX_DELEGATION = "true"
}
}
)
}
▼ bootstrap.sh ファイル¶
AWSのセルフマネージドNodeグループで任意のAMIを使用していたり、またはマネージドNodeグループで起動テンプレートを使ってNodeを作成している場合は、以降の手順が必要になる。
一方で、AMIを指定していなかったり、起動テンプレートを使用していない場合には、以降の手順は不要である (v1.9 以上のAmazon VPC CNI)。
max-pods-calculator.sh ファイルを使用して、事前にPodの最大数を計算しておく。
なお、vCPUが 30 未満のインスタンスタイプの場合に最大数は 110 個になり、それ以外の場合は 250 個になる。
設定した上限に合わせて、CIDR (サブネット内の *.*.*.*/28) の予約が必要になる。
例えば、48 個のPodを乗せたいなら、CIDR (サブネット内の *.*.*.*/28) 当たりで 16 個のIPアドレスを使用できるようになるので、CIDR (サブネット内の *.*.*.*/28) は 3 個予約する必要がある。
# ファイルをダウンロードする
$ curl -O https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/max-pods-calculator.sh
# 権限を変更する
$ chmod +x max-pods-calculator.sh
# --cni-prefix-delegation-enabledオプションを有効化した上でPod最大数を計算する
$ ./max-pods-calculator.sh \
--instance-type <インスタンスタイプ> \
--cni-version <Amazon VPC CNIのバージョン> \
--cni-prefix-delegation-enabled
bootstrap.sh ファイルに必要なパラメーターを渡す。
#!/bin/bash
# ユーザーデータファイル
/etc/eks/bootstrap.sh foo-eks-cluster \
--b64-cluster-ca $B64_CLUSTER_CA \
--apiserver-endpoint $APISERVER_ENDPOINT \
--container-runtime containerd \
--use-max-pods false \
--kubelet-extra-args "--max-pods=<max-pods-calculator.shファイルから取得したPodの最大数>"
▼ 環境変数¶
MINIMUM_IP_TARGET (Node当たりの *.*.*.*/28 を持つENIの個数) または WARM_IP_TARGET (Node当たりのウォーム状態のセカンダリープライベートIPアドレス数) で、Node当たりのPod数を設定する。
他にも設定可能な変数があるが、ここではこの2つを使用する。
セカンダリーIPアドレス割り当てモードとの比較¶
▼ IPアドレス数¶
AWSドキュメントでEC2 Nodeに割り当てられるIPアドレスを増やす方法を調べると、従来のセカンダリーIPアドレス割り当てモードではなく、Prefix delegationモードの記載が充実している。
AWSとしては、Prefix delegationモードの方を使って欲しいのかもしれない。
実際、セカンダリーIPアドレス割り当てモードでは、割り当てられるIPアドレスが劇的に増えないため、Prefix delegationモードのほうが良い。
▼ セカンダリーIPアドレス割り当てモードからの移行¶
もし、セカンダリーIPアドレス割り当てモードからPrefix delegationモードに移行する場合、既存のNodeグループとNodeを削除したうえで、Nodeを新規作成する。
ローリングアップグレードで移行する場合、セカンダリーIPアドレス割り当てモードとPrefix delegationモードの両方をNodeに適用してしまう。
シナリオ¶
05. 代替¶
- Antrea (
L3/L4) - Calico (
L3) - Cilium (
L3/L4/L7) - Cloud-Native Contail Networking