我们可以看到,kube-proxy 有一个 –cluster-cidr 的参数,我们就来解开这个参数究竟有没有用
|
|
可以看到,参数说明是说,如果配置,那么从外部发往 Service Cluster IP 的流量将被伪装,从 Pod 发往外部 LB 将被直接发往对应的 cluster IP。但实际上做了什么并不知道,那么就从源码解决这个问题。
首先我们知道,参数是作为 kube-proxy server 的参数,位于 cmd/kube-proxy 下,而对应的逻辑则位于 pkg/kube-proxy 下,参数很明显,就是 clusterCIDR,那么我们就寻找这个参数的调用即可。
在 API KubeProxyConfiguration 中我们找到的对应的 ClusterCIDR ,在这里的注释又变为 ”用于桥接集群外部流量“。这里涉及到关于 kube-proxy 的两个模式 “LocalMode” 和 “ProxyMode“。
- LocalMode:表示是来自节点本地流量的模式,包含 ClusterCIDR, NodeCIDR
- ProxyMode:就是 kube-proxy 最常用的模式,包含 iptables, IPVS, user namespace, kernelspace
而参数 –cluster-cidr 是作为选择使用的 “本地网络检测器” (Local Network Detector),这里起到的作用就是 “将集群外部的流量伪装成 service VIP” ,从代码中我们可以看到 Detector 将决定了你使用的是什么网络,无论是 LocalMode 还是 ProxyMode。
在代码 cmd/kube-proxy/app/server_others.go 中可以看到是如何选择的 LocalMode 方式,可以看出在存在三种模式:
- 没有配置 –cluster-cidr 则会返回一个 NoOpLocalDetector;
- 在配置了 –cluster-cidr ,则将会使用 CIDR 的本地模式;
- 如果 –cluster-cidr 没有配置,但配置了 LocalModeNodeCIDR,则会设置为 CNI 为该 Node 配置的 POD CIDR 的地址 (使用参数 –proxy-mode 指定的模式,如果为空,那么会检测对应操作系统默认 Linux 为 iptables,如果内核开启 IPVS 那么则使用 IPVS,windows 默认为 kernelspace)
|
|
这里我们以 IPVS 为例,如果开启了 localDetector 在 这个 ipvs proxier 中做了什么? 在代码 pkg/proxy/ipvs/proxier.go 可以看到
|
|
可以看到“不管使用了什么模式,都会更新一条 iptables 规则” 这就代表了使用了什么模式,而这个则被称之为 LocalTrafficDetector,也就是本地流量的检测,那我们看一下这个做了什么。
在使用 IPVS 的日志中,可以看到这样一条规则,这个是来自集群外部的 IP 去访问集群 CLUSTER IP (KUBE-CLUSTER-IP,即集群内所有 service IP) 时, 将非集群 IP 地址,转换为集群内的 IP 地址 (做源地址转换)
|
|
而这个步骤分布在所有模式下 (iptables&ipvs),这里还是没说到两个概念 LocalMode 和 ProxyMode,实际上这两个模式的区别为:
- LocalMode:集群 IP 伪装采用 ClusterCIDR 还是 NodeCIDR,ClusterCIDR 是使用集群 Pod IP 的地址段 (IP Range),而 LocalCIDR 只仅仅使用被分配给该 kubernetes node 上的 Pod 做地址伪装
- ProxyMode:和 LocalMode 没有任何关系,是 kube-proxy 在运行时使用什么为集群 service 做代理,例如 iptables, ipvs ,而在这些模式下将采用什么 LocalMode 为集群外部地址作伪装,大概分为三种类型:
- 为来自集群外部地址 (cluster-off):所有非 Pod 地址的请求执行跳转 (KUBE-POSTROUTING)
- 没有操作 :在非 iptables/ipvs 模式下,不做伪装
- masqueradeAll:为所有访问 cluster ip 的地址做伪装
ClusterCIDR 原理
kube-proxy 为 kube node 上生成一些 NAT 规则,如下所示
|
|
可以看到这里做了几个链,在 KUBE-SERVICES 链中指明了非来自 ClusterCIDR 的 IP 都做一个,并且访问的目的地址是 KUBE-CLUSTER-IP (ipset 里配置的地址) 那么将跳转到 KUBE-MARK-MASQ 链做一个 --set-xmark 0x4000/0x4000
,而在 KUBE-POSTROUTING 中对没有被标记 0x4000/0x4000
的操作不做处理
具体来说,-A KUBE-NODE-PORT -p tcp -m comment --comment "Kubernetes nodeport TCP port for masquerade purpose" -m set --match-set KUBE-NODE-PORT-TCP dst -j KUBE-MARK-MASQ
做了如下操作:
-A KUBE-SERVICES
:将这条规则附加到名为KUBE-SERVICES
的iptables链。! -s 10.244.0.0/16
:排除源IP地址为10.244.0.0/16
的流量(即来自Kubernetes服务集群IP的流量)。-m comment --comment "Kubernetes service cluster ip + port for masquerade purpose"
:添加一条注释,说明这个规则的用途。-m set --match-set KUBE-CLUSTER-IP dst,dst
:使用IP集合KUBE-CLUSTER-IP
来匹配目标IP地址和目标端口。-j KUBE-MARK-MASQ
:如果流量匹配了前面的条件,将流量传递到名为KUBE-MARK-MASQ
的目标。
iptables -j RETURN
是用于iptables规则中的一个目标动作,它不是用于拒绝或接受数据包的动作,而是用于从当前规则链中返回(返回到调用链)的动作。具体来说,当规则链中的数据包被标记为
RETURN
时,它们将不再受到当前链中后续规则的影响,而会立即返回到调用链,以便继续进行后续规则的处理。这通常用于某些高级设置,例如在自定义规则链中执行特定的操作后返回到主要的防火墙链。
从代码中可以看到,对应执行 jump 的操作的链就是 KUBE-MARK-MASQ
|
|
继续往下 KUBE-POSTROUTING 可以看到对应伪装是一个动态的源地址改造,而 RETURN 则不是被标记的请求
|
|
这整体就是 ClusterCIDR 在 kube-proxy 中的应用,换句话说还需要关注一个 LocalCIDR