Kubernetes Service Proxy 无秘密
背景
是负责 k8s 集群内通信规则创建的组建,k8s 官方文档解释
Kubernetes 网络代理在每个节点上运行。网络代理反映了每个节点上 Kubernetes API 中定义的服务,并且可以执行简单的 TCP、UDP 和 SCTP 流转发,或者在一组后端进行 循环 TCP、UDP 和 SCTP 转发。
但是 kube-proxy 本身并不负责流量转发等工作。Kubernetes 支持三种 Service Proxy 模式:iptables、IPVS 和 Userspace。根据服务代理方式分析服务请求报文路径。
Service 和 Pod 信息
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
my-nginx-756f645cd7-gh7sq 1/1 Running 14 15d 192.167.2.231 kube03
my-nginx-756f645cd7-hm7rg 1/1 Running 17 20d 192.167.2.206 kube03
my-nginx-756f645cd7-qfqbp 1/1 Running 16 20d 192.167.1.123 kube02
$ kubectl get pod -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
my-nginx-cluster ClusterIP 10.103.1.234 80/TCP 15d run=my-nginx
my-nginx-loadbalancer LoadBalancer 10.96.98.173 172.35.0.200 80:30781/TCP 15d run=my-nginx
my-nginx-nodeport NodePort 10.97.229.148 80:30915/TCP
这里展示了 的 和 信息。可以从上面的信息看出部署了三个 Pod。并添加了 类型的 、 类型的 和 的 。
Iptables 模式

