- 阅读本文档时,可能因为代理问题,无法正常查看截图
- kubernetes: v1.18.2
- cri: docker
- cni: flannel
- proxyMode: iptables
数据包在linux系统中,是绕不开 iptables 的,因为篇幅问题,本文不对 iptables 展开讨论,有兴趣的同学请移步 Iptables
ClusterIP
NodePort
LoadBalancer
ClusterIP
-
在 k8s 集群创建一个
ClusterIP
类型的service
, 其ip
为10.254.122.75
,port
为80
-
在集群内的任一节点执行
curl 10.254.122.75:80
,可以正常访问服务,通过kubectl get ep
获取对应的后端有若干个pod
实现流程如下
-
数据包首先进入 iptable 的
PREROUTING
链,依次进入raw
表、mangle
表、 和nat
表,根据上文总结,k8s 只操作nat
表,直接查看PREROUTING
链的nat
表: -
数据包命中,随后数据包进入
KUBE-SERVICES
链,先后命中KUBE-MARK-MASQ
和KUBE-SVC-4N57TFCL4MD7ZTDA
链 -
然后报文进入
KUBE-SVC-4N57TFCL4MD7ZTDA
链,并在其规则中完成loadbalance
, 本例中,报文随机命中KUBE-SEP-EZ6FKUNEIONYFN4Z
链 -
随后报文进入
KUBE-SEP-EZ6FKUNEIONYFN4Z
链,并在该链中完成dnat
,实现从service ip
到实际 endpointpod id
的转换,对应kubectl get ep
获取到的后端 -
完成
dnat
之后,报文完成所有PREROUTING
链,进入routering decision
阶段, 数据报文的源ip
为本地ip,目的ip 为 dnat 之后的 ip ,查看本地路由, 此时有两种情况,会在步骤 9 和 10 中分别讨论- 命中本节点,报文进入 cni0 设备
- 命中其他节点,报文进入 flannel.1 设备
-
路由命中本节点场景分析 (endpoint 为 172.30.0.35:80)
- 命中本地的数据包, 依次进入
INPUT
链的mangle
和filter
表,未命中任何规则,直接通过 - 数据包到达
cni0
设备,cni0
设备是一个linux bridge
,作为宿主机上所有pod
的网关 通过arp寻址
数据包顺利到达pod
pod
完成对数据包的处理- 数据包在处理完之后,进入
routering decision
阶段,路由后,依次进入OUTPUT
链的raw
,mangle
,nat
,filter
表,未命中任何规则,直接通过 - 数据包通过
OUTPUT
链后, 进行routering decision
,随后数据包进入POSTROUTING
链的mangle
和nat
表,mangle
表直接通过,数据包进入nat
表会命中KUBE-POSTROUTING
链,根据KUBE-MARK-MASQ
的MARK
完成MASQUERADE
. 至此,整个流程结束.
- 命中本地的数据包, 依次进入
-
路由命中其他节点场景分析 (endpoint 为 172.30.4.43:80) - 命中其他节点的数据包,将依次进入
FORWARD
链的mangle
和filter
表,命中filter
表的KUBE-FORWARD
链,完成FORWARD
. - 数据包进入routering decision
阶段的, 其目的ip
为172.30.1.43
, 根据路由,出口为flannel.1
下一条为172.30.4.0
- 随后数据包进入POSTROUTING
链的mangle
和nat
表,命中nat
表的KUBE-POSTROUTING
, 完成MASQUERADE
后,数据包离开本机,到达目的主机
- 无头服务(headless)在服务&外传区讨论,此处不做赘述
NodePort
-
创建一个
NodePort
类型的service
,访问集群的任一台节点的 ip + port(本例为31980)均可以正常连接 -
查看被访问的节点(或者是任意节点)的
iptables
规则,数据包先后进入PREROUTING
链的raw
表,mangle
表, 和nat
表(前文已介绍,raw 和 mangle 表均没有规则,默认通过),直接查看nat
表规则 -
和
ClusterIP
场景相关,数据包会命中PREROUTING
的KUBE-SERVICES
链,然后命中其子链KUBE-NODEPORTS
-
数据包进入
KUBE-NODEPORTS
后,根据dpt
先后命中KUBE-MARK-MASQ
(0x4000) 和KUBE-SVC-37ROJ3MK6RKFMQ2B
并在KUBE-SVC-37ROJ3MK6RKFMQ2B
链中完成负载均衡 (statistic mode random) -
随后数据包进入负载均衡之后的链中,本例是
KUBE-SEP-4XK7BREWKZE733EB
链,查看KUBE-SEP-4XK7BREWKZE733EB
规则,发现数据包命中dnat
规则,在此完成nodeport:port
到pod:port
的转换,数据包会根据dnat
后的目的ip
,匹配路由,到达后端pod
, 此时分两种情况,在 6 和 7 中分别讨论 -
pod
不在本节点,以172.30.1.53:80
为例,此时数据包已通过PREROUTING
链, 根据路由
,将从flannel.1
设备到达下一跳172.30.1.0
, 然后数据包进入FORWARD
链- 数据包依次通过
FORWARD
的mangle
和filter
表,命中filter
的KUBE-FORWARD
链的两条规则 (0x4000/0x4000), 直接通过FORWARD
链 - 数据包在通过
FORWARD
之后,未对数据包做修改,直接进入POSTROUTING
链 - 数据包进入
POSTROUTING
链的mangle
和nat
表, 命中nat
表的KUBE-POSTROUTING
链,完成MASQUERADE
, 进行snat
- 然后数据包命中
POSTROUTING
的RETURN
规则,数据包完成全部规则,即将离开本节点,去往下一跳, 此时数据包的源ip为本节点flannel.1
的地址(伪装), 目的地址为后端pod
地址 - 然后数据包被送到目的
pod
所在的节点,进入其PREROUTING
链, 依次通过raw
,mangle
,nat
表, 未命中任何规则,然后数据包进行路由,根据路由表,数据包会被送到cni0
设备, cni0
是本地设备,意味着数据包会进入INPUT
链,依次通过mangle
和filter
表, 未命中规则,然后进行Local Process
(处理过程忽略)Local Process
之后,数据包进入OUTPUT
链, 依次通过raw
,mangle
,nat
表,未命中规则- 然后数据包进入
route decsion
,目前数据包的源ip
是 pod 的 ip ,目的ip
是之前 node 节点的flannelf.1
的 ip, 根据路由,数据包通过flannelf.1
设备前往对端的flannelf.1
网关是172.30.0.0
- 然后数据包进入
POSTROUTING
链, 依次通过mangle
和nat
表, 命中nat
表的RETURN
, 完成目的节点
的所有操作, 根据路由,数据包被送会源节点
. - 数据包达到
源节点
(源ip
为pod ip
,目的ip
为172.30.0.0/32
), 依次通过PREROUTING
的raw
,mangle
和nat
表,未命中规则,进入route decsion
阶段,命中默认路由 - 随后数据包进入
FORWARD
链的mangle
和filter
表,命中KUBE-FORWARD
的规则,快速通过FORWARD
链 - 随后数据包进入
POSTROUTING
的mangle
和nat
表,命中nat
表的KUBE-POSTROUTING
链, 完成snat
(MASQUERADE) - 完成
POSTROUTING
链之后,数据包正式离开node
节点,返回给外端的请求端. 至此,整个流程结束.
- 数据包依次通过
-
pod
在本节点, 以172.30.3.2:80
为例, 此时数据包已通过PREROUTING
链- 根据路由,命中本地,数据包将被送到容器网关
cni0
设备 - 数据包依次进入
INPUT
链的mangle
和filter
表,未命中任何规则,直接通过 - 数据包在处理完之后,进入 routering decision 阶段,路由后,依次进入 OUTPUT 链的 raw, mangle, nat, filter 表,未命中任何规则,直接通过
- 数据包通过
OUTPUT
链后, 进行routering decision
,随后数据包进入POSTROUTING
链的mangle
和nat
表,mangle
表直接通过,数据包进入nat
表会命中KUBE-POSTROUTING
链,根据KUBE-MARK-MASQ
的 MARK 完成MASQUERADE
. 然后数据包
根据conntrack
离开宿主机. 至此,整个流程结束.
- 根据路由,命中本地,数据包将被送到容器网关
LoadBalancer
- 参考 NodePort 章节,LoadBalancer 和 NodePort 集群内实现原理大致相同,又因云厂商
provider
有所差异
ClusterIP(headless)
-
什么是无头服务?Headless Service
-
创建一个无头服务 —— 指定服务类型为
ClusterIP
, 且设置Spec.ClusterIP
为None
, 对于无头服务,kubernetes
不会为其分配ClusterIP
,kube-proxy
也不会处理(无规则下发),由dns
根据selector
自动配置转发:- 如果配置
selector
,DNS
直接指向后端pods
- 如果未配置
selector
,DNS
指向和service
名称相同的任意后端, 或 externalName 类型的服务
- 如果配置
-
综上所述,无头服务通过
dns
的能力实现loadbalance
,不经过kubernetes
转发实现机制,直接将请求转发到后端的pod
上
ExternalName
- 直接作用于域名(可以用作
kubernetes
的跨namespaces
服务访问)服务名
是namespaces
隔离的 (test-svc.default.svc.cluster.local)ClusterIP
是非namespaces
隔离的