扩展 Service IP 范围
Kubernetes v1.33 [stable]
(enabled by default: true)
This is a stable feature in Kubernetes, and has been since version 1.33. It was first available in the v1.27 release.
本文将介绍如何扩展分配给集群的现有 Service IP 范围。
准备开始
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
你的 Kubernetes 服务器版本必须不低于版本 v1.29.要获知版本信息,请输入 kubectl version
.
说明:
虽然你可以在更早的版本中使用此特性,但此特性只有从 v1.33 版本开始才进阶至 GA(正式发布)并获得官方支持。
扩展 Service IP 范围
如果 Kubernetes 集群的 kube-apiserver 启用了 MultiCIDRServiceAllocator
特性门控且激活了
networking.k8s.io/v1beta1
API 组,集群将创建一个新的 ServiceCIDR 对象,
该对象采用 kubernetes
这个众所周知的名称并基于 kube-apiserver 的 --service-cluster-ip-range
命令行参数的值来使用 IP 地址范围。
kubectl get servicecidr
NAME CIDRS AGE
kubernetes 10.96.0.0/28 17d
公认的 kubernetes
Service 将 kube-apiserver 的端点暴露给 Pod,
计算出默认 ServiceCIDR 范围中的第一个 IP 地址,并将该 IP 地址用作其集群 IP 地址。
kubectl get service kubernetes
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 17d
在本例中,默认 Service 使用具有对应 IPAddress 对象的 ClusterIP 10.96.0.1。
kubectl get ipaddress 10.96.0.1
NAME PARENTREF
10.96.0.1 services/default/kubernetes
ServiceCIDR 受到 终结器 的保护, 以避免留下孤立的 Service ClusterIP;只有在存在包含现有 IPAddress 的另一个子网或者没有属于此子网的 IPAddress 时,才会移除终结器。
扩展 Service 可用的 IP 数量
有时候用户需要增加可供 Service 使用的 IP 地址数量。 以前,增加 Service 范围是一个可能导致数据丢失的破坏性操作。 有了这个新的特性后,用户只需添加一个新的 ServiceCIDR 对象,便能增加可用地址的数量。
添加新的 ServiceCIDR
对于 Service 范围为 10.96.0.0/28 的集群,只有 2^(32-28) - 2 = 14 个可用的 IP 地址。
kubernetes.default
Service 始终会被创建;在这个例子中,你只剩下了 13 个可能的 Service。
for i in $(seq 1 13); do kubectl create service clusterip "test-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done
10.96.0.11
10.96.0.5
10.96.0.12
10.96.0.13
10.96.0.14
10.96.0.2
10.96.0.3
10.96.0.4
10.96.0.6
10.96.0.7
10.96.0.8
10.96.0.9
error: failed to create ClusterIP service: Internal error occurred: failed to allocate a serviceIP: range is full
通过创建一个扩展或新增 IP 地址范围的新 ServiceCIDR,你可以提高 Service 可用的 IP 地址数量。
cat <EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1beta1
kind: ServiceCIDR
metadata:
name: newcidr1
spec:
cidrs:
- 10.96.0.0/24
EOF
servicecidr.networking.k8s.io/newcidr1 created
这将允许你创建新的 Service,其 ClusterIP 将从这个新的范围中选取。
for i in $(seq 13 16); do kubectl create service clusterip "test-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done
10.96.0.48
10.96.0.200
10.96.0.121
10.96.0.144
删除 ServiceCIDR
如果存在依赖于 ServiceCIDR 的 IPAddress,你将无法删除 ServiceCIDR。
kubectl delete servicecidr newcidr1
servicecidr.networking.k8s.io "newcidr1" deleted
Kubernetes 在 ServiceCIDR 上使用一个终结器来跟踪这种依赖关系。
kubectl get servicecidr newcidr1 -o yaml
apiVersion: networking.k8s.io/v1beta1
kind: ServiceCIDR
metadata:
creationTimestamp: "2023-10-12T15:11:07Z"
deletionGracePeriodSeconds: 0
deletionTimestamp: "2023-10-12T15:12:45Z"
finalizers:
- networking.k8s.io/service-cidr-finalizer
name: newcidr1
resourceVersion: "1133"
uid: 5ffd8afe-c78f-4e60-ae76-cec448a8af40
spec:
cidrs:
- 10.96.0.0/24
status:
conditions:
- lastTransitionTime: "2023-10-12T15:12:45Z"
message:
There are still IPAddresses referencing the ServiceCIDR, please remove
them or create a new ServiceCIDR
reason: OrphanIPAddress
status: "False"
type: Ready
移除一些 Service,这些 Service 包含阻止删除 ServiceCIDR 的 IP 地址:
for i in $(seq 13 16); do kubectl delete service "test-$i" ; done
service "test-13" deleted
service "test-14" deleted
service "test-15" deleted
service "test-16" deleted
控制平面会注意到这种移除操作。控制平面随后会移除其终结器,以便真正移除待删除的 ServiceCIDR。
kubectl get servicecidr newcidr1
Error from server (NotFound): servicecidrs.networking.k8s.io "newcidr1" not found
Kubernetes Service CIDR 策略
集群管理员可以实现策略来控制集群中 ServiceCIDR 资源的创建和修改。 这允许集中管理 Service 所使用的 IP 地址范围,有助于防止意外或冲突的配置。 Kubernetes 提供如验证准入策略(Validating Admission Policy)等机制来强制执行这些规则。
使用验证准入策略阻止未授权的 ServiceCIDR 创建或更新
在某些情况下,集群管理员可能希望限制允许的 IP 范围,或完全禁止对集群 Service IP 范围的更改。
说明:
默认的 "kubernetes" ServiceCIDR 是由 kube-apiserver 创建的,用于在集群中保证一致性,
并且是集群正常运行所必需的,因此必须始终被允许。你可以通过在 ValidatingAdmissionPolicy
中添加以下条件来确保不会限制默认的 ServiceCIDR:
matchConditions:
- name: 'exclude-default-servicecidr'
expression: "object.metadata.name != 'kubernetes'"
如下例所示。
限制 Service CIDR 范围为某些特定范围
以下是一个 ValidatingAdmissionPolicy
的示例,它只允许在给定的 allowed
范围内的子范围创建 ServiceCIDR。
(因此示例的策略允许 ServiceCIDR 使用 cidrs: ['10.96.1.0/24']
或
cidrs: ['2001:db8:0:0:ffff::/80', '10.96.0.0/20']
,但不允许 cidrs: ['172.20.0.0/16']
。)
你可以复制此策略,并将 allowed
的值更改为适合你集群的取值。
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: "servicecidrs.default"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["networking.k8s.io"]
apiVersions: ["v1","v1beta1"]
operations: ["CREATE", "UPDATE"]
resources: ["servicecidrs"]
matchConditions:
- name: 'exclude-default-servicecidr'
expression: "object.metadata.name != 'kubernetes'"
variables:
- name: allowed
expression: "['10.96.0.0/16','2001:db8::/64']"
validations:
- expression: "object.spec.cidrs.all(newCIDR, variables.allowed.exists(allowedCIDR, cidr(allowedCIDR).containsCIDR(newCIDR)))"
# 对提交的 ServiceCIDR 对象的 spec.cidrs 中列出的所有 CIDR(newCIDR),
# 检查 VAP 的 `allowed` 列表中是否至少存在一个 CIDR(allowedCIDR),
# 使 allowedCIDR 完全包含 newCIDR。
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "servicecidrs-binding"
spec:
policyName: "servicecidrs.default"
validationActions: [Deny,Audit]
如果你想要编写自己的验证 expression
,参阅 CEL 文档以了解更多信息。
限制任何对 ServiceCIDR API 的使用
以下示例展示了如何使用 ValidatingAdmissionPolicy
及其绑定,
来限制创建任何新的 Service CIDR 范围,但不包括默认的 "kubernetes" ServiceCIDR:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: "servicecidrs.deny"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["networking.k8s.io"]
apiVersions: ["v1","v1beta1"]
operations: ["CREATE", "UPDATE"]
resources: ["servicecidrs"]
validations:
- expression: "object.metadata.name == 'kubernetes'"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "servicecidrs-deny-binding"
spec:
policyName: "servicecidrs.deny"
validationActions: [Deny,Audit]