模式下的 :
Chain KUBE-SERVICES (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ tcp -- * * !192.167.0.0/16 10.96.98.173 /* default/my-nginx-loadbalancer: cluster IP */ tcp dpt:80
0 0 KUBE-SVC-TNQCJ2KHUMKABQTD tcp -- * * 0.0.0.0/0 10.96.98.173 /* default/my-nginx-loadbalancer: cluster IP */ tcp dpt:80
0 0 KUBE-FW-TNQCJ2KHUMKABQTD tcp -- * * 0.0.0.0/0 172.35.0.200 /* default/my-nginx-loadbalancer: loadbalancer IP */ tcp dpt:80
0 0 KUBE-MARK-MASQ tcp -- * * !192.167.0.0/16 10.103.1.234 /* default/my-nginx-cluster: cluster IP */ tcp dpt:80
0 0 KUBE-SVC-52FY5WPFTOHXARFK tcp -- * * 0.0.0.0/0 10.103.1.234 /* default/my-nginx-cluster: cluster IP */ tcp dpt:80
0 0 KUBE-MARK-MASQ tcp -- * * !192.167.0.0/16 10.97.229.148 /* default/my-nginx-nodeport: cluster IP */ tcp dpt:80
0 0 KUBE-SVC-6JXEEPSEELXY3JZG tcp -- * * 0.0.0.0/0 10.97.229.148 /* default/my-nginx-nodeport: cluster IP */ tcp dpt:80
0 0 KUBE-NODEPORTS all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
模式下的 :
Chain KUBE-NODEPORTS (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-nginx-loadbalancer: */ tcp dpt:30781
0 0 KUBE-SVC-TNQCJ2KHUMKABQTD tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-nginx-loadbalancer: */ tcp dpt:30781
0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-nginx-nodeport: */ tcp dpt:30915
0 0 KUBE-SVC-6JXEEPSEELXY3JZG tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-nginx-nodeport: */ tcp dpt:30915
模式下的 :
Chain KUBE-FW-TNQCJ2KHUMKABQTD (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-nginx-loadbalancer: loadbalancer IP */
0 0 KUBE-SVC-TNQCJ2KHUMKABQTD all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-nginx-loadbalancer: loadbalancer IP */
0 0 KUBE-MARK-DROP all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-nginx-loadbalancer: loadbalancer IP */
模式下的 :
Chain KUBE-SVC-TNQCJ2KHUMKABQTD (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SEP-6HM47TA5RTJFOZFJ all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.33332999982
0 0 KUBE-SEP-AHRDCNDYGFSFVA64 all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.50000000000
0 0 KUBE-SEP-BK523K4AX5Y34OZL all -- * * 0.0.0.0/0 0.0.0.0/0
模式下的 :
Chain KUBE-SEP-6HM47TA5RTJFOZFJ (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 192.167.2.231 0.0.0.0/0
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp to:192.167.2.231:80
模式下的 :
Chain KUBE-POSTROUTING (1 references)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */ mark match
0x4000/0x4000
模式下的 :
Chain KUBE-MARK-MASQ (23 references)
pkts bytes target prot opt in out source destination
0 0 MARK all -- * * 0.0.0.0/0 0.0.0.0/0 MARK or 0x4000
模式下的 :
Chain KUBE-MARK-DROP (10 references)
pkts bytes target prot opt in out source destination
0 0 MARK all -- * * 0.0.0.0/0 0.0.0.0/0 MARK or 0x8000
这里的 使用是 模式。这是 当前使用的默认代理模式。在上方图中展示了 模式下服务请求包的路径。上方的 是展示了主要 表的内容。
由于大多数 传输的请求包是通过 的 传递到主机的网络命名空间,因此请求包通过 表传递到 表。 或者 进程 使用 的网络命名空间传输的请求数据包由 表传递到 表。
如果 表中请求包的目标和目标与的和匹配,则请求的包被转发到 表,即的表.
如果 表中请求包的目标 是节点自己的 ,则将请求包转发到 表。如果 表中请求报文的目标 与 的 匹配,则将请求报文传送到 表,即 的 表。
如果 表中请求包的目标 和目标 与 的和匹配,则将请求包转发到 表,的表,然后再将 传送到 表,也就是 表
在 表中,请求包通过的统计功能,在构成的之间起到随机均匀负载均衡的作用。在 中,由于由三个组成,可以看出请求包设置为随机均衡负载均衡,使用三个表。在 表中,请求包使用 的 和 中设置的 进行 。由的发出的请求包通过构建的容器网络传递给该。
由于传递给的请求包是通过的传递给的,所以发出的响应包的应该到,而不是 。 中未指定 的 规则。但是,根据 的(连接跟踪)的 连接信息对从 收到的响应数据包进行 。
Source IP
请求包的将被留存,或通过作为的进行。 是一个表,将使用 对请求包进行标记。的在 表中成为,作为的成为。在 表中,你会发现 表会检测出被标记过的包。

根据NodePort、LoadBalancer Service的externalTrafficPolicy的数据包路径图
主要用于。因为由 的负载均衡器执行负载均衡,所以主机不需要负载均衡,所以可以保留请求包的。如果值为,云服务提供商的负载均衡器会对目标 Pod 执行健康检查,如果健康检查失败或者目标 Pod 不存在,那么发送到主机上的数据包将会被丢弃删除。

在中向自己所属的的发送请求数据包,在请求数据包返回时也需要 。图中左边就标识这种情况。请求数据包被DNAT, 数据包的和都是本身的。因此,返回响应数据包时,响应数据包不通过的表,因此不会执行,直接在 中处理。
如果使用 ,则可以通过将返回 的请求包强制传递给 Host 来执行 SNAT。这种通过故意绕过数据包来接收数据包的方法称为。图中的右侧显示了使用应用的情况。如果表中请求数据包的Src IP与DNAT的IP相同,即发送到的数据包由自己接收时,则请求的数据包经过 表是被 ,在 表中被因为接收到的数据包的被设置为的,因此的响应被发送到的 表,然后进行和传递给。
用户空间
Userspace Mode下的服务请求报文路径图:

用户空间模式下的 KUBE-PORTALS-CONTAINER:
Chain KUBE-PORTALS-CONTAINER (1 references)
pkts bytes target prot opt in out source destination
0 0 REDIRECT tcp -- * * 0.0.0.0/0 10.96.98.173 /* default/my-nginx-loadbalancer: */ tcp dpt:80 redir ports 38023
0 0 REDIRECT tcp -- * * 0.0.0.0/0 172.35.0.200 /* default/my-nginx-loadbalancer: */ tcp dpt:80 redir ports 38023
0 0 REDIRECT tcp -- * * 0.0.0.0/0 10.103.1.234 /* default/my-nginx-cluster: */ tcp dpt:80 redir ports 36451
0 0 REDIRECT tcp -- * * 0.0.0.0/0 10.97.229.148 /* default/my-nginx-nodeport: */ tcp dpt:80 redir ports 44257
用户空间模式下的 KUBE-NODEPORT-CONTAINER:
Chain KUBE-NODEPORT-CONTAINER (1 references)
pkts bytes target prot opt in out source destination
0 0 REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-nginx-loadbalancer: */ tcp dpt:30781 redir ports 38023
0 0 REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-nginx-nodeport: */ tcp dpt:30915 redir ports 44257
用户空间模式下的 KUBE-PORTALS-HOST:
Chain KUBE-PORTALS-HOST (1 references)
pkts bytes target prot opt in out source destination
0 0 DNAT tcp -- * * 0.0.0.0/0 10.96.98.173 /* default/my-nginx-loadbalancer: */ tcp dpt:80 to:172.35.0.100:38023
0 0 DNAT tcp -- * * 0.0.0.0/0 172.35.0.200 /* default/my-nginx-loadbalancer: */ tcp dpt:80 to:172.35.0.100:38023
0 0 DNAT tcp -- * * 0.0.0.0/0 10.103.1.234 /* default/my-nginx-cluster: */ tcp dpt:80 to:172.35.0.100:46635
0 0 DNAT tcp -- * * 0.0.0.0/0 10.97.229.148 /* default/my-nginx-nodeport: */ tcp dpt:80 to:172.35.0.100:32847
用户空间模式下的 KUBE-NODEPORT-HOST:
Chain KUBE-NODEPORT-HOST (1 references)
pkts bytes target prot opt in out source destination
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-nginx-loadbalancer: */ tcp dpt:30781 to:172.35.0.100:38023
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-nginx-nodeport: */ tcp dpt:30915 to:172.35.0.100:44257
Service Proxy 的 iptables 模式是一种运行在用户空间的 kube-proxy 扮演 Service Proxy 角色的模式。这是 Kubernetes 提供的第一个代理模式。目前使用的不是很好,因为与iptables模式相比性能较差。上图中展示了 Userspace 模式下Service 请求包的路径。
由于大多数 传输的请求包是通过 的 传递到 的 命名空间,因此请求包通过 PREROUTING 表传递到 表。如果 标准中请求包的 目标 和目标 与 服务的 和 匹配,则请求包被重定向到 。如果请求数据包的目标 是节点自己的,则将数据包投递到表。如果 表中请求包的 目标 与 的端口匹配,则请求包被重定向到 。如果请求包的目标 和目标 与的和匹配,请求包也会被重定向到kube-proxy。
或 进程使用 的 传输的请求数据包由 表 传递到 表。 和 表中的后续请求包处理类似于 和 表中的请求包处理。不同之处在于,DNAT是在不重定向请求包的情况下执行的。
通过 和 发送到 的所有请求数据包都被传递到 。 收到的请求数据包的每个目标 映射一个服务。因此, 可以通过重定向和 请求数据包的 目标 确定请求数据包应该传递到哪个服务。 通过将接收到的请求数据包均匀地负载均衡到属于请求数据包要传输到的服务的多个 来重新传输接收到的请求数据包。
由于 运行在主机的 中,因此 发送的请求包也会经过 表。但是由于发送的请求包的目标 是的,所以请求包不会被 表改变,而是通过搭建的传递给。