はじめに
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つ。
- 余分に確保するENIの数。デフォルトは 1
- ネットワークインターフェース1個につき、EC2インスタンスのスペックごとに定められる最大のIPアドレス数まで確保される。
- 確保されるIPアドレスのち1つは aws-node に割り当てられるので、その他のPodに割当できるのは 最大数 - 1 になる。
- 余分に確保するセカンダリIPアドレスの数
WARM_IP_TARGET' が設定されると
WARM_ENI_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 = 20
、WARM_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_TARGET
と MINIMUM_IP_TARGET
は設定されていない。
現象
ワーカーノードのインスタンスタイプは r5.xlarge
で、ENI1つごとに15個IPアドレスが確保される。 このうち、セカンダリIPアドレスは、プライマリIPアドレスを引いた14個。
- まずENIが2個(デフォルト1個 + WARN分の1個)付いて、IPアドレスが30個確保される。(セカンダリIPアドレスは28個)
- ワーカーノードにPodがだいたい20個デプロイされる。(aws-nodeとkube-proxyを除く)
- このときPodに割り当てられていないセカンダリIPアドレスの数は、ENI1つで確保できるセカンダリIPアドレスの14個より少なくなる。 (
{ セカンダリIPアドレス合計28個 - Pod20個 = 8個} < {ENI1つで確保出るセカンダリIPアドレス数 = 14個}
) - そこで
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対応するか検討したほうがいい。