本文已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。
Kubernetes 1.27:避免为 NodePort 服务分配端口时发生冲突
在 Kubernetes 中,可以使用 Service 为一组 Pod 上运行的应用程序提供统一的流量端点。客户端可以使用 Service 提供的虚拟 IP 地址 (或VIP) 进行访问,Kubernetes 为访问不同后端 Pod 的流量提供负载均衡,但是 ClusterIP 类型的 Service 仅限于提供对集群内节点的访问,而来自集群外部的流量无法路由。解决此问题的一种方法是使用 type: NodePort
Service,它设置到集群中所有节点的特定端口的映射,从而将来自外部的流量重定向到集群内部。
Kubernetes 如何为 Service 分配节点端口?
当创建 type: NodePort
的 Service 时,其对应的端口会以两种方式之一分配:
动态:如果 Service 类型为
NodePort
,并且您没有在 Service 的spec
中显式设置nodePort
值,Kubernetes 控制平面将在创建时自动为其分配一个未使用的端口。静态:除了上述的动态自动分配之外,您还可以显式分配一个位于 nodeport 端口范围配置内的端口。
您手动分配的 nodePort
值在整个集群中必须是唯一的。尝试创建一个 type: NodePort
的 Service,且您显式指定的节点端口已被分配,会导致错误。
为什么需要预留 NodePort Service 的端口?
有时,您可能希望 NodePort Service 在众所周知的端口上运行,以便集群内部或外部的其他组件和用户可以使用它们。
在一些复杂的集群部署中,Kubernetes 节点和同一网络上的其他服务器混合使用,可能需要使用一些预定义的端口进行通信。特别是,一些基本组件不能依赖于支持 type: LoadBalancer
Service 的 VIP,因为该集群的虚拟 IP 地址映射实现也依赖于这些基础组件。
现在假设您需要在 Kubernetes 上向 Kubernetes 集群外部的客户端公开一个 Minio 对象存储服务,并且约定的端口是 30009
,我们需要创建如下 Service:
apiVersion: v1
kind: Service
metadata:
name: minio
spec:
ports:
- name: api
nodePort: 30009
port: 9000
protocol: TCP
targetPort: 9000
selector:
app: minio
type: NodePort
然而,如前所述,如果 minio
Service 所需的端口 (30009) 未被预留,并且在 minio
Service 之前或同时创建了另一个 type: NodePort
(或可能是 type: LoadBalancer
) Service 并动态分配,则 TCP 端口 30009 可能会被分配给该其他 Service;如果是这样,则由于节点端口冲突,minio
Service 的创建将会失败。
如何避免 NodePort Service 端口冲突?
Kubernetes 1.24 为 type: ClusterIP
Service 引入了更改,将集群 IP 地址的 CIDR 范围划分为两个使用不同分配策略的块,以降低冲突的风险。在 Kubernetes 1.27 中,作为一项 alpha 功能,您可以对 type: NodePort
Service 采用类似的策略。您可以启用新的特性门控 ServiceNodePortStaticSubrange
。启用此功能后,您可以使用不同的端口分配策略来处理 type: NodePort
Service,并降低冲突的风险。
NodePort
的端口范围将根据公式 min(max(16, nodeport-size / 32), 128)
进行划分。公式的结果将是一个介于 16 和 128 之间的数字,其步长随着 nodeport 范围的增大而增加。公式的结果决定了静态端口范围的大小。当端口范围小于 16 时,静态端口范围的大小将设置为 0,这意味着所有端口都将动态分配。
默认情况下,动态端口分配将使用上限范围,一旦耗尽,它将使用下限范围。这将允许用户在下限范围使用静态分配,并且冲突风险较低。
示例
默认范围: 30000-32767
范围属性 | 值 |
---|---|
service-node-port-range | 30000-32767 |
频段偏移 | min(max(16, 2768/32), 128) = min(max(16, 86), 128) = min(86, 128) = 86 |
静态频段起始 | 30000 |
静态频段结束 | 30085 |
动态频段起始 | 30086 |
动态频段结束 | 32767 |
非常小的范围: 30000-30015
范围属性 | 值 |
---|---|
service-node-port-range | 30000-30015 |
频段偏移 | 0 |
静态频段起始 | - |
静态频段结束 | - |
动态频段起始 | 30000 |
动态频段结束 | 30015 |
小(下边界)范围: 30000-30127
范围属性 | 值 |
---|---|
service-node-port-range | 30000-30127 |
频段偏移 | min(max(16, 128/32), 128) = min(max(16, 4), 128) = min(16, 128) = 16 |
静态频段起始 | 30000 |
静态频段结束 | 30015 |
动态频段起始 | 30016 |
动态频段结束 | 30127 |
大(上边界)范围: 30000-34095
范围属性 | 值 |
---|---|
service-node-port-range | 30000-34095 |
频段偏移 | min(max(16, 4096/32), 128) = min(max(16, 128), 128) = min(128, 128) = 128 |
静态频段起始 | 30000 |
静态频段结束 | 30127 |
动态频段起始 | 30128 |
动态频段结束 | 34095 |
非常大的范围: 30000-38191
范围属性 | 值 |
---|---|
service-node-port-range | 30000-38191 |
频段偏移 | min(max(16, 8192/32), 128) = min(max(16, 256), 128) = min(256, 128) = 128 |
静态频段起始 | 30000 |
静态频段结束 | 30127 |
动态频段起始 | 30128 |
动态频段结束 | 38191 |