kube-proxy插件和cilium-agent插件
kube-proxy和cilium-agent,都是以DaemonSet形式部署在每个节点上。
kube-proxy直接监听Kubernetes API Server中Service和EndpointSlice的变化,当有新的Service创建或者Endpoints发生变化时,kube-proxy会自动配置节点的iptables或ipvs规则来实现负载均衡。
kube-proxy直接监听Kubernetes API Server中Service和EndpointSlice的变化,当有新的Service创建或者Endpoints发生变化时,Cilium Agent 会编译和加载相应的 eBPF 程序到内核,处理实际的流量转发。
CoreDNS插件
CoreDNS采用模块化插件架构,kubernetes插件只是众多插件中的一个。
- 监听Kubernetes API
CoreDNS启动时会通过kubernetes插件连接到Kubernetes API Server,持续监听(watch)集群中Service的变化。
- 自动生成DNS记录
当Service创建或删除时,CoreDNS会自动在内存中生成对应的DNS记录。
例如,在default命名空间创建名为nginx的服务时,CoreDNS会创建一条将nginx.default.svc.cluster.local映射到该Service ClusterIP的A记录。
- 响应DNS查询
当Pod内的应用程序发起DNS查询时,CoreDNS会根据其内存中的DNS记录库进行解析,将服务名称转换为对应的ClusterIP地址。
这个过程完全由CoreDNS自身完成,不需要其他组件介入。
service
Service 是 Kubernetes 项目中用来将一组 Pod 暴露给外界访问的一种机制。那么,这个 Service 又是如何被访问的呢?
- 第一种方式,是以 Service 的 VIP(Virtual IP,即:虚拟 IP)方式。
- 比如:当我访问 10.0.23.1 这个 Service 的 IP 地址时,10.0.23.1 其实就是一个 VIP,它会把请求转发到该 Service 所代理的某一个 Pod 上。这里的具体原理,我会在后续的 Service 章节中进行详细介绍。
- 第二种方式,就是以 Service 的 DNS 方式。
- 比如:这时候,只要我访问“my-svc.my-namespace.svc.cluster.local”这条 DNS 记录,就可以访问到名叫 my-svc 的 Service 所代理的某一个 Pod。
Service DNS 的方式下,具体还可以分为两种处理方法:
- 第一种处理方法,是 Normal Service。
- 这种情况下,你访问“my-svc.my-namespace.svc.cluster.local”解析到的,正是 my-svc 这个 Service 的 VIP,后面的流程就跟 VIP 方式一致了。
- 而第二种处理方法,正是 Headless Service。
- 这种情况下,你访问“my-svc.my-namespace.svc.cluster.local”解析到的,直接就是 my-svc 代理的某一个 Pod 的 IP 地址。
- 可以看到,这里的区别在于,Headless Service 不需要分配一个 VIP,而是可以直接以 DNS 记录的方式解析出被代理 Pod 的 IP 地址。
找到容器不容易:Service、DNS与服务发现
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连通性的方法:
|
|
这些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 地址和端口。
从外界连通Service与Service调试“三板斧”
- 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。
- LoadBalancer:从外部访问service的第二种方式 在公有云提供的Kubernetes服务里,都使用了一个叫做CloudProvider的转接层,来跟公有云本身的API进行对接。
所以,LoadBalancer 类型的 Service 被提交后,Kubernetes 就会调用 CloudProvider 在公有云上为你创建一个负载均衡服务,并且把被代理的 Pod 的 IP 地址配置给负载均衡服务做后端。
这个时候与ClusterIP和NodePort相关的iptables规则就不被创建了。
本文发表于 0001-01-01,最后修改于 0001-01-01。
本站永久域名「 jiavvc.top 」,也可搜索「 极客油画 」找到我。

