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
。
评论