🧑‍🚒

Amazon EKS ワーカーノードが確保するIPアドレスを減らす

2022/05/07に公開

Original Post

https://nshmura.com/posts/reduced-ip-reserved-by-eks-nodes/

はじめに

EKSワーカーノードが、VPCサブネットのプライベートIPアドレスを大量に消費してしまい、プライベートIPアドレスが枯渇しまう問題が発生した。

暫定的な対策を取った記録をまとめる。

やったこと

最初に暫定対策を書いておく。

デフォルトで入っている aws-node プラグインの設定値を変更して、余分に確保されていたプライベートIPアドレスを開放した。

下記のコマンドを実行すると、aws-node が全て再起動して設定が反映される。

kubectl -n kube-system set env daemonset aws-node WARM_ENI_TARGET-
kubectl -n kube-system set env daemonset aws-node MINIMUM_IP_TARGET=20
kubectl -n kube-system set env daemonset aws-node WARM_IP_TARGET=2

EKSワーカーノードである、EC2インスタンス1台ごとに、最低20個のIPアドレスが確保され、常に2個のIPアドレスが余分に確保されるようになる。

変更前は、r5.xlarge のEC2インスタンス1台ごとに、最低でも28個のIPアドレスが確保され、常に14個の余分なIPアドレスが確保されるようになっていたので、かなり余剰分を削ることができた。

前提知識

EKSの各ワーカーノードには、Amazon VPC コンテナネットワークインターフェイス (CNI) プラグインがデフォルトでインストールされている。

このプラグインは aws-node という名前で Daemonset として各ワーカーノードにデプロイされており、ワーカーノード上で動作するPodへプライベートIPアドレスを割当てている。

ワーカーノード(EC2インスタンス)のプライベートIPアドレス

EC2インスタンスのネットワークインタフェース(ENI)には、2種類のプライベートIPアドレスが割り当てられる。

種類 説明 補足
プライマリプライベートIPv4アドレス ENIにデフォルト割り当てられるIPアドレス aws-node と kube-proxy のPodには、プライマリプライベートIPアドレスが割り当てられる(同じIPアドレス)
セカンダリプライベートIPv4アドレス ENIに2つ以上のIPを割り当てたい場合は、セカンダリプライベートIPv4アドレスを付けることになる aws-node と kube-proxy 以外のPodにはセカンダリプライベートIPv4アドレスが割り当てられる

以下では、プライマリプライベートIPv4アドレスを プライマリIPアドレス、セカンダリプライベートIPv4アドレスをセカンダリIPアドレス、特に区別する必要がない場合はIPアドレスと呼ぶ。

ネットワークインターフェースごとに確保できる最大のIPアドレスの数はEC2インスタンスタイプによって決まっている
参照: IP addresses per network interface per instance type

例えば r5.xlarge だと1個のENIで15個のIPアドレスを割り当て可能。ただし、1つはプライマリーIPアドレスで aws-node と kube-proxy に割り当てられるため、その他のPodに利用できるのはセカンダリIPアドレスの14個となる。

aws-node の動作

aws-node がワーカーノードであるEC2インスタンスにENIを割り当てる。

ENIはデフォルトでプライマリIPアドレスが1つ割り当てられるが、追加でセカンダリIPアドレスを割り当てることができる。aws-nodeが設定に従って必要なセカンダリIPアドレスを確保して、Podに割り当てている。

aws-node の設定値

aws-node の設定値のうち、IPアドレスの割当に関する設定項目は以下の3つ。

  • WARM_ENI_TARGET

    • 余分に確保するENIの数。デフォルトは 1
    • ネットワークインターフェース1個につき、EC2インスタンスのスペックごとに定められる最大のIPアドレス数まで確保される。
    • 確保されるIPアドレスのち1つは aws-node に割り当てられるので、その他のPodに割当できるのは 最大数 - 1 になる。
  • WARM_IP_TARGET

    • 余分に確保するセカンダリIPアドレスの数
    • WARM_IP_TARGET' が設定されると WARM_ENI_TARGET` は無視される。
  • MINIMUM_IP_TARGET

    • 最小限、確保するセカンダリIPアドレスの数

WARM_ENI_TARGETによるIP確保の例

デフォルトでは、WARM_ENI_TARGET = 1 が設定されている。ワーカーノードのインスタンスタイプがr5.xlarge だとすると、以下のような動作になる。

  • ワーカーノードには最初にENIが2個(デフォルト1個 + WARN分の1個)割り当てられ、ENI2個分で確保できる最大のIPアドレス30個が確保される。この内、aws-node と kube-proxy 以外のPodに利用できるセカンダリIPアドレスは、 (15 - 1) * 2 の 28個

  • その後は、常に1個分のENIで確保出来るセカンダリIPアドレス(14個)の余剰IPアドレスが存在するように、ENIの割当を追加する。

