|
| 1 | +--- |
| 2 | +layout: blog |
| 3 | +title: "Kubernetes 1.27:为 NodePort Service 分配端口时避免冲突" |
| 4 | +date: 2023-05-11 |
| 5 | +slug: nodeport-dynamic-and-static-allocation |
| 6 | +--- |
| 7 | +<!-- |
| 8 | +layout: blog |
| 9 | +title: "Kubernetes 1.27: Avoid Collisions Assigning Ports to NodePort Services" |
| 10 | +date: 2023-05-11 |
| 11 | +slug: nodeport-dynamic-and-static-allocation |
| 12 | +--> |
| 13 | + |
| 14 | +<!-- |
| 15 | +**Author:** Xu Zhenglun (Alibaba) |
| 16 | +--> |
| 17 | +**作者:** Xu Zhenglun (Alibaba) |
| 18 | + |
| 19 | +**译者:** [Michael Yao](https://github.com/windsonsea) (DaoCloud) |
| 20 | + |
| 21 | +<!-- |
| 22 | +In Kubernetes, a Service can be used to provide a unified traffic endpoint for |
| 23 | +applications running on a set of Pods. Clients can use the virtual IP address (or _VIP_) provided |
| 24 | +by the Service for access, and Kubernetes provides load balancing for traffic accessing |
| 25 | +different back-end Pods, but a ClusterIP type of Service is limited to providing access to |
| 26 | +nodes within the cluster, while traffic from outside the cluster cannot be routed. |
| 27 | +One way to solve this problem is to use a `type: NodePort` Service, which sets up a mapping |
| 28 | +to a specific port of all nodes in the cluster, thus redirecting traffic from the |
| 29 | +outside to the inside of the cluster. |
| 30 | +--> |
| 31 | +在 Kubernetes 中,对于以一组 Pod 运行的应用,Service 可以为其提供统一的流量端点。 |
| 32 | +客户端可以使用 Service 提供的虚拟 IP 地址(或 **VIP**)进行访问, |
| 33 | +Kubernetes 为访问不同的后端 Pod 的流量提供负载均衡能力, |
| 34 | +但 ClusterIP 类型的 Service 仅限于供集群内的节点来访问, |
| 35 | +而来自集群外的流量无法被路由。解决这个难题的一种方式是使用 `type: NodePort` Service, |
| 36 | +这种服务会在集群所有节点上为特定端口建立映射关系,从而将来自集群外的流量重定向到集群内。 |
| 37 | + |
| 38 | +<!-- |
| 39 | +## How Kubernetes allocates node ports to Services? |
| 40 | +
|
| 41 | +When a `type: NodePort` Service is created, its corresponding port(s) are allocated in one |
| 42 | +of two ways: |
| 43 | +
|
| 44 | +- **Dynamic** : If the Service type is `NodePort` and you do not set a `nodePort` |
| 45 | + value explicitly in the `spec` for that Service, the Kubernetes control plane will |
| 46 | + automatically allocate an unused port to it at creation time. |
| 47 | +
|
| 48 | +- **Static** : In addition to the dynamic auto-assignment described above, you can also |
| 49 | + explicitly assign a port that is within the nodeport port range configuration. |
| 50 | +--> |
| 51 | +## Kubernetes 如何为 Services 分配节点端口? |
| 52 | + |
| 53 | +当 `type: NodePort` Service 被创建时,其所对应的端口将以下述两种方式之一分配: |
| 54 | + |
| 55 | +- **动态分配**:如果 Service 类型是 `NodePort` 且你没有为 Service 显式设置 `nodePort` 值, |
| 56 | + Kubernetes 控制面将在创建时自动为其分配一个未使用的端口。 |
| 57 | + |
| 58 | +- **静态分配**:除了上述动态自动分配,你还可以显式指定 nodeport 端口范围配置内的某端口。 |
| 59 | + |
| 60 | +<!-- |
| 61 | +The value of `nodePort` that you manually assign must be unique across the whole cluster. |
| 62 | +Attempting to create a Service of `type: NodePort` where you explicitly specify a node port that |
| 63 | +was already allocated results in an error. |
| 64 | +--> |
| 65 | +你手动分配的 `nodePort` 值在整个集群范围内一定不能重复。 |
| 66 | +如果尝试在创建 `type: NodePort` Service 时显式指定已分配的节点端口,将产生错误。 |
| 67 | + |
| 68 | +<!-- |
| 69 | +## Why do you need to reserve ports of NodePort Service? |
| 70 | +
|
| 71 | +Sometimes, you may want to have a NodePort Service running on well-known ports |
| 72 | +so that other components and users inside o r outside the cluster can use them. |
| 73 | +--> |
| 74 | +## 为什么需要保留 NodePort Service 的端口? |
| 75 | + |
| 76 | +有时你可能想要 NodePort Service 运行在众所周知的端口上, |
| 77 | +便于集群内外的其他组件和用户可以使用这些端口。 |
| 78 | + |
| 79 | +<!-- |
| 80 | +In some complex cluster deployments with a mix of Kubernetes nodes and other servers on the same network, |
| 81 | +it may be necessary to use some pre-defined ports for communication. In particular, some fundamental |
| 82 | +components cannot rely on the VIPs that back `type: LoadBalancer` Services |
| 83 | +because the virtual IP address mapping implementation for that cluster also relies on |
| 84 | +these foundational components. |
| 85 | +
|
| 86 | +Now suppose you need to expose a Minio object storage service on Kubernetes to clients |
| 87 | +running outside the Kubernetes cluster, and the agreed port is `30009`, we need to |
| 88 | +create a Service as follows: |
| 89 | +--> |
| 90 | +在某些复杂的集群部署场景中在同一网络上混合了 Kubernetes 节点和其他服务器, |
| 91 | +可能有必要使用某些预定义的端口进行通信。尤为特别的是,某些基础组件无法使用用来支撑 |
| 92 | +`type: LoadBalancer` Service 的 VIP,因为针对集群实现的虚拟 IP 地址映射也依赖这些基础组件。 |
| 93 | + |
| 94 | +现在假设你需要在 Kubernetes 上将一个 Minio 对象存储服务暴露给运行在 Kubernetes 集群外的客户端, |
| 95 | +协商后的端口是 `30009`,我们需要创建以下 Service: |
| 96 | + |
| 97 | +```yaml |
| 98 | +apiVersion: v1 |
| 99 | +kind: Service |
| 100 | +metadata: |
| 101 | + name: minio |
| 102 | +spec: |
| 103 | + ports: |
| 104 | + - name: api |
| 105 | + nodePort: 30009 |
| 106 | + port: 9000 |
| 107 | + protocol: TCP |
| 108 | + targetPort: 9000 |
| 109 | + selector: |
| 110 | + app: minio |
| 111 | + type: NodePort |
| 112 | +``` |
| 113 | +
|
| 114 | +<!-- |
| 115 | +However, as mentioned before, if the port (30009) required for the `minio` Service is not reserved, |
| 116 | +and another `type: NodePort` (or possibly `type: LoadBalancer`) Service is created and dynamically |
| 117 | +allocated before or concurrently with the `minio` Service, TCP port 30009 might be allocated to that |
| 118 | +other Service; if so, creation of the `minio` Service will fail due to a node port collision. |
| 119 | +--> |
| 120 | +然而如前文所述,如果 `minio` Service 所需的端口 (30009) 未被预留, |
| 121 | +且另一个 `type: NodePort`(或者也包括 `type: LoadBalancer`)Service |
| 122 | +在 `minio` Service 之前或与之同时被创建、动态分配,TCP 端口 30009 可能被分配给了这个 Service; |
| 123 | +如果出现这种情况,`minio` Service 的创建将由于节点端口冲突而失败。 |
| 124 | + |
| 125 | +<!-- |
| 126 | +## How can you avoid NodePort Service port conflicts? |
| 127 | +Kubernetes 1.24 introduced changes for `type: ClusterIP` Services, dividing the CIDR range for cluster |
| 128 | +IP addresses into two blocks that use different allocation policies to [reduce the risk of conflicts](/docs/reference/networking/virtual-ips/#avoiding-collisions). |
| 129 | +In Kubernetes 1.27, as an alpha feature, you can adopt a similar policy for `type: NodePort` Services. |
| 130 | +You can enable a new [feature gate](/docs/reference/command-line-tools-reference/feature-gates/) |
| 131 | +`ServiceNodePortStaticSubrange`. Turning this on allows you to use a different port allocation strategy |
| 132 | +for `type: NodePort` Services, and reduce the risk of collision. |
| 133 | +--> |
| 134 | +## 如何才能避免 NodePort Service 端口冲突? |
| 135 | + |
| 136 | +Kubernetes 1.24 引入了针对 `type: ClusterIP` Service 的变更,将集群 IP 地址的 CIDR |
| 137 | +范围划分为使用不同分配策略的两块来[减少冲突的风险](/zh-cn/docs/reference/networking/virtual-ips/#avoiding-collisions)。 |
| 138 | +在 Kubernetes 1.27 中,作为一个 Alpha 特性,你可以为 `type: NodePort` Service 采用类似的策略。 |
| 139 | +你可以启用新的[特性门控](/zh-cn/docs/reference/command-line-tools-reference/feature-gates/) |
| 140 | +`ServiceNodePortStaticSubrange`。开启此门控将允许你为 |
| 141 | +`type: NodePort` Service 使用不同的端口分配策略,减少冲突的风险。 |
| 142 | + |
| 143 | +<!-- |
| 144 | +The port range for `NodePort` will be divided, based on the formula `min(max(16, nodeport-size / 32), 128)`. |
| 145 | +The outcome of the formula will be a number between 16 and 128, with a step size that increases as the |
| 146 | +size of the nodeport range increases. The outcome of the formula determine that the size of static port |
| 147 | +range. When the port range is less than 16, the size of static port range will be set to 0, |
| 148 | +which means that all ports will be dynamically allocated. |
| 149 | + |
| 150 | +Dynamic port assignment will use the upper band by default, once this has been exhausted it will use the lower range. |
| 151 | +This will allow users to use static allocations on the lower band with a low risk of collision. |
| 152 | +--> |
| 153 | +`NodePort` 的端口范围将基于公式 `min(max(16, 节点端口数 / 32), 128)` 进行划分。 |
| 154 | +这个公式的结果将是一个介于 16 到 128 的数字,随着节点端口范围变大,步进值也会变大。 |
| 155 | +此公式的结果决定了静态端口范围的大小。当端口范围小于 16 时,静态端口范围的大小将被设为 0, |
| 156 | +这意味着所有端口都将被动态分配。 |
| 157 | + |
| 158 | +动态端口分配默认使用数值较高的一段,一旦用完,它将使用较低范围。 |
| 159 | +这将允许用户在冲突风险较低的较低端口段上使用静态分配。 |
| 160 | + |
| 161 | +<!-- |
| 162 | +## Examples |
| 163 | + |
| 164 | +### default range: 30000-32767 |
| 165 | +| Range properties | Values | |
| 166 | +|-------------------------|-------------------------------------------------------| |
| 167 | +| service-node-port-range | 30000-32767 | |
| 168 | +| Band Offset |   `min(max(16, 2768/32), 128)` <br>= `min(max(16, 86), 128)` <br>= `min(86, 128)` <br>= 86 | |
| 169 | +| Static band start | 30000 | |
| 170 | +| Static band end | 30085 | |
| 171 | +| Dynamic band start | 30086 | |
| 172 | +| Dynamic band end | 32767 | |
| 173 | +--> |
| 174 | +## 示例 |
| 175 | + |
| 176 | +### 默认范围:30000-32767 |
| 177 | + |
| 178 | +| 范围属性 | 值 | |
| 179 | +| ----------------------- | ----------------------------------------------------------------------------------------------- | |
| 180 | +| service-node-port-range | 30000-32767 | |
| 181 | +| 分段偏移量 |   `min(max(16, 2768/32), 128)` <br>= `min(max(16, 86), 128)` <br>= `min(86, 128)` <br>= 86 | |
| 182 | +| 起始静态段 | 30000 | |
| 183 | +| 结束静态段 | 30085 | |
| 184 | +| 起始动态段 | 30086 | |
| 185 | +| 结束动态段 | 32767 | |
| 186 | + |
| 187 | +{{< mermaid >}} |
| 188 | +pie showData |
| 189 | + title 30000-32767 |
| 190 | + "Static" : 86 |
| 191 | + "Dynamic" : 2682 |
| 192 | +{{< /mermaid >}} |
| 193 | + |
| 194 | +<!-- |
| 195 | +### very small range: 30000-30015 |
| 196 | +| Range properties | Values | |
| 197 | +|-------------------------|-------------------------------------------------------| |
| 198 | +| service-node-port-range | 30000-30015 | |
| 199 | +| Band Offset | 0 | |
| 200 | +| Static band start | - | |
| 201 | +| Static band end | - | |
| 202 | +| Dynamic band start | 30000 | |
| 203 | +| Dynamic band end | 30015 | |
| 204 | +--> |
| 205 | +### 超小范围:30000-30015 |
| 206 | + |
| 207 | +| 范围属性 | 值 | |
| 208 | +| ----------------------- | ----------- | |
| 209 | +| service-node-port-range | 30000-30015 | |
| 210 | +| 分段偏移量 | 0 | |
| 211 | +| 起始静态段 | - | |
| 212 | +| 结束静态段 | - | |
| 213 | +| 起始动态段 | 30000 | |
| 214 | +| 动态动态段 | 30015 | |
| 215 | + |
| 216 | +{{< mermaid >}} |
| 217 | +pie showData |
| 218 | + title 30000-30015 |
| 219 | + "Static" : 0 |
| 220 | + "Dynamic" : 16 |
| 221 | +{{< /mermaid >}} |
| 222 | + |
| 223 | +<!-- |
| 224 | +### small(lower boundary) range: 30000-30127 |
| 225 | +| Range properties | Values | |
| 226 | +|-------------------------|-------------------------------------------------------| |
| 227 | +| service-node-port-range | 30000-30127 | |
| 228 | +| Band Offset |   `min(max(16, 128/32), 128)` <br>= `min(max(16, 4), 128)` <br>= `min(16, 128)` <br>= 16 | |
| 229 | +| Static band start | 30000 | |
| 230 | +| Static band end | 30015 | |
| 231 | +| Dynamic band start | 30016 | |
| 232 | +| Dynamic band end | 30127 | |
| 233 | +--> |
| 234 | +### 小(下边界)范围:30000-30127 |
| 235 | + |
| 236 | +| 范围属性 | 值 | |
| 237 | +| ---------------------- | --------------------------------------------------------------------------------------------- | |
| 238 | +| service-node-port-range | 30000-30127 | |
| 239 | +| 分段偏移量 |   `min(max(16, 128/32), 128)` <br>= `min(max(16, 4), 128)` <br>= `min(16, 128)` <br>= 16 | |
| 240 | +| 起始静态段 | 30000 | |
| 241 | +| 结束静态段 | 30015 | |
| 242 | +| 起始动态段 | 30016 | |
| 243 | +| 结束动态段 | 30127 | |
| 244 | + |
| 245 | +{{< mermaid >}} |
| 246 | +pie showData |
| 247 | + title 30000-30127 |
| 248 | + "Static" : 16 |
| 249 | + "Dynamic" : 112 |
| 250 | +{{< /mermaid >}} |
| 251 | + |
| 252 | +<!-- |
| 253 | +### large(upper boundary) range: 30000-34095 |
| 254 | +| Range properties | Values | |
| 255 | +|-------------------------|-------------------------------------------------------| |
| 256 | +| service-node-port-range | 30000-34095 | |
| 257 | +| Band Offset |   `min(max(16, 4096/32), 128)` <br>= `min(max(16, 128), 128)` <br>= `min(128, 128)` <br>= 128 | |
| 258 | +| Static band start | 30000 | |
| 259 | +| Static band end | 30127 | |
| 260 | +| Dynamic band start | 30128 | |
| 261 | +| Dynamic band end | 34095 | |
| 262 | +--> |
| 263 | +### 大(上边界)范围:30000-34095 |
| 264 | + |
| 265 | +| 范围属性 | 值 | |
| 266 | +| -----------------------| -------------------------------------------------------------------------------------------------- | |
| 267 | +| service-node-port-range | 30000-34095 | |
| 268 | +| 分段偏移量 |   `min(max(16, 4096/32), 128)` <br>= `min(max(16, 128), 128)` <br>= `min(128, 128)` <br>= 128 | |
| 269 | +| 起始静态段 | 30000 | |
| 270 | +| 结束静态段 | 30127 | |
| 271 | +| 起始动态段 | 30128 | |
| 272 | +| 结束动态段 | 34095 | |
| 273 | + |
| 274 | +{{< mermaid >}} |
| 275 | +pie showData |
| 276 | + title 30000-34095 |
| 277 | + "Static" : 128 |
| 278 | + "Dynamic" : 3968 |
| 279 | +{{< /mermaid >}} |
| 280 | + |
| 281 | +<!-- |
| 282 | +### very large range: 30000-38191 |
| 283 | +| Range properties | Values | |
| 284 | +|-------------------------|-------------------------------------------------------| |
| 285 | +| service-node-port-range | 30000-38191 | |
| 286 | +| Band Offset |   `min(max(16, 8192/32), 128)` <br>= `min(max(16, 256), 128)` <br>= `min(256, 128)` <br>= 128 | |
| 287 | +| Static band start | 30000 | |
| 288 | +| Static band end | 30127 | |
| 289 | +| Dynamic band start | 30128 | |
| 290 | +| Dynamic band end | 38191 | |
| 291 | +--> |
| 292 | +### 超大范围:30000-38191 |
| 293 | + |
| 294 | +| 范围属性 | 值 | |
| 295 | +| ---------------------- | -------------------------------------------------------------------------------------------------- | |
| 296 | +| service-node-port-range | 30000-38191 | |
| 297 | +| 分段偏移量 |   `min(max(16, 8192/32), 128)` <br>= `min(max(16, 256), 128)` <br>= `min(256, 128)` <br>= 128 | |
| 298 | +| 起始静态段 | 30000 | |
| 299 | +| 结束静态段 | 30127 | |
| 300 | +| 起始动态段 | 30128 | |
| 301 | +| 结束动态段 | 38191 | |
| 302 | + |
| 303 | +{{< mermaid >}} |
| 304 | +pie showData |
| 305 | + title 30000-38191 |
| 306 | + "Static" : 128 |
| 307 | + "Dynamic" : 8064 |
| 308 | +{{< /mermaid >}} |
0 commit comments