深入理解Kubernetes中Service的实现
iptables 是 Kubernetes 中 Service 的默认实现,了解 iptables 是理解 Service 工作原理不可缺少的一步。iptables 的构成在对包处理的过程中,内核添加了对包自定义处理的操作,这些自定义操作以链的形式存在,而链由表组成;每条链由不同的表组成,每个表由若
iptables 是 Kubernetes 中 Service 的默认实现,了解 iptables 是理解 Service 工作原理不可缺少的一步。
iptables 的构成
在对包处理的过程中,内核添加了对包自定义处理的操作,这些自定义操作以链的形式存在,而链由表组成;
每条链由不同的表组成,每个表由若干条规则构成;
每条链中同名的表相互独立,表名用来区分规则的作用。
5 条链
PREROUTING 接收包时执行的第一个链,执行完后进行路由选择
INPUT 路由选择后发现是本机的包时,会执行该链
FORWARD 接收到数据包,经 PREROUTING 处理并进行路由选择后,发现非本机数据包,进行转发处理时,执行此链
OUTPUT 发送数据包,进行路由选择之后,遇到的第一个链
POSTROUTING 紧跟 OUTPUT 链执行
4 张表
raw目的:将命中规则的包,跳过其它表的处理,它的优先级最高。 位置:由于其目的是跳过其它表,所以只需要在接收和发送两大过程的最开头处把关,所以只需要用到 PREROUTING 和 OUTPUT 两个钩子。
mangle目的:根据规则修改数据包的一些标志位,比如 TTL 位置:有可能会在任意位置都有可能会修改网络包,所以它是用到了全部的钩子位置。
nat目的:实现网络地址转换 位置:分为 SNAT(Source NAT)和 DNAT(Destination NAT)两种,可能会工作在 PREROUTING、INPUT、OUTPUT、POSTROUTING 四个位置。
filter目的:过滤某些包,这是防火墙工作的基础 位置:只在 INPUT、OUTPUT 和 FORWARD 这三步中工作就够了
链表之间的关系

iptables 的工作场景
接收网络包
PREROUTING链 -> 路由判断(是本机)-> INPUT链 -> ...
转发网络包
PREROUTING链 -> 路由判断(不是本设备,找到下一跳) -> FORWARD链 -> POSTROUTING链 -> ...
发送网络包
路由选择 -> OUTPUT链 -> POSTROUTING链 -> ...
iptables 的工作流程与契机
简化的包处理流程
数据接收过程走的是 1 和 2
发送过程走的是 4 、5
转发过程是 1、3、5

完整的包处理流程
包处理的流程图

