Kubernetes Service Proxy 无秘密
背景
kube-proxy 是负责 k8s 集群内通信规则创建的组建,k8s 官方文档解释
Kubernetes 网络代理在每个节点上运行。网络代理反映了每个节点上 Kubernetes API 中定义的服务,并且可以执行简单的 TCP、UDP 和 SCTP 流转发,或者在一组后端进行 循环 TCP、UDP 和 SCTP 转发。
但是 kube-proxy 本身并不负责流量转发等工作。Kubernetes 支持三种 Service Proxy 模式:iptables、IPVS 和 Userspace。根据服务代理方式分析服务请求报文路径。
Service 和 Pod 信息
这里展示了 Kubernetes Service Proxy 的 Service 和 Pod 信息。可以从上面的信息看出部署了三个 nginx Pod。并添加了ClusterIP 类型的 my-nginx-cluster 、NodePort 类型的 nginx-nodeport 和 LoadBalancer 的 my-nginx-loadbalancer。
Iptables 模式
iptables 模式下的 KUBE-SERVICES:
iptables 模式下的 KUBE-NODEPORTS:
iptables 模式下的 KUBE-FW-XXX:
iptables 模式下的 KUBE-SVC-XXX:
iptables 模式下的 KUBE-SEP-XXX:
iptables 模式下的 KUBE-POSTROUTING:
iptables 模式下的 KUBE-MARK-MASQ:
iptables 模式下的 KUBE-MARK-DROP:
这里的 Service Proxy 使用是 iptables 模式。这是 Kubernetes 当前使用的默认代理模式。在上方图中展示了 iptables 模式下服务请求包的路径。上方的 bash 是展示了主要 NAT 表的内容。
由于大多数 Pod 传输的请求包是通过 Pod 的 veth 传递到主机的网络命名空间,因此请求包通过 PREROUTING 表传递到 KUBE-SERVICES 表。Pod 或者 Host进程 使用 Host 的网络命名空间传输的请求数据包由 OUTPUT 表传递到 KUBE-SERVICES 表。
如果KUBE-SERVICES 表中请求包的目标IP和目标Port与ClusterIP Service的IP和Port匹配,则请求的包被转发到KUBE-SVC-XXX 表,即ClusterIP Service的NAT表.
如果 KUBE-SERVICES 表中请求包的目标 IP 是节点自己的 IP,则将请求包转发到 KUBE-NODEPORTS 表。如果 KUBE-NODEPORTS 表中请求报文的目标 Port 与 NodePort Service 的 Port匹配,则将请求报文传送到 KUBE-SVC-XXX 表,即 NodePort Service 的 NAT 表。
如果KUBE-SERVICES 表中请求包的目标 IP和目标 Port与LoadBalancer Service 的External IP和Port匹配,则将请求包转发到KUBE-FW-XXX 表,LoadBalancer Service的NAT表,然后再将 LoadBalancer Service 传送到 KUBE-SVC-XXX 表,也就是 NAT 表
在KUBE-SVC-XXX 表中,请求包通过iptables的统计功能,在构成Service的Pod之间起到随机均匀负载均衡的作用。在 KUBE-SVC-TNQCJ2KHUMKABQTD 中,由于Service由三个pod组成,可以看出请求包设置为随机均衡负载均衡,使用三个KUBE-SEP-XXX表。在 KUBE-SEP-XXX 表中,请求包使用 Pod 的 IP 和 Service 中设置的 Port 进行 DNAT。由Pod的IP发出的DNAT请求包通过CNI Plugin构建的容器网络传递给该Pod。
由于传递给service的请求包是通过iptables的DNAT传递给Pod的,所以Pod发出的响应包的Src IP应该SNAT到Service IP,而不是Pod IP 。iptables 中未指定 Serivce 的 SNAT 规则。但是,iptables根据Linux Kernel 的Conntrack(连接跟踪)的TCP 连接信息对从 Service Pod 收到的响应数据包进行 SNAT。
Source IP
Service请求包的Src IP将被留存,或通过Masquerade作为Host的IP进行SNAT。KUBE-MARK-MASQ 是一个表,将使用 Masquerade 对请求包进行标记。Marking的Packet在KUBE-POSTROUTING 表中成为Masquerade,Src IP作为Host的IP成为SNAT。在iptables 表中,你会发现KUBE-MARK-MASQ 表会检测出被Masquerade标记过的包。
根据 NodePort、LoadBalancer Service 的 externalTrafficPolicy 的数据包路径图
图中左侧为externalTrafficPolicy值为Cluster,执行 Masquerade。LoadBalancer Service 的 NodePort 和 externalTrafficPolicy 的 Cluster。如果externalTrafficPolicy值设置为Cluster,则请求包的Src IP通过Masquerade 被 SNAT到Host的IP。在 KUBE-NODEPORTS 表中,可以通过 KUBE-MARK-MASQ 表检查所有以 NodePort 和 LoadBalancer Service Port作为 Dest Port 的数据包。
图右侧是通过将externalTrafficPolicy设置为Local,不执行Masquerade如果将 externalTrafficPolicy 值设置为 Local,则 KUBE-MARK-MASQ 表中相关规则将不会出现在 KUBE-NODEPORTS 表中,所以就不会执行到 Masquerade。请求数据包的 Src IP 不会被修改。另外,请求包在宿主机中不会被负载均衡,而是将请求包发送到的 Host 中的目标pod。如果请求数据包被发送到主机不存在目标 pod,那么请求数据包将被丢弃。
ExternalTrafficPolicy Local主要用于LoadBalancer Service。因为由 Cloud Provider 的负载均衡器执行负载均衡,所以主机不需要负载均衡,所以可以保留请求包的Src IP。如果externalTrafficPolicy值为Local,云服务提供商的负载均衡器会对目标 Pod 执行健康检查,如果健康检查失败或者目标 Pod 不存在,那么发送到主机上的数据包将会被丢弃删除。
在Pod中向自己所属的Service的IP发送请求数据包,在请求数据包返回时也需要 Masquerade。图中左边就标识这种情况。请求数据包被 DNAT, 数据包的Src IP和Dest IP都是Pod本身的IP。因此,Pod返回响应数据包时,响应数据包不通过Host的NAT表,因此不会执行SNAT,直接在 Pod 中处理。
如果使用 Masquerade,则可以通过将返回 Pod 的请求包强制传递给 Host 来执行 SNAT。这种通过故意绕过数据包来接收数据包的方法称为Hairpinning。图中的右侧显示了使用Masqurade应用Hairpinning的情况。如果KUBE-SEP-XXX表中请求数据包的 Src IP 与 DNAT 的 IP 相同,即Pod发送到Service的数据包由自己接收时,则请求的数据包经过KUBE-MARK-MASQ 表是被 Marking,在KUBE-POSTROUTING 表中被Masquerade因为Pod接收到的数据包的Src IP被设置为Host的IP,因此Pod的响应被发送到Host的NAT 表,然后进行SNAT和DNAT传递给Pod。
用户空间
Userspace Mode 下的服务请求报文路径图:
用户空间模式下的 KUBE-PORTALS-CONTAINER:
用户空间模式下的 KUBE-NODEPORT-CONTAINER:
用户空间模式下的 KUBE-PORTALS-HOST:
用户空间模式下的 KUBE-NODEPORT-HOST:
Service Proxy 的 iptables 模式是一种运行在用户空间的 kube-proxy 扮演 Service Proxy 角色的模式。这是 Kubernetes 提供的第一个代理模式。目前使用的不是很好,因为与 iptables 模式相比性能较差。上图中展示了 Userspace 模式下 Service 请求包的路径。
由于大多数 Pod 传输的请求包是通过 Pod 的 veth 传递到 Host 的 Network 命名空间,因此请求包通过 PREROUTING 表传递到KUBE-PORTALS-CONTAINER 表。如果 KUBE-PORTALS-CONTAINER 标准中请求包的 目标 IP 和目标 Port 与 ClusterIP 服务的 IP 和 Port 匹配,则请求包被重定向到kube-proxy 。如果请求数据包的目标 IP是节点自己的IP,则将数据包投递到KUBE-NODEPORT-CONTAINER表。如果 KUBE-NODEPORT-CONTAINER 表中请求包的 目标 Port 与 NodePort Service 的端口匹配,则请求包被重定向到 kube-proxy。如果请求包的目标 IP和目标 Port与LoadBalancer Service的External IP和Port匹配,请求包也会被重定向到 kube-proxy。
Pod 或 Host 进程使用 Host 的 Network Namespace 传输的请求数据包由 OUTPUT 表 传递到 KUBE-PORTALS-HOST 表。KUBE-PORTALS-HOST 和 KUBE-NODEPORT-HOST 表中的后续请求包处理类似于 KUBE-PORTALS-CONTAINER 和 KUBE-NODEPORT-CONTAINER 表中的请求包处理。不同之处在于,DNAT 是在不重定向请求包的情况下执行的。
通过 Redirect 和 DNAT 发送到 Service 的所有请求数据包都被传递到 kube-proxy。kube-proxy 收到的请求数据包的每个目标 Port 映射一个服务。因此,kube-proxy 可以通过重定向和 NAT 请求数据包的 目标 Port 确定请求数据包应该传递到哪个服务。kube-proxy 通过将接收到的请求数据包均匀地负载均衡到属于请求数据包要传输到的服务的多个 Pod 来重新传输接收到的请求数据包。
由于 kube-proxy 运行在主机的 Network Namespace 中,因此 kube-proxy 发送的请求包也会经过 Service NAT 表。但是由于kube-proxy发送的请求包的目标 IP是Pod的IP,所以请求包不会被Service NAT 表改变,而是通过CNI Plugin搭建的Container Network传递给Pod。











评论