website/content/zh-cn/docs/tasks/network/extend-service-ip-ranges.md

14 KiB
Raw Permalink Blame History

reviewers min-kubernetes-server-version title content_type
thockin
dwinship
v1.29 扩展 Service IP 范围 task

{{< feature-state feature_gate_name="MultiCIDRServiceAllocator" >}}

本文将介绍如何扩展分配给集群的现有 Service IP 范围。

{{% heading "prerequisites" %}}

{{< include "task-tutorial-prereqs.md" >}}

{{< version-check >}}

{{< note >}}

虽然你可以在更早的版本中使用此特性,但此特性只有从 v1.33 版本开始才进阶至 GA正式发布并获得官方支持。 {{< /note >}}

扩展 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 受到 {{<glossary_tooltip text="终结器" term_id="finalizer">}} 的保护, 以避免留下孤立的 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 范围的更改。

{{< note >}}

默认的 "kubernetes" ServiceCIDR 是由 kube-apiserver 创建的,用于在集群中保证一致性, 并且是集群正常运行所必需的,因此必须始终被允许。你可以通过在 ValidatingAdmissionPolicy 中添加以下条件来确保不会限制默认的 ServiceCIDR

  matchConditions:
  - name: 'exclude-default-servicecidr'
    expression: "object.metadata.name != 'kubernetes'"

如下例所示。

{{</ note >}}

限制 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 中列出的所有 CIDRnewCIDR
  # 检查 VAP 的 `allowed` 列表中是否至少存在一个 CIDRallowedCIDR
  # 使 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]