Kubernetes 文档的这一部分提供了 Kubernetes 网络的参考详细信息。
1 - Service 所用的协议
如果你配置 Service, 你可以从 Kubernetes 支持的任何网络协议中选择一个协议。
Kubernetes 支持以下协议用于 Service:
当你定义 Service 时, 你还可以指定其使用的应用协议。
本文详细说明了一些特殊场景,这些场景通常均使用 TCP 作为传输协议:
支持的协议
Service 端口的 protocol
有 3 个有效值:
SCTP
Kubernetes v1.20 [stable]
当使用支持 SCTP 流量的网络插件时,你可以为大多数 Service 使用 SCTP。
对于 type: LoadBalancer
Service,对 SCTP 的支持情况取决于提供此项设施的云供应商(大部分不支持)。
运行 Windows 的节点不支持 SCTP。
支持多宿主 SCTP 关联
对多宿主 SCTP 关联的支持要求 CNI 插件可以支持为 Pod 分配多个接口和 IP 地址。
针对多宿主 SCTP 关联的 NAT 需要在对应的内核模块具有特殊的逻辑。
当 kube-proxy 处于 userspace 模式时不支持管理 SCTP 关联。
TCP
你可以将 TCP 用于任何类别的 Service,这是默认的网络协议。
UDP
你可以将 UDP 用于大多数 Service。对于 type: LoadBalancer
Service,
UDP 的支持与否取决于提供此项设施的云供应商。
特殊场景
HTTP
如果你的云供应商支持负载均衡,而且尤其是该云供应商的负载均衡器实现了 HTTP/HTTPS 反向代理, 可将流量转发到该 Service 的后端端点,那么你就可以使用 LoadBalancer 模式的 Service 以便在 Kubernetes 集群外部配置负载均衡器。
通常,你将 Service 协议设置为 TCP
,
并添加一个注解
(一般取决于你的云供应商)配置负载均衡器,以便在 HTTP 级别处理流量。
此配置也可能包括为你的工作负载提供 HTTPS(基于 TLS 的 HTTP)并反向代理纯 HTTP。
你也可以使用 Ingress 来暴露 HTTP/HTTPS Service。
你可能还想指定连接的应用协议是
http
还是 https
。如果从负载均衡器到工作负载的会话是不带 TLS 的 HTTP,请使用 http
;
如果从负载均衡器到工作负载的会话使用 TLS 加密,请使用 https
。
PROXY 协议
如果你的云供应商支持此协议,你可以使用设置为 type: LoadBalancer
的 Service,
在 Kubernetes 本身的外部配置负载均衡器,以转发用
PROXY 协议封装的连接。
负载均衡器然后发送一个初始的八位元组系列来描述传入的连接,这类似于以下示例(PROXY 协议 v1):
PROXY TCP4 192.0.2.202 10.0.42.7 12345 7\r\n
代理协议前导码之后的数据是来自客户端的原始数据。 当任何一侧关闭连接时,负载均衡器也会触发连接关闭并在可行的情况下发送所有残留数据。
通常,你会将 Service 协议定义为 TCP
。
你还会设置一个特定于云供应商的注解,将负载均衡器配置为以 PROXY 协议封装所有传入的连接。
TLS
如果你的云供应商支持 TLS,你可以使用设置为 type: LoadBalancer
的 Service
作为设置外部反向代理的一种方式,其中从客户端到负载均衡器的连接是 TLS 加密的且该负载均衡器是
TLS 对等服务器。从负载均衡器到工作负载的连接可以是 TLS,或可能是纯文本。
你可以使用的确切选项取决于你的云供应商或自定义 Service 实现。
通常,你会将协议设置为 TCP
并设置一个注解(通常特定于你的云供应商),
将负载均衡器配置为充当一个 TLS 服务器。你将使用特定于云供应商的机制来配置 TLS 身份
(作为服务器,也可能作为连接到工作负载的客户端)。
2 - 端口和协议
当你在一个有严格网络边界的环境里运行 Kubernetes,例如拥有物理网络防火墙或者拥有公有云中虚拟网络的自有数据中心, 了解 Kubernetes 组件使用了哪些端口和协议是非常有用的。
控制面
协议 | 方向 | 端口范围 | 目的 | 使用者 |
---|---|---|---|---|
TCP | 入站 | 6443 | Kubernetes API server | 所有 |
TCP | 入站 | 2379-2380 | etcd server client API | kube-apiserver, etcd |
TCP | 入站 | 10250 | Kubelet API | 自身, 控制面 |
TCP | 入站 | 10259 | kube-scheduler | 自身 |
TCP | 入站 | 10257 | kube-controller-manager | 自身 |
尽管 etcd 的端口也列举在控制面的部分,但你也可以在外部自己托管 etcd 集群或者自定义端口。
工作节点
协议 | 方向 | 端口范围 | 目的 | 使用者 |
---|---|---|---|---|
TCP | 入站 | 10250 | Kubelet API | 自身, 控制面 |
TCP | 入站 | 30000-32767 | NodePort Services† | 所有 |
† NodePort Services的默认端口范围。
所有默认端口都可以重新配置。当使用自定义的端口时,你需要打开这些端口来代替这里提到的默认端口。
一个常见的例子是 API 服务器的端口有时会配置为 443。或者你也可以使用默认端口, 把 API 服务器放到一个监听 443 端口的负载均衡器后面,并且路由所有请求到 API 服务器的默认端口。
3 - 虚拟 IP 和服务代理
Kubernetes 集群中的每个节点会运行一个
kube-proxy
(除非你已经部署了自己的替换组件来替代 kube-proxy
)。
kube-proxy
组件负责除 type
为
ExternalName
以外的服务,实现虚拟 IP 机制。
一个时不时出现的问题是,为什么 Kubernetes 依赖代理将入站流量转发到后端。 其他方案呢?例如,是否可以配置具有多个 A 值(或 IPv6 的 AAAA)的 DNS 记录, 使用轮询域名解析?
使用代理转发方式实现 Service 的原因有以下几个:
- DNS 的实现不遵守记录的 TTL 约定的历史由来已久,在记录过期后可能仍有结果缓存。
- 有些应用只做一次 DNS 查询,然后永久缓存结果。
- 即使应用程序和库进行了适当的重新解析,TTL 取值较低或为零的 DNS 记录可能会给 DNS 带来很大的压力, 从而变得难以管理。
在下文中,你可以了解到 kube-proxy 各种实现方式的工作原理。
总的来说,你应该注意到,在运行 kube-proxy
时,
可能会修改内核级别的规则(例如,可能会创建 iptables 规则),
在某些情况下,这些规则直到重启才会被清理。
因此,运行 kube-proxy 这件事应该只由了解在计算机上使用低级别、特权网络代理服务会带来的后果的管理员执行。
尽管 kube-proxy
可执行文件支持 cleanup
功能,但这个功能并不是官方特性,因此只能根据具体情况使用。
本文中的一些细节会引用这样一个例子: 运行了 3 个 Pod 副本的无状态图像处理后端工作负载。 这些副本是可互换的;前端不需要关心它们调用了哪个后端副本。 即使组成这一组后端程序的 Pod 实际上可能会发生变化, 前端客户端不应该也没必要知道,而且也不需要跟踪这一组后端的状态。
代理模式
注意,kube-proxy 会根据不同配置以不同的模式启动。
- kube-proxy 的配置是通过 ConfigMap 完成的,kube-proxy 的 ConfigMap 实际上弃用了 kube-proxy 大部分标志的行为。
- kube-proxy 的 ConfigMap 不支持配置的实时重新加载。
- kube-proxy 不能在启动时验证和检查所有的 ConfigMap 参数。
例如,如果你的操作系统不允许你运行 iptables 命令,标准的 kube-proxy 内核实现将无法工作。
同样,如果你的操作系统不支持
netsh
,它也无法在 Windows 用户空间模式下运行。
用户空间代理模式
Kubernetes v1.23 [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 通过轮询算法选择后端。
用户空间代理的 Service 概览
示例
例如,考虑本文前面描述的图像处理应用的例子。 当创建后端 Service 时,Kubernetes 控制平面分配一个虚拟 IP 地址,例如 10.0.0.1。 假设 Service 端口是 1234,那么集群中的所有 kube-proxy 实例都会观察到该 Service。 当一个 kube-proxy 观察到新 Service 时,它会随机打开一个新端口, 建立从虚拟 IP 地址到这个新端口的 iptables 重定向,并开始在其上接受连接。
当客户端连接到 Service 的虚拟 IP 地址时,iptables 规则会生效,将数据包重定向到代理自身的端口。 “Service 代理” 选择一个后端,并开始代理客户端到后端的流量。
这意味着 Service 所有者可以选择任何他们想要的端口而不会发生冲突。 客户端可以连接到 IP 和端口,也不需要知道它们实际访问的是哪些 Pod。
扩缩容挑战
在中小型规模集群中使用用户空间代理的 VIP 是有效的,但是不能拓展到具有数千个 Service 的大型集群。 针对门户的初始设计提案 中有更多的细节。
使用用户空间代理会隐藏访问 Service 的数据包的源 IP 地址。 这使得某些类型的网络过滤(防火墙)失效。 iptables 代理模式不会隐藏集群内的源 IP 地址, 但仍会隐藏通过负载均衡器或节点端口进入的客户端数据包源 IP 地址。
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 就绪探针来验证后端 Pod 是否健康。 这样可以避免 kube-proxy 将流量发送到已知失败的 Pod 上。
iptables 代理的 Service 概览
示例
仍以前面描述的图像处理应用程序为例。 当创建后端 Service 时,Kubernetes 控制平面会分配一个虚拟 IP 地址,例如 10.0.0.1。 对于这个例子而言,假设 Service 端口是 1234。 集群中的所有 kube-proxy 实例都会观察到新 Service 的创建。
当节点上的 kube-proxy 观察到新 Service 时,它会添加一系列 iptables 规则, 这些规则从虚拟 IP 地址重定向到更多 iptables 规则,每个 Service 都定义了这些规则。 每个 Service 规则链接到每个后端端点的更多规则, 并且每个端点规则将流量重定向(使用目标 NAT)到后端。
当客户端连接到 Service 的虚拟 IP 地址时,iptables 规则会生效。 会选择一个后端(基于会话亲和性或随机选择),并将数据包重定向到后端。 与用户空间代理不同,数据包不会被复制到用户空间, 不需要 kube-proxy 参与,虚拟 IP 地址就可以正常工作, 节点可以看到来自未更改的客户端 IP 地址的流量。
当流量通过节点端口或负载均衡器进入时,也会执行相同的基本流程, 只是在这些情况下,客户端 IP 地址会被更改。
IPVS 代理模式
在 ipvs
模式下,kube-proxy 监视 Kubernetes Service 和 EndpointSlice,
然后调用 netlink
接口创建 IPVS 规则,
并定期与 Kubernetes Service 和 EndpointSlice 同步 IPVS 规则。
该控制回路确保 IPVS 状态与期望的状态保持一致。
访问 Service 时,IPVS 会将流量导向到某一个后端 Pod 。
IPVS 代理模式基于 netfilter 回调函数,类似于 iptables 模式, 但它使用哈希表作为底层数据结构,在内核空间中生效。 这意味着 IPVS 模式下的 kube-proxy 比 iptables 模式下的 kube-proxy 重定向流量的延迟更低,同步代理规则时性能也更好。 与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。
IPVS 为将流量均衡到后端 Pod 提供了更多选择:
rr
:轮询lc
:最少连接(打开连接数最少)dh
:目标地址哈希sh
:源地址哈希sed
:最短预期延迟nq
:最少队列
要在 IPVS 模式下运行 kube-proxy,必须在启动 kube-proxy 之前确保节点上的 IPVS 可用。
当 kube-proxy 以 IPVS 代理模式启动时,它会验证 IPVS 内核模块是否可用。 如果未检测到 IPVS 内核模块,则 kube-proxy 会退回到 iptables 代理模式运行。
IPVS 代理的 Service 概览
会话亲和性
在这些代理模型中,绑定到 Service IP:Port 的流量被代理到合适的后端, 客户端不需要知道任何关于 Kubernetes、Service 或 Pod 的信息。
如果要确保来自特定客户端的连接每次都传递给同一个 Pod,
你可以通过设置 Service 的 .spec.sessionAffinity
为 ClientIP
来设置基于客户端 IP 地址的会话亲和性(默认为 None
)。
会话粘性超时
你还可以通过设置 Service 的 .spec.sessionAffinityConfig.clientIP.timeoutSeconds
来设置最大会话粘性时间(默认值为 10800,即 3 小时)。
在 Windows 上不支持为 Service 设置最大会话粘性时间。
将 IP 地址分配给 Service
与实际路由到固定目标的 Pod IP 地址不同,Service IP 实际上不是由单个主机回答的。 相反,kube-proxy 使用数据包处理逻辑(例如 Linux 的 iptables) 来定义虚拟 IP 地址,这些地址会按需被透明重定向。
当客户端连接到 VIP 时,其流量会自动传输到适当的端点。 实际上,Service 的环境变量和 DNS 是根据 Service 的虚拟 IP 地址(和端口)填充的。
避免冲突
Kubernetes 的主要哲学之一是, 你不应需要在完全不是你的问题的情况下面对可能导致你的操作失败的情形。 对于 Service 资源的设计,也就是如果你选择的端口号可能与其他人的选择冲突, 就不应该让你自己选择端口号。这是一种失败隔离。
为了允许你为 Service 选择端口号,我们必须确保没有任何两个 Service 会发生冲突。
Kubernetes 通过从为 API 服务器配置的 service-cluster-ip-range
CIDR 范围内为每个 Service 分配自己的 IP 地址来实现这一点。
为了确保每个 Service 都获得唯一的 IP,内部分配器在创建每个 Service 之前更新 etcd 中的全局分配映射,这种更新操作具有原子性。 映射对象必须存在于数据库中,这样 Service 才能获得 IP 地址分配, 否则创建将失败,并显示无法分配 IP 地址。
在控制平面中,后台控制器负责创建该映射(从使用内存锁定的旧版本的 Kubernetes 迁移时需要这一映射)。 Kubernetes 还使用控制器来检查无效的分配(例如,因管理员干预而导致无效分配) 以及清理已分配但没有 Service 使用的 IP 地址。
Service 虚拟 IP 地址的地址段
Kubernetes v1.25 [beta]
Kubernetes 根据配置的 service-cluster-ip-range
的大小使用公式
min(max(16, cidrSize / 16), 256)
将 ClusterIP
范围分为两段。
该公式可以解释为:介于 16 和 256 之间,并在上下界之间存在渐进阶梯函数的分配。
Kubernetes 优先通过从高段中选择来为 Service 分配动态 IP 地址,
这意味着如果要将特定 IP 地址分配给 type: ClusterIP
Service,
则应手动从低段中分配 IP 地址。
该方法降低了分配导致冲突的风险。
如果你禁用 ServiceIPStaticSubrange
特性门控,
则 Kubernetes 用于手动分配和动态分配的 IP 共享单个地址池,这适用于 type: ClusterIP
的 Service。
流量策略
你可以设置 .spec.internalTrafficPolicy
和 .spec.externalTrafficPolicy
字段来控制 Kubernetes 如何将流量路由到健康(“就绪”)的后端。
外部流量策略
你可以设置 .spec.externalTrafficPolicy
字段来控制从外部源路由的流量。
有效值为 Cluster
和 Local
。
将字段设置为 Cluster
会将外部流量路由到所有准备就绪的端点,
将字段设置为 Local
仅会将流量路由到本地节点上准备就绪的端点。
如果流量策略为 Local
并且没有本地节点端点,
那么 kube-proxy 不会转发与相关 Service 相关的任何流量。
Kubernetes v1.22 [alpha]
如果为 kube-proxy 启用了 ProxyTerminatingEndpoints
特性门控,
kube-proxy 会检查节点是否具有本地端点以及是否所有本地端点都标记为终止。
如果有本地端点并且所有本地端点都被标记为终止,则 kube-proxy 忽略所有取值为 Local
的外部流量策略。
相反,当所有本地节点端点均处于终止中时,
kube-proxy 将该 Service 的流量转发到其他健康端点,
就好像外部流量策略设置为 Cluster
一样。
这种对处于终止中的端点的转发行为使得外部负载均衡器能优雅地排空由
NodePort
服务支持的连接,即使在健康检查节点端口开始出现故障时也是如此。
否则,在节点仍然在负载均衡器的节点池情况下,在 Pod 终止期间,流量可能会丢失。
内部流量策略
Kubernetes v1.22 [beta]
你可以设置 .spec.internalTrafficPolicy
字段来控制从内部源路由的流量。
有效值为 Cluster
和 Local
。
将字段设置为 Cluster
会将内部流量路由到所有准备就绪的端点,
将字段设置为 Local
仅会将流量路由到本地节点准备就绪的端点。
如果流量策略为 Local
但没有本地节点端点,那么 kube-proxy 会丢弃该流量。
接下来
要了解有关 Service 的更多信息, 请阅读使用 Service 连接应用。
也可以: