Service の静的 IP アドレス用のレンジを分割する ServiceIPStaticSubrange
というフィーチャーゲートが、Kubernetes v1.24 でアルファ機能として導入されました。これは、Service の動的 IP アドレスと、静的 IP アドレスの衝突リスクを減らすための機能です。この記事では Kubernetes v1.24.0 時点で、この機能について調べた内容を記載しています。
Kubernetes v1.24 時点ではこの機能はアルファのため、本番環境での利用はベータ以上を待ったほうが良いでしょう。
背景
Service の IP アドレス(Cluster IP) は、動的・静的どちらの割り当ても可能です。通常は、.spec.clusterIP
を省略し、動的な割り当てを使うことが多いと思います。しかし、クラスタ DNS の Service 1のように、静的な割り当てが必要なケースも稀ながら存在します。
動的・静的どちらの割り当ても、API サーバーが管理する Cluster IP のレンジから IP アドレスを選択します。動的な IP アドレスはこのレンジ内からランダムに選択されます 2。そのため、静的な Cluster IP は、動的に割り当てられた Cluster IP とアドレスが衝突する可能性がありました。
クラスタ DNS の場合、他の Service よりも早く作成しておくことで衝突を回避できます。しかし、汎用的な解決策ではなく、Service 再作成時など衝突の可能性が残っていました。
静的な Cluster IP 範囲の分割する (ServiceIPStaticSubrange
)
この問題の解決のため実装されたのが、ServiceIPStaticSubrange
という機能です。Kubernetes v1.24 ではアルファ機能のため、利用するにはフィーチャーゲートで有効にする必要があります。
この機能は、API サーバーの引数 --service-cluster-ip-range
で指定された Cluster IP のレンジを、前半を静的割り当て用の優先レンジ、後半を動的割り当て用の優先レンジとして分割して管理する機能です。静的割り当て用のレンジは CIDR の大きさに応じて 16〜256 個の範囲で分割されます。
例えば、Cluster IP のレンジが 192.168.0.0/16
のとき、以下のように分割されます。
IP アドレス | IP 数 | 用途 |
---|---|---|
192.168.0.0 | 1 | ネットワークアドレス |
192.168.0.1 - 192.168.1.0 | 256 | 静的割り当て優先レンジ |
192.168.1.1 - 192.168.255.254 | 65278 | 動的割り当て優先レンジ |
192.168.255.255 | 1 | ブロードキャストアドレス |
このレンジはあくまで優先して使われるレンジであり、動的割り当て用レンジが枯渇した場合、静的割り当て用レンジからも動的に割り当てられる点にご注意ください。KEP-3070 によると、後方互換性を保つための仕様のようです。この仕様は公式ドキュメント にも記載されています。
Dynamic IP allocations will be preferentially chosen from the upper band, reducing risks of conflicts with the IPs assigned from the lower band.
Kubernetes v1.24.0 の実装では、動的用レンジの枯渇時に IP アドレスが重複するバグがあります。これは Fix ServiceIPStaticSubrange assigns duplicate IP addresses #109928 で修正されています。
CIDR サイズによる静的割り当て用のサイズ
静的割り当て用の優先レンジは、CIDR の大きさに応じて 16〜256 個の範囲で分割されます。この値は以下の計算式で求められます。
min(max(16, cidrSize / 16), 256)
動的割り当て用の優先レンジのサイズは以下で計算で求められます。(引いている 2 はネットワークアドレスとブロードキャストアドレス)
cidrSize - staticSize - 2
CIDR range | 全 IP 数 | static 用 | dynamic 用 |
---|---|---|---|
/26 | 64 | 16 | 46 |
/25 | 128 | 16 | 110 |
/24 | 256 | 16 | 238 |
/23 | 512 | 32 | 478 |
/22 | 1,024 | 64 | 958 |
/21 | 2,048 | 128 | 1,918 |
/20 | 4,096 | 256 | 3,838 |
/19 | 8,192 | 256 | 7,934 |
/18 | 16,384 | 256 | 16,126 |
/17 | 32,768 | 256 | 32,510 |
/16 | 65,536 | 256 | 65,278 |
minikube で ServiceIPStaticSubrange
試す
minikube を使うと、ServiceIPStaticSubrange
を簡単に試すことができます。ここでは、意図的に小さい Cluster IP レンジ (/26
) を指定しています。
minikube start \
--kubernetes-version v1.24.0 \
--extra-config=apiserver.service-cluster-ip-range=10.96.0.0/26 \
--feature-gates=ServiceIPStaticSubrange=true
minikube 直後は、静的割り当てされた kubernetes
3 と、kube-dns
の 2 Service が存在しています。
$ kubectl get svc --all-namespaces
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3m8s
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 3m7s
Service を作成する前にダミーの Pod を作成しておきます。
kubectl create deploy nginx --image nginx --port 80
動的割り当ての Service を作ります。動的割り当て優先レンジの個数ピッタリの 46 個作成します。
for i in $(seq 1 46); do
kubectl expose --name "svc$i" deploy/nginx --port 80
done
作成された Service を IP アドレスでソートして見てみます。動的割り当て優先レンジである 10.96.0.17
〜 10.96.0.62
の範囲で Service (svc*
) が作らていることが確認できます。
$ kubectl get svc --all-namespaces | sort -k4 -V
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3m53s
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 3m52s
default svc43 ClusterIP 10.96.0.17 <none> 80/TCP 16s
default svc40 ClusterIP 10.96.0.18 <none> 80/TCP 16s
default svc11 ClusterIP 10.96.0.19 <none> 80/TCP 18s
default svc10 ClusterIP 10.96.0.20 <none> 80/TCP 18s
#....
default svc7 ClusterIP 10.96.0.60 <none> 80/TCP 18s
default svc29 ClusterIP 10.96.0.61 <none> 80/TCP 17s
default svc28 ClusterIP 10.96.0.62 <none> 80/TCP 17s
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
本来、この次に動的割り当ての Service 作成すると、静的割り当て優先レンジの範囲から IP アドレスが割り当てられます。しかし、前述の通り Kubernetes v1.24.0 の実装では、動的用レンジの枯渇時に IP アドレスが重複するバグ のため正しく動作しませんでした。
ServiceIPStaticSubrange
無効の場合
前項の手順を ServiceIPStaticSubrange
だけ無効にした場合、作成された Service は以下のようになりました。静的割り当ての Service (kubernetes
, kube-dns
) と動的割り当ての Service の IP アドレスが並んで割り当てられていることが確認できます。
$ kubectl get svc --all-namespaces | sort -k4 -V | head
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6m40s
default svc6 ClusterIP 10.96.0.2 <none> 80/TCP 76s
default svc25 ClusterIP 10.96.0.3 <none> 80/TCP 75s
default svc37 ClusterIP 10.96.0.4 <none> 80/TCP 74s
default svc39 ClusterIP 10.96.0.5 <none> 80/TCP 74s
default svc36 ClusterIP 10.96.0.6 <none> 80/TCP 74s
default svc41 ClusterIP 10.96.0.7 <none> 80/TCP 74s
default svc43 ClusterIP 10.96.0.8 <none> 80/TCP 74s
default svc46 ClusterIP 10.96.0.9 <none> 80/TCP 74s
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 6m38s
まとめ
ServiceIPStaticSubrange
機能を使って、Service の静的 IP アドレス用のレンジを分割して管理できることが確認できました。現在はアルファのため本番環境の使用には適しませんが、将来的に静的な IP アドレスの割り当て時の IP アドレスの衝突のリスクを大きく減らすことができそうです。
参考
- https://kubernetes.io/docs/concepts/services-networking/service/#service-ip-static-sub-range
- https://github.com/kubernetes/enhancements/tree/master/keps/sig-network/3070-reserved-service-ip-range
-
クラスタ DNS の IP アドレスは、kubelet の引数
--cluster-dns
に事前に指定しておく必要があるため、静的な IP アドレスが必要です。 ↩ -
現在の動的割り当ての実装は Current Services ClusterIPs allocation model が参考になります。 ↩
-
default
ネームスペースのkubernetes
は Kubernetes API サーバー用の特別な Service です。 ↩