iptables 的常用命令
常用参数
源地址:-s 192.168.1.0/24
目标地址:-d 192.168.1.11
协议:-p tcp|udp|icmp
从哪个网卡进来:-i eth0|lo
从哪个网卡出去:-o eth0|lo
目标端口(必须制定协议):-p tcp|udp --dport 8080
源端口(必须制定协议):-p tcp|udp --sport 8080
iptables
iptables -t ${表名} ${Commands} ${链名} ${链中的规则号} ${匹配条件} ${目标动作}
表名:4张表,filter、nat、mangle、raw
Commands:尾部追加-A、检查-C、删除-D、头部插入-I、替换-R、查看全部-L、清空-F、新建-N、默认规则-P(默认为ACCEPT)
链名:5条链,PREROUTING、INPUT、FORWOARD、OUTPUT、POSTROUTING
匹配条件:-p协议、-4 IPv4协议包 、-6 IPv6 协议包、-s 源地址、-d 目标地址、-i/-o 进/出网络接口名、--dport/--sport 目标/源端口
目标动作:拒绝访问-j REJECT、允许通过-j ACCEPT、丢弃-j DROP、记录日志 -j LOG、源地址转换-j snat、目标地址转换-j dnat、还有RETURN、QUEUE
示例:
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 ! -o br0 -j MASQUERADE
SNAT 源地址替换
iptables -t nat -A PREROUTING ! -i br0 -p tcp -m tcp --dport 8088 -j DNAT --to-destination 192.168.0.2:80
DNAT 目的地址转换
iptables -I INPUT -s 121.0.0.0/8 -j DROP
封禁
iptables -I INPUT -s 121.0.0.0/8 -j DROP
解封
iptables -t filter -I INPUT -s 1.2.3.4 -p tcp --dport 22 -j ACCEPT
iptables -t filter -I INPUT -p tcp --dport 22 -j DROP
Kubernetes Service 的原理
相关网段分别为
- Pod CIDR:
10.244.0.0/16 - Service CIDR:
10.96.0.0/12
创建一个测试 Deployment 和 Service,
# 镜像为 ARM 架构
cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: http-server
name: http-server
spec:
replicas: 3
selector:
matchLabels:
app: http-server
template:
metadata:
labels:
app: http-server
spec:
containers:
- image: ghcr.io/joengjyu/simple-http-server:20221009
name: simple-http-server
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
labels:
app: http-server
name: http-svc
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: http-server
---
apiVersion: v1
kind: Service
metadata:
labels:
app: http-server
name: http-svc-node-port
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: http-server
EOF
结果如下
$ kubectl get pod,svc
NAME READY STATUS RESTARTS AGE IP
pod/http-server-7b59bbfffc-bxcvs 1/1 Running 0 3m12s 10.244.2.9
pod/http-server-7b59bbfffc-f7cgt 1/1 Running 0 3m12s 10.244.1.7
pod/http-server-7b59bbfffc-wpfb2 1/1 Running 0 3m12s 10.244.2.8
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
service/http-svc ClusterIP 10.107.38.142 <none> 80/TCP
service/http-svc-node-port NodePort 10.108.51.223 <none> 80:32054/TCP
然后通过 iptables-save 命令得到所有链、表上的规则,相关规则可概括为:
KUBE-SERVICES 或者 KUBE-NODEPORTS 规则对应的 Service 的⼊⼝链,这个规则应该与 VIP 和 Service 端⼝⼀⼀对应;
KUBE-SEP-(hash) 规则对应的 DNAT 链,这些规则应该与 Endpoints ⼀⼀对应;
KUBE-SVC-(hash) 规则对应的负载均衡链,这些规则的数⽬应该与 Endpoints 数⽬⼀致;
如果是 NodePort 模式的话,还有 POSTROUTING 处的 SNAT 链。
Cluster IP 类型
其中与 Service 相关的规则如下:
PREROUTING和OUTPUT链中的 NAT 表中其他规则处理完之后,跳转到自定义链KUBE-SERVICES上;
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
- 自定义链
KUBE-SERVICES上有对 Servicehttp-svc的 Cluster IP 做处理,如果目标 IP 是10.107.38.142/32且端口为80,则交给自定义链KUBE-SVC-GM62ZXCOIF6ZCRWU处理;
-A KUBE-SERVICES -d 10.107.38.142/32 -p tcp -m comment --comment "default/http-svc cluster IP" -m tcp --dport 80 -j KUBE-SVC-GM62ZXCOIF6ZCRWU
-A KUBE-SERVICES -d 10.108.51.223/32 -p tcp -m comment --comment "default/http-svc-node-port cluster IP" -m tcp --dport 80 -j KUBE-SVC-C3VEVZBK3X2BH5IZ
- 由于存在两个 Pod,因此在负载均衡链
KUBE-SVC-ZV76JU53N5UCJJE6上可以看到两条主要的规则,其中第一条以30%的概率执行,第二条是50%的概率执行,第三条100%执行(由于这两条 iptables 规则存在先后顺序,且如果第一条执行后,第二条就不会执行,依次类推)。
-A KUBE-SVC-GM62ZXCOIF6ZCRWU ! -s 10.244.0.0/16 -d 10.107.38.142/32 -p tcp -m comment --comment "default/http-svc cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SVC-GM62ZXCOIF6ZCRWU -m comment --comment "default/http-svc -> 10.244.1.7:80" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-C6IB5RPVIEGEDGUL
-A KUBE-SVC-GM62ZXCOIF6ZCRWU -m comment --comment "default/http-svc -> 10.244.2.8:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-NVFETS636V4CPTUN
-A KUBE-SVC-GM62ZXCOIF6ZCRWU -m comment --comment "default/http-svc -> 10.244.2.9:80" -j KUBE-SEP-IHI45R5HV5QHYZU6
- 在 DNAT 链上,可以看到将目标地址换成了对应 Pod 的 IP 地址。
-A KUBE-SEP-C6IB5RPVIEGEDGUL -s 10.244.1.7/32 -m comment --comment "default/http-svc" -j KUBE-MARK-MASQ
-A KUBE-SEP-C6IB5RPVIEGEDGUL -p tcp -m comment --comment "default/http-svc" -m tcp -j DNAT --to-destination 10.244.1.7:80
-A KUBE-SEP-NVFETS636V4CPTUN -s 10.244.2.8/32 -m comment --comment "default/http-svc" -j KUBE-MARK-MASQ
-A KUBE-SEP-NVFETS636V4CPTUN -p tcp -m comment --comment "default/http-svc" -m tcp -j DNAT --to-destination 10.244.2.8:80
-A KUBE-SEP-IHI45R5HV5QHYZU6 -s 10.244.2.9/32 -m comment --comment "default/http-svc" -j KUBE-MARK-MASQ
-A KUBE-SEP-IHI45R5HV5QHYZU6 -p tcp -m comment --comment "default/http-svc" -m tcp -j DNAT --to-destination 10.244.2.9:80
NodePort 类型
跳转到 NodePort 链 KUBE-NODEPORTS 的时机:
- 本机从网卡中接受数据包
- 做为
KUBE-SERVICES链条的守门员
-A INPUT -m comment --comment "kubernetes health check service ports" -j KUBE-NODEPORTS
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
KUBE-NODEPORTS 链条详情
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/http-svc-node-port" -m tcp --dport 32054 -j KUBE-EXT-C3VEVZBK3X2BH5IZ
-A KUBE-EXT-C3VEVZBK3X2BH5IZ -m comment --comment "masquerade traffic for default/http-svc-node-port external destinations" -j KUBE-MARK-MASQ
-A KUBE-EXT-C3VEVZBK3X2BH5IZ -j KUBE-SVC-C3VEVZBK3X2BH5IZ
跳转到 KUBE-SVC-C3VEVZBK3X2BH5IZ 链后,接下来的处理方式与 http-svc-node-port 的 ClusterIP 方式一致。