Podの数 ENIの数 プライマリーIPアドレス数 セカンダリIPアドレス数
1 2 2 28
2 2 2 28
14 2 2 28
15 3 3 37

※ Podの数は、aws-node と kube-proxy を除いた数。

MINIMUM_IP_TARGET と WARM_IP_TARGETによるIP確保の例

MINIMUM_IP_TARGET = 20WARM_IP_TARGET = 5 と設定した場合で、ワーカーノードのインスタンスタイプがr5.xlarge だとすると、以下のような動作になる。

  • 最初に 20個分のセカンダリIPアドレスが確保される。
    20個分のセカンダリIPアドレスを確保するには、2つENIが必要なので、ENIは2つ付く。
  • その後、常に5個の余剰なセカンダリIPアドレスが存在するようにセカンダリIPアドレスの割当が追加される。
Podの数 ENIの数 プライマリーIPアドレス数 セカンダリIPアドレス数
1 2 2 20
2 2 2 20
15 2 2 20
16 2 2 21
23 2 2 28
24 3 3 29

対応前の状況確認

設定値

実際にデプロイされている aws-node の設定

$ kubectl get daemonset aws-node -n kube-system -o jsonpath='{.spec.template.spec.containers[*].env}' | jq .
[
...
  {
    "name": "WARM_ENI_TARGET",
    "value": "1"
  },
  {
    "name": "WARM_PREFIX_TARGET",
    "value": "1"
  }
]

上記のように、デフォルトでは WARM_ENI_TARGET が 1 に設定されており、WARM_IP_TARGETMINIMUM_IP_TARGET は設定されていない。

現象

ワーカーノードのインスタンスタイプは r5.xlargeで、ENI1つごとに15個IPアドレスが確保される。
このうち、セカンダリIPアドレスは、プライマリIPアドレスを引いた14個。

  1. まずENIが2個(デフォルト1個 + WARN分の1個)付いて、IPアドレスが30個確保される。(セカンダリIPアドレスは28個)
  2. ワーカーノードにPodがだいたい20個デプロイされる。(aws-nodeとkube-proxyを除く)
  3. このときPodに割り当てられていないセカンダリIPアドレスの数は、ENI1つで確保できるセカンダリIPアドレスの14個より少なくなる。 ( { セカンダリIPアドレス合計28個 - Pod20個 = 8個} < {ENI1つで確保出るセカンダリIPアドレス数 = 14個}
  4. そこで aws-node がENIを1個追加して、{ENI3個 * 14 = セカンダリIPアドレス42個}を確保する。

この時点で {確保されたセカンダリIPアドレス42個 - デプロイされたPod20個 = 22個} のセカンダリIPアドレスが余剰に確保されてしまっている。

ワーカーノードが10台だと

  • 消費IPアドレス: {ワーカーノード10台 * 確保IPアドレス42個} = 420個
  • 余剰IPアドレス: {ワーカーノード10台 * 余剰IPアドレス24個} = 240個

余剰にIPアドレスを確保しすぎているので、これを減らしたい。

※使われていないプライマリIPアドレスもあるので、実際にはもう少し多い。

暫定対策

WARM_ENI_TARGETによるIP確保から、MINIMUM_IP_TARGET と WARM_IP_TARGETによるIP確保に変更した。

kubectl -n kube-system set env daemonset aws-node WARM_ENI_TARGET-
kubectl -n kube-system set env daemonset aws-node MINIMUM_IP_TARGET=20
kubectl -n kube-system set env daemonset aws-node WARM_IP_TARGET=2

上記のコマンドを実行すると、aws-node が全て再起動し、余分なプライベートIPアドレスがすぐに開放された。

ワーカーノードが10台だと

  • 消費IPアドレス: {ワーカーノード10台 * 確保IPアドレス22個} = 220個
  • 余剰IPアドレス: {ワーカーノード10台 * 余剰IPアドレス2個} = 20個

のように余剰に確保されるIPアドレスも最小限になり、消費IPアドレスはだいたい 半分 になった。

根本対策

そもそもVPCのサブネットで利用可能なプライベートIPアドレスが少ないのが問題。
サブネットを追加するか、EKSクラスターをIPv6対応するか検討したほうがいい。

Discussion