diff --git a/content/zh-cn/docs/reference/networking/virtual-ips.md b/content/zh-cn/docs/reference/networking/virtual-ips.md index 9e70fe2d8b..04a3a55656 100644 --- a/content/zh-cn/docs/reference/networking/virtual-ips.md +++ b/content/zh-cn/docs/reference/networking/virtual-ips.md @@ -4,47 +4,44 @@ content_type: reference weight: 50 --- - +--> - - +--> Kubernetes 集群中的每个{{< glossary_tooltip term_id="node" text="节点" >}}会运行一个 [kube-proxy](/zh-cn/docs/reference/command-line-tools-reference/kube-proxy/) (除非你已经部署了自己的替换组件来替代 `kube-proxy`)。 - - +--> `kube-proxy` 组件负责除 `type` 为 [`ExternalName`](/zh-cn/docs/concepts/services-networking/service/#externalname) 以外的{{< glossary_tooltip term_id="service" text="服务">}},实现**虚拟 IP** 机制。 - - +--> 一个时不时出现的问题是,为什么 Kubernetes 依赖代理将入站流量转发到后端。 其他方案呢?例如,是否可以配置具有多个 A 值(或 IPv6 的 AAAA)的 DNS 记录, 使用轮询域名解析? - - +--> 使用代理转发方式实现 Service 的原因有以下几个: * DNS 的实现不遵守记录的 TTL 约定的历史由来已久,在记录过期后可能仍有结果缓存。 @@ -62,7 +58,7 @@ There are a few reasons for using proxying for Services: * 即使应用程序和库进行了适当的重新解析,TTL 取值较低或为零的 DNS 记录可能会给 DNS 带来很大的压力, 从而变得难以管理。 - +--> 在下文中,你可以了解到 kube-proxy 各种实现方式的工作原理。 总的来说,你应该注意到,在运行 `kube-proxy` 时, 可能会修改内核级别的规则(例如,可能会创建 iptables 规则), @@ -79,15 +75,14 @@ to use as-is. 因此,运行 kube-proxy 这件事应该只由了解在计算机上使用低级别、特权网络代理服务会带来的后果的管理员执行。 尽管 `kube-proxy` 可执行文件支持 `cleanup` 功能,但这个功能并不是官方特性,因此只能根据具体情况使用。 - - +--> 本文中的一些细节会引用这样一个例子: 运行了 3 个 Pod 副本的无状态图像处理后端工作负载。 @@ -95,15 +90,14 @@ nor should they need to keep track of the set of backends themselves. 即使组成这一组后端程序的 Pod 实际上可能会发生变化, 前端客户端不应该也没必要知道,而且也不需要跟踪这一组后端的状态。 - - -## 代理模式{#proxy-modes} +--> +## 代理模式 {#proxy-modes} - - +--> 注意,kube-proxy 会根据不同配置以不同的模式启动。 - kube-proxy 的配置是通过 ConfigMap 完成的,kube-proxy 的 ConfigMap 实际上弃用了 kube-proxy 大部分标志的行为。 @@ -125,240 +118,270 @@ Note that the kube-proxy starts up in different modes, which are determined by i 例如,如果你的操作系统不允许你运行 iptables 命令,标准的 kube-proxy 内核实现将无法工作。 同样,如果你的操作系统不支持 `netsh`,它也无法在 Windows 用户空间模式下运行。 - -### 用户空间代理模式 {#proxy-mode-userspace} - -{{< feature-state for_k8s_version="v1.23" state="deprecated" >}} - - - -这种(遗留)模式使用 iptables 添加拦截规则,然后使用 kube-proxy 工具执行流量转发。 -kube-proxy 监视 Kubernetes 控制平面对 Service 和 EndpointSlice 对象的增加、修改和删除。 -对于每个 Service,kube-proxy 在本地节点上打开一个端口(随机选择)。 -任何对这个**代理端口**的连接都将代理到 Service 的一个后端 Pod(通过 EndpointSlices 报告)。 -kube-proxy 在决定使用哪个后端 Pod 时会考虑 Service 的 `sessionAffinity` 设置。 - - -用户空间代理添加 iptables 规则,这些规则捕获流向 Service 的 `clusterIP`(虚拟 IP)和 `port` 的流量。 -这些规则将这些流量重定向到代理后端 Pod 的代理端口。 - - -默认情况下,用户空间模式下的 kube-proxy 通过轮询算法选择后端。 - -{{< figure src="/images/docs/services-userspace-overview.svg" title="用户空间代理的 Service 概览" class="diagram-medium" >}} - - -#### 示例 {#packet-processing-userspace} - - -例如,考虑本文[前面](#example)描述的图像处理应用的例子。 -当创建后端 Service 时,Kubernetes 控制平面分配一个虚拟 IP 地址,例如 10.0.0.1。 -假设 Service 端口是 1234,那么集群中的所有 kube-proxy 实例都会观察到该 Service。 -当一个 kube-proxy 观察到新 Service 时,它会随机打开一个新端口, -建立从虚拟 IP 地址到这个新端口的 iptables 重定向,并开始在其上接受连接。 - - -当客户端连接到 Service 的虚拟 IP 地址时,iptables 规则会生效,将数据包重定向到代理自身的端口。 -“Service 代理” 选择一个后端,并开始代理客户端到后端的流量。 - - -这意味着 Service 所有者可以选择任何他们想要的端口而不会发生冲突。 -客户端可以连接到 IP 和端口,也不需要知道它们实际访问的是哪些 Pod。 - - -#### 扩缩容挑战 {#scaling-challenges-userspace} - - - -在中小型规模集群中使用用户空间代理的 VIP 是有效的,但是不能拓展到具有数千个 Service 的大型集群。 -[针对门户的初始设计提案](https://github.com/kubernetes/kubernetes/issues/1107) -中有更多的细节。 - - -使用用户空间代理会隐藏访问 Service 的数据包的源 IP 地址。 -这使得某些类型的网络过滤(防火墙)失效。 -iptables 代理模式不会隐藏集群内的源 IP 地址, -但仍会隐藏通过负载均衡器或节点端口进入的客户端数据包源 IP 地址。 - - - ### `iptables` 代理模式 {#proxy-mode-iptables} +--> +### `iptables` 代理模式 {#proxy-mode-iptables} - +--> 在这种模式下,kube-proxy 监视 Kubernetes 控制平面,获知对 Service 和 EndpointSlice 对象的添加和删除操作。 对于每个 Service,kube-proxy 会添加 iptables 规则,这些规则捕获流向 Service 的 `clusterIP` 和 `port` 的流量, 并将这些流量重定向到 Service 后端集合中的其中之一。 对于每个端点,它会添加指向一个特定后端 Pod 的 iptables 规则。 - +--> 默认情况下,iptables 模式下的 kube-proxy 会随机选择一个后端。 使用 iptables 处理流量的系统开销较低,因为流量由 Linux netfilter 处理, 无需在用户空间和内核空间之间切换。这种方案也更为可靠。 - - +--> 如果 kube-proxy 以 iptables 模式运行,并且它选择的第一个 Pod 没有响应, 那么连接会失败。这与用户空间模式不同: 在后者这种情况下,kube-proxy 会检测到与第一个 Pod 的连接失败, 并会自动用不同的后端 Pod 重试。 - - +--> 你可以使用 Pod [就绪探针](/zh-cn/docs/concepts/workloads/pods/pod-lifecycle/#container-probes)来验证后端 Pod 是否健康。 这样可以避免 kube-proxy 将流量发送到已知失败的 Pod 上。 - -{{< figure src="/images/docs/services-iptables-overview.svg" title="iptables 代理的 Service 概览" class="diagram-medium" >}} + +{{< figure src="/images/docs/services-iptables-overview.svg" title="iptables 模式下 Service 的虚拟 IP 机制" class="diagram-medium" >}} - +--> #### 示例 {#packet-processing-iptables} - -仍以[前面](#example)描述的图像处理应用程序为例。 +--> +例如,考虑本页中[前面](#example)描述的图像处理应用程序。 当创建后端 Service 时,Kubernetes 控制平面会分配一个虚拟 IP 地址,例如 10.0.0.1。 对于这个例子而言,假设 Service 端口是 1234。 集群中的所有 kube-proxy 实例都会观察到新 Service 的创建。 - +--> 当节点上的 kube-proxy 观察到新 Service 时,它会添加一系列 iptables 规则, 这些规则从虚拟 IP 地址重定向到更多 iptables 规则,每个 Service 都定义了这些规则。 每个 Service 规则链接到每个后端端点的更多规则, 并且每个端点规则将流量重定向(使用目标 NAT)到后端。 - +redirected to the backend without rewriting the client IP address. +--> 当客户端连接到 Service 的虚拟 IP 地址时,iptables 规则会生效。 -会选择一个后端(基于会话亲和性或随机选择),并将数据包重定向到后端。 -与用户空间代理不同,数据包不会被复制到用户空间, -不需要 kube-proxy 参与,虚拟 IP 地址就可以正常工作, -节点可以看到来自未更改的客户端 IP 地址的流量。 +会选择一个后端(基于会话亲和性或随机选择),并将数据包重定向到后端,无需重写客户端 IP 地址。 - +--> 当流量通过节点端口或负载均衡器进入时,也会执行相同的基本流程, 只是在这些情况下,客户端 IP 地址会被更改。 - +#### 优化 iptables 模式性能 {#optimizing-iptables-mode-performance} + +在大型集群(有数万个 Pod 和 Service)中,当 Service(或其 EndpointSlices)发生变化时 +iptables 模式的 kube-proxy 在更新内核中的规则时可能要用较长时间。 +你可以通过(`kube-proxy --config ` 指定的)kube-proxy +[配置文件](/zh-cn/docs/reference/config-api/kube-proxy-config.v1alpha1/)的 +[`iptables` 节](/zh-cn/docs/reference/config-api/kube-proxy-config.v1alpha1/#kubeproxy-config-k8s-io-v1alpha1-KubeProxyIPTablesConfiguration)中的选项来调整 +kube-proxy 的同步行为: + +```none +... +iptables: + minSyncPeriod: 1s + syncPeriod: 30s +... +``` + +##### `minSyncPeriod` + + +`minSyncPeriod` 参数设置尝试同步 iptables 规则与内核之间的最短时长。 +如果是 `0s`,那么每次有任一 Service 或 Endpoint 发生变更时,kube-proxy 都会立即同步这些规则。 +这种方式在较小的集群中可以工作得很好,但如果在很短的时间内很多东西发生变更时,它会导致大量冗余工作。 +例如,如果你有一个由 Deployment 支持的 Service,共有 100 个 Pod,你删除了这个 Deployment, +且设置了 `minSyncPeriod: 0s`,kube-proxy 最终会从 iptables 规则中逐个删除 Service 的 Endpoint, +总共更新 100 次。使用较大的 `minSyncPeriod` 值时,多个 Pod 删除事件将被聚合在一起, +因此 kube-proxy 最终可能会进行例如 5 次更新,每次移除 20 个端点, +这样在 CPU 利用率方面更有效率,能够更快地同步所有变更。 + + +`minSyncPeriod` 的值越大,可以聚合的工作越多, +但缺点是每个独立的变更可能最终要等待整个 `minSyncPeriod` 周期后才能被处理, +这意味着 iptables 规则要用更多时间才能与当前的 apiserver 状态同步。 + + +默认值 `1s` 对于中小型集群是一个很好的折衷方案。 +在大型集群中,可能需要将其设置为更大的值。 +(特别是,如果 kube-proxy 的 `sync_proxy_rules_duration_seconds` 指标表明平均时间远大于 1 秒, +那么提高 `minSyncPeriod` 可能会使更新更有效率。) + +##### `syncPeriod` + + +`syncPeriod` 参数控制与单次 Service 和 Endpoint 的变更没有直接关系的少数同步操作。 +特别是,它控制 kube-proxy 在外部组件已干涉 kube-proxy 的 iptables 规则时通知的速度。 +在大型集群中,kube-proxy 也仅在每隔 `syncPeriod` 时长执行某些清理操作,以避免不必要的工作。 + + +在大多数情况下,提高 `syncPeriod` 预计不会对性能产生太大影响, +但在过去,有时将其设置为非常大的值(例如 `1h`)很有用。 +现在不再推荐这种做法,因为它对功能的破坏可能会超过对性能的改进。 + + +##### 实验性的性能改进 {#minimize-iptables-restore} + +{{< feature-state for_k8s_version="v1.26" state="alpha" >}} + + +在 Kubernetes 1.26 中,社区对 iptables 代理模式进行了一些新的性能改进, +但默认未启用(并且可能还不应该在生产集群中启用)。要试用它们, +请使用 `--feature-gates=MinimizeIPTablesRestore=true,…` 为 kube-proxy 启用 `MinimizeIPTablesRestore` +[特性门控](/zh-cn/docs/reference/command-line-tools-reference/feature-gates/)。 + + +如果你启用该特性门控并且之前覆盖了 `minSyncPeriod`, +你应该尝试移除该覆盖并让 kube-proxy 使用默认值 (`1s`) 或至少使用比之前更小的值。 + + +如果你注意到 kube-proxy 的 `sync_proxy_rules_iptables_restore_failures_total` 或 +`sync_proxy_rules_iptables_partial_restore_failures_total` 指标在启用此特性后升高, +这可能表明你发现了该特性的错误,你应该提交错误报告。 + + +--> ### IPVS 代理模式 {#proxy-mode-ipvs} - +When accessing a Service, IPVS directs traffic to one of the backend Pods. +--> 在 `ipvs` 模式下,kube-proxy 监视 Kubernetes Service 和 EndpointSlice, 然后调用 `netlink` 接口创建 IPVS 规则, 并定期与 Kubernetes Service 和 EndpointSlice 同步 IPVS 规则。 该控制回路确保 IPVS 状态与期望的状态保持一致。 +访问 Service 时,IPVS 会将流量导向到某一个后端 Pod。 - - -访问 Service 时,IPVS 会将流量导向到某一个后端 Pod 。 - +--> IPVS 代理模式基于 netfilter 回调函数,类似于 iptables 模式, 但它使用哈希表作为底层数据结构,在内核空间中生效。 这意味着 IPVS 模式下的 kube-proxy 比 iptables 模式下的 kube-proxy 重定向流量的延迟更低,同步代理规则时性能也更好。 与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。 - - +--> IPVS 为将流量均衡到后端 Pod 提供了更多选择: * `rr`:轮询 @@ -397,209 +416,225 @@ IPVS 为将流量均衡到后端 Pod 提供了更多选择: * `sed`:最短预期延迟 * `nq`:最少队列 - {{< note >}} - - +--> 要在 IPVS 模式下运行 kube-proxy,必须在启动 kube-proxy 之前确保节点上的 IPVS 可用。 当 kube-proxy 以 IPVS 代理模式启动时,它会验证 IPVS 内核模块是否可用。 如果未检测到 IPVS 内核模块,则 kube-proxy 会退回到 iptables 代理模式运行。 {{< /note >}} - + +{{< figure src="/images/docs/services-ipvs-overview.svg" title="IPVS 模式下 Service 的虚拟 IP 地址机制" class="diagram-medium" >}} -{{< figure src="/images/docs/services-ipvs-overview.svg" title="IPVS 代理的 Service 概览" class="diagram-medium" >}} - - +--> ## 会话亲和性 {#session-affinity} - +--> 在这些代理模型中,绑定到 Service IP:Port 的流量被代理到合适的后端, 客户端不需要知道任何关于 Kubernetes、Service 或 Pod 的信息。 - +--> 如果要确保来自特定客户端的连接每次都传递给同一个 Pod, 你可以通过设置 Service 的 `.spec.sessionAffinity` 为 `ClientIP` 来设置基于客户端 IP 地址的会话亲和性(默认为 `None`)。 - +--> ### 会话粘性超时 {#session-stickiness-timeout} - +--> 你还可以通过设置 Service 的 `.spec.sessionAffinityConfig.clientIP.timeoutSeconds` 来设置最大会话粘性时间(默认值为 10800,即 3 小时)。 {{< note >}} - +--> 在 Windows 上不支持为 Service 设置最大会话粘性时间。 {{< /note >}} - +--> ## 将 IP 地址分配给 Service {#ip-address-assignment-to-services} - +--> 与实际路由到固定目标的 Pod IP 地址不同,Service IP 实际上不是由单个主机回答的。 相反,kube-proxy 使用数据包处理逻辑(例如 Linux 的 iptables) 来定义**虚拟** IP 地址,这些地址会按需被透明重定向。 - +--> 当客户端连接到 VIP 时,其流量会自动传输到适当的端点。 实际上,Service 的环境变量和 DNS 是根据 Service 的虚拟 IP 地址(和端口)填充的。 - +--> ### 避免冲突 {#avoiding-collisions} - +--> Kubernetes 的主要哲学之一是, 你不应需要在完全不是你的问题的情况下面对可能导致你的操作失败的情形。 对于 Service 资源的设计,也就是如果你选择的端口号可能与其他人的选择冲突, 就不应该让你自己选择端口号。这是一种失败隔离。 - +--> 为了允许你为 Service 选择端口号,我们必须确保没有任何两个 Service 会发生冲突。 Kubernetes 通过从为 API 服务器配置的 `service-cluster-ip-range` CIDR 范围内为每个 Service 分配自己的 IP 地址来实现这一点。 - +--> 为了确保每个 Service 都获得唯一的 IP,内部分配器在创建每个 Service 之前更新 {{< glossary_tooltip term_id="etcd" >}} 中的全局分配映射,这种更新操作具有原子性。 映射对象必须存在于数据库中,这样 Service 才能获得 IP 地址分配, 否则创建将失败,并显示无法分配 IP 地址。 - +--> 在控制平面中,后台控制器负责创建该映射(从使用内存锁定的旧版本的 Kubernetes 迁移时需要这一映射)。 Kubernetes 还使用控制器来检查无效的分配(例如,因管理员干预而导致无效分配) 以及清理已分配但没有 Service 使用的 IP 地址。 - +--> #### Service 虚拟 IP 地址的地址段 {#service-ip-static-sub-range} {{< feature-state for_k8s_version="v1.25" state="beta" >}} - +--> Kubernetes 根据配置的 `service-cluster-ip-range` 的大小使用公式 - `min(max(16, cidrSize / 16), 256)` 将 `ClusterIP` 范围分为两段。 +`min(max(16, cidrSize / 16), 256)` 将 `ClusterIP` 范围分为两段。 该公式可以解释为:介于 16 和 256 之间,并在上下界之间存在渐进阶梯函数的分配。 - +--> Kubernetes 优先通过从高段中选择来为 Service 分配动态 IP 地址, 这意味着如果要将特定 IP 地址分配给 `type: ClusterIP` Service, 则应手动从**低**段中分配 IP 地址。 该方法降低了分配导致冲突的风险。 - +--> 如果你禁用 `ServiceIPStaticSubrange` [特性门控](/zh-cn/docs/reference/command-line-tools-reference/feature-gates/), 则 Kubernetes 用于手动分配和动态分配的 IP 共享单个地址池,这适用于 `type: ClusterIP` 的 Service。 - +--> ## 流量策略 {#traffic-policies} - +--> 你可以设置 `.spec.internalTrafficPolicy` 和 `.spec.externalTrafficPolicy` 字段来控制 Kubernetes 如何将流量路由到健康(“就绪”)的后端。 - +### 内部流量策略 {#internal-traffic-policy} + +{{< feature-state for_k8s_version="v1.22" state="beta" >}} + + +你可以设置 `.spec.internalTrafficPolicy` 字段来控制来自内部源的流量如何被路由。 +有效值为 `Cluster` 和 `Local`。 +将字段设置为 `Cluster` 会将内部流量路由到所有准备就绪的端点, +将字段设置为 `Local` 仅会将流量路由到本地节点准备就绪的端点。 +如果流量策略为 `Local` 但没有本地节点端点,那么 kube-proxy 会丢弃该流量。 + + +--> ### 外部流量策略 {#external-traffic-policy} - - +--> 你可以设置 `.spec.externalTrafficPolicy` 字段来控制从外部源路由的流量。 有效值为 `Cluster` 和 `Local`。 将字段设置为 `Cluster` 会将外部流量路由到所有准备就绪的端点, @@ -607,79 +642,70 @@ relevant Service. 如果流量策略为 `Local` 并且没有本地节点端点, 那么 kube-proxy 不会转发与相关 Service 相关的任何流量。 -{{< note >}} -{{< feature-state for_k8s_version="v1.22" state="alpha" >}} - -如果为 kube-proxy 启用了 `ProxyTerminatingEndpoints` -[特性门控](/zh-cn/docs/reference/command-line-tools-reference/feature-gates/), -kube-proxy 会检查节点是否具有本地端点以及是否所有本地端点都标记为终止。 -如果有本地端点并且**所有**本地端点都被标记为终止,则 kube-proxy 忽略所有取值为 `Local` 的外部流量策略。 -相反,当所有本地节点端点均处于终止中时, -kube-proxy 将该 Service 的流量转发到其他健康端点, -就好像外部流量策略设置为 `Cluster` 一样。 + +### 流向正终止的端点的流量 {#traffic-to-terminating-endpoints} +{{< feature-state for_k8s_version="v1.26" state="beta" >}} -这种对处于终止中的端点的转发行为使得外部负载均衡器能优雅地排空由 -`NodePort` 服务支持的连接,即使在健康检查节点端口开始出现故障时也是如此。 -否则,在节点仍然在负载均衡器的节点池情况下,在 Pod 终止期间,流量可能会丢失。 -{{< /note >}} +If the `ProxyTerminatingEndpoints` +[feature gate](/docs/reference/command-line-tools-reference/feature-gates/) +is enabled in kube-proxy and the traffic policy is `Local`, that node's +kube-proxy uses a more complicated algorithm to select endpoints for a Service. +With the feature enabled, kube-proxy checks if the node +has local endpoints and whether or not all the local endpoints are marked as terminating. +If there are local endpoints and **all** of them are terminating, then kube-proxy +will forward traffic to those terminating endpoints. Otherwise, kube-proxy will always +prefer forwarding traffic to endpoints that are not terminating. +--> +如果为 kube-proxy 启用了 `ProxyTerminatingEndpoints` +[特性门控](/zh-cn/docs/reference/command-line-tools-reference/feature-gates/)且流量策略为 `Local`, +则节点的 kube-proxy 将使用更复杂的算法为 Service 选择端点。 +启用此特性时,kube-proxy 会检查节点是否具有本地端点以及是否所有本地端点都标记为正在终止过程中。 +如果有本地端点并且**所有**本地端点都被标记为处于终止过程中, +则 kube-proxy 会将转发流量到这些正在终止过程中的端点。 +否则,kube-proxy 会始终选择将流量转发到并未处于终止过程中的端点。 - + +这种对处于终止过程中的端点的转发行为使得 `NodePort` 和 `LoadBalancer` Service +能有条不紊地腾空设置了 `externalTrafficPolicy: Local` 时的连接。 -{{< feature-state for_k8s_version="v1.22" state="beta" >}} +当一个 Deployment 被滚动更新时,处于负载均衡器后端的节点可能会将该 Deployment 的 N 个副本缩减到 +0 个副本。在某些情况下,外部负载均衡器可能在两次执行健康检查探针之间将流量发送到具有 0 个副本的节点。 +将流量路由到处于终止过程中的端点可确保正在缩减 Pod 的节点能够正常接收流量, +并逐渐降低指向那些处于终止过程中的 Pod 的流量。 +到 Pod 完成终止时,外部负载均衡器应该已经发现节点的健康检查失败并从后端池中完全移除该节点。 - -你可以设置 `.spec.internalTrafficPolicy` 字段来控制从内部源路由的流量。 -有效值为 `Cluster` 和 `Local`。 -将字段设置为 `Cluster` 会将内部流量路由到所有准备就绪的端点, -将字段设置为 `Local` 仅会将流量路由到本地节点准备就绪的端点。 -如果流量策略为 `Local` 但没有本地节点端点,那么 kube-proxy 会丢弃该流量。 - - ## {{% heading "whatsnext" %}} - +read [Connecting Applications with Services](/docs/tutorials/services/connect-applications-service/). +--> 要了解有关 Service 的更多信息, 请阅读[使用 Service 连接应用](/zh-cn/docs/tutorials/services/connect-applications-service/)。 - - 也可以: -* 阅读 [Service](/zh-cn/docs/concepts/services-networking/service/) + +* 阅读 [Service](/zh-cn/docs/concepts/services-networking/service/) 了解其概念 +* 阅读 [Ingress](/zh-cn/docs/concepts/services-networking/ingress/) 了解其概念 * 阅读 [API 参考](/zh-cn/docs/reference/kubernetes-api/service-resources/service-v1/)进一步了解 Service API