flannel默认使用8285端口作为UDP封装报文的端口,VxLan使用8472端口。
Cluster-IP 为何无法ping通:
- Cluster-IP是虚拟 IP,并非真实绑定到某个网络设备(如网卡),而是由 kube-proxy 通过 iptables/IPVS 规则(或者由Cilium通过eBPF程序)在内存中维护的抽象地址。
- 设计目的:提供稳定的内部访问端点,实现负载均衡和服务发现,不用于直接网络层通信(如 ICMP/ping)。
spec.clusterIP的值为None时,该service就是headless service:
- 无负载均衡:可以通过 DNS 查询到该service下所有 Pod 的 IP 列表,但是需要自行实现负载均衡。
- 和常规service的区别:常规 Service 经 DNS 解析后返回Cluster-IP,而headless service返回所有后端 Pod 的 IP。
- 通常和 StatefulSet 配合使用,StatefulSet有个spec.serviceName属性可以关联Headless Service,之后就可以使用
<pod-name>.<headless-service-name>.<namespace>.svc.cluster.local
这种格式的dns记录了。(默认情况下,pod是没有dns记录的)
宿主机防火墙不放行service网段通常不会有问题,除了下列场景:
- HostNetwork Pod 访问 Service:HostNetwork Pod 直接使用宿主机网络,若防火墙阻止 Service 网段,访问会失败。
- 宿主机直接访问 Cluster-IP:若宿主机直接命令行
curl <Cluster-IP>:<Port>
,流量会走宿主机网络,可能被防火墙阻断。通常无实际需求,可忽略。
k8s中,探测service连通性的方法:
1. ClusterIP
|
|
这些iptables规则是所有的机器都有,还是仅仅在pod所在的节点上采用?
- 如果仅仅在pod所在的节点上有,
- 如果在所有的节点上都有,而cluster-ip就是集群内的ip,service端口也是集群内端口,那么只要在集群内,都能访问
-A KUBE-SERVICES -d 172.30.229.88/32 -p tcp -m comment --comment "default/hostnames:default cluster IP" -m tcp --dport 80 -j KUBE-SVC-ODX2UBAZM7RQWOIU
这条iptables规则的含义是: 凡是目的地址是 172.30.229.88、目的端口是 80 的 IP 包,都应该跳转到另外一条名叫 KUBE-SVC-ODX2UBAZM7RQWOIU 的 iptables 链进行处理。
而KUBE-SVC-ODX2UBAZM7RQWOIU是一组随机模式(–mode random)的iptables链:
-A KUBE-SVC-ODX2UBAZM7RQWOIU -m comment --comment "default/hostnames:default" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-2UBEEDHLNQW4STS3
-A KUBE-SVC-ODX2UBAZM7RQWOIU -m comment --comment "default/hostnames:default" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-KC6PYBWMWF265T4F
-A KUBE-SVC-ODX2UBAZM7RQWOIU -m comment --comment "default/hostnames:default" -j KUBE-SEP-MBYAUMDXE6ZQXIYZ
而随机转发的目的地,分别是KUBE-SEP-2UBEEDHLNQW4STS3, KUBE-SEP-KC6PYBWMWF265T4F, KUBE-SEP-MBYAUMDXE6ZQXIYZ,
这三条链指向的最终目的地,其实就是这个Service代理的三个Pod,而且Service也是在这里实现的负载均衡。
需要注意的是,iptables 规则的匹配是从上到下逐条进行的,所以为了保证上述三条规则每条被选中的概率都相同,我们应该将它们的 probability 字段的值分别设置为 1/3(0.333…)、1/2 和 1。
通过查看上述三条链的明细,我们就很容易理解Service进行转发的具体原理了:
-A KUBE-SEP-2UBEEDHLNQW4STS3 -s 10.244.5.4/32 -m comment --comment "default/hostnames:default" -j KUBE-MARK-MASQ
-A KUBE-SEP-2UBEEDHLNQW4STS3 -p tcp -m comment --comment "default/hostnames:default" -m tcp -j DNAT --to-destination 10.244.5.4:9376
-A KUBE-SEP-KC6PYBWMWF265T4F -s 10.244.5.5/32 -m comment --comment "default/hostnames:default" -j KUBE-MARK-MASQ
-A KUBE-SEP-KC6PYBWMWF265T4F -p tcp -m comment --comment "default/hostnames:default" -m tcp -j DNAT --to-destination 10.244.5.5:9376
-A KUBE-SEP-MBYAUMDXE6ZQXIYZ -s 10.244.5.6/32 -m comment --comment "default/hostnames:default" -j KUBE-MARK-MASQ
-A KUBE-SEP-MBYAUMDXE6ZQXIYZ -p tcp -m comment --comment "default/hostnames:default" -m tcp -j DNAT --to-destination 10.244.5.6:9376
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
可以看到,这三条链,其实是三条 DNAT 规则。但在 DNAT 规则之前,iptables 对流入的 IP 包还设置了一个“标志”(–set-xmark)。
而 DNAT 规则的作用,就是在 PREROUTING 检查点之前,也就是在路由之前,将流入 IP 包的目的地址和端口,改成–to-destination 所指定的新的目的地址和端口。可以看到,这个目的地址和端口,正是被代理 Pod 的 IP 地址和端口。
2. NodePort: 从外部访问service的第一种方式
|
|
相对于ClusterIP,添加了这条iptables规则:
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/hostnames:default" -m tcp --dport 30080 -j KUBE-SVC-ODX2UBAZM7RQWOIU
而KUBE-SVC-ODX2UBAZM7RQWOIU是一组随机模式(–mode random)的iptables链,以实现service的负载均衡,和ClusterIP的一致。
需要注意的是,在NodePort方式下,Kubernetes会在IP包离开宿主机发往目的Pod时,对这个IP包做一次SNAT操作,如下所示:
-A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
可以看到,这条规则设置在 POSTROUTING 检查点,也就是说,它给即将离开这台主机的 IP 包,进行了一次 SNAT 操作,将这个 IP 包的源地址替换成了这台宿主机上的 CNI 网桥地址,或者宿主机本身的 IP 地址(如果 CNI 网桥不存在的话)。
当然,这个 SNAT 操作只需要对 Service 转发出来的 IP 包进行(否则普通的 IP 包就被影响了)。而 iptables 做这个判断的依据,就是查看该 IP 包是否有一个“0x4000”的“标志”。你应该还记得,这个标志正是在 IP 包被执行 DNAT 操作之前被打上去的。
可是,为什么一定要对流出的包做 SNAT操作呢? 这里的原理其实很简单,如下所示:
client
\ ^
\ \
v \
node 1 <--- node 2
| ^ SNAT
| | --->
v |
endpoint
当一个外部的 client 通过 node 2 的地址访问一个 Service 的时候,node 2 上的负载均衡规则,就可能把这个 IP 包转发给一个在 node 1 上的 Pod。这里没有任何问题。
而当 node 1 上的这个 Pod 处理完请求之后,它就会按照这个 IP 包的源地址发出回复。
- 可是,如果没有做 SNAT 操作的话,这时候,被转发来的 IP 包的源地址就是 client 的 IP 地址。
- 所以此时,Pod 就会直接将回复发给client。
- 对于 client 来说,它的请求明明发给了 node 2,收到的回复却来自 node 1,这个 client 很可能会报错。
- 在上图中,当 IP 包离开 node 2 之后,它的源 IP 地址就会被 SNAT 改成 node 2 的 CNI 网桥地址或者 node 2 自己的地址。
- 这样,Pod 在处理完成之后就会先回复给 node 2(而不是 client),然后再由 node 2 发送给 client。
- 当然,这也就意味着这个 Pod 只知道该 IP 包来自于 node 2,而不是外部的 client。
- 这对于 Pod 需要明确知道所有请求来源的场景来说,这是不可以的。
- 这个时候,需要将Service的spec.externalTrafficPolicy字段改为local:相当于关闭了节点转发的功能。
该service的iptables规则,会设置为只将 IP 包转发给运行在这台宿主机上的 Pod: client ^ / \ / / \ / v X node 1 node 2 ^ | | | | v endpoint
- 部署nginx的时候,使用DaemonSet来部署;部署nginx service的时候,将spec.externalTrafficPolicy字段设置为local。
3. LoadBalancer:从外部访问service的第二种方式
在公有云提供的Kubernetes服务里,都使用了一个叫做CloudProvider的转接层,来跟公有云本身的API进行对接。
所以,LoadBalancer 类型的 Service 被提交后,Kubernetes 就会调用 CloudProvider 在公有云上为你创建一个负载均衡服务,并且把被代理的 Pod 的 IP 地址配置给负载均衡服务做后端。
这个时候与ClusterIP和NodePort相关的iptables规则就不被创建了。
本文发表于 0001-01-01,最后修改于 0001-01-01。
本站永久域名「 jiavvc.top 」,也可搜索「 后浪笔记一零二四 」找到我。