本篇文章发布于cylon的收藏册,转载请声明出处哦~
我们可以看到,kube-proxy 有一个 –cluster-cidr 的参数,我们就来解开这个参数究竟有没有用
$ kube-proxy -h|grep cidr
--cluster-cidr string The CIDR range of pods in the cluster. When configured, traffic sent to a Service cluster IP from outside this range will be masqueraded and traffic sent from pods to an external LoadBalancer IP will be directed to the respective cluster IP instead
可以看到,参数说明是说,如果配置,那么从外部发往 Service Cluster IP 的流量将被伪装,从 Pod 发往外部 LB 将被直接发往对应的 cluster IP。但实际上做了什么并不知道,那么就从源码解决这个问题。
首先我们知道,参数是作为 kube-proxy server 的参数,位于 cmd/kube-proxy 下,而对应的逻辑则位于 pkg/kube-proxy 下,参数很明显,就是 clusterCIDR,那么我们就寻找这个参数的调用即可。
在 API KubeProxyConfiguration 中我们找到的对应的 ClusterCIDR ,在这里的注释又变为 ”用于桥接集群外部流量“。这里涉及到关于 kube-proxy 的两个模式 “LocalMode” 和 “ProxyMode“。
而参数 –cluster-cidr 是作为选择使用的 “本地网络检测器” (Local Network Detector),这里起到的作用就是 “将集群外部的流量伪装成 service VIP” ,从代码中我们可以看到 Detector 将决定了你使用的是什么网络,无论是 LocalMode 还是 ProxyMode。
在代码 cmd/kube-proxy/app/server_others.go 中可以看到是如何选择的 LocalMode 方式,可以看出在存在三种模式:
func getLocalDetector(mode proxyconfigapi.LocalMode, config *proxyconfigapi.KubeProxyConfiguration, ipt utiliptables.Interface, nodeInfo *v1.Node) (proxyutiliptables.LocalTrafficDetector, error) {
switch mode {
case proxyconfigapi.LocalModeClusterCIDR:
if len(strings.TrimSpace(config.ClusterCIDR)) == 0 {
klog.Warning("detect-local-mode set to ClusterCIDR, but no cluster CIDR defined")
break
}
return proxyutiliptables.NewDetectLocalByCIDR(config.ClusterCIDR, ipt)
case proxyconfigapi.LocalModeNodeCIDR:
if len(strings.TrimSpace(nodeInfo.Spec.PodCIDR)) == 0 {
klog.Warning("detect-local-mode set to NodeCIDR, but no PodCIDR defined at node")
break
}
return proxyutiliptables.NewDetectLocalByCIDR(nodeInfo.Spec.PodCIDR, ipt)
}
klog.V(0).Info("detect-local-mode: ", string(mode), " , defaulting to no-op detect-local")
return proxyutiliptables.NewNoOpLocalDetector(), nil
}
这里我们以 IPVS 为例,如果开启了 localDetector 在 这个 ipvs proxier 中做了什么? 在代码 pkg/proxy/ipvs/proxier.go 可以看到
if !proxier.ipsetList[kubeClusterIPSet].isEmpty() {
args = append(args[:0],
"-A", string(kubeServicesChain),
"-m", "comment", "--comment", proxier.ipsetList[kubeClusterIPSet].getComment(),
"-m", "set", "--match-set", proxier.ipsetList[kubeClusterIPSet].Name,
)
if proxier.masqueradeAll {
writeLine(proxier.natRules, append(args, "dst,dst", "-j", string(KubeMarkMasqChain))...)
} else if proxier.localDetector.IsImplemented() {
// This masquerades off-cluster traffic to a service VIP. The idea
// is that you can establish a static route for your Service range,
// routing to any node, and that node will bridge into the Service
// for you. Since that might bounce off-node, we masquerade here.
// If/when we support "Local" policy for VIPs, we should update this.
writeLine(proxier.natRules, proxier.localDetector.JumpIfNotLocal(append(args, "dst,dst"), string(KubeMarkMasqChain))...)
} else {
// Masquerade all OUTPUT traffic coming from a service ip.
// The kube dummy interface has all service VIPs assigned which
// results in the service VIP being picked as the source IP to reach
// a VIP. This leads to a connection from VIP: to
// VIP:.
// Always masquerading OUTPUT (node-originating) traffic with a VIP
// source ip and service port destination fixes the outgoing connections.
writeLine(proxier.natRules, append(args, "src,dst", "-j", string(KubeMarkMasqChain))...)
}
}
可以看到“不管使用了什么模式,都会更新一条 iptables 规则” 这就代表了使用了什么模式,而这个则被称之为 LocalTrafficDetector,也就是本地流量的检测,那我们看一下这个做了什么。
在使用 IPVS 的日志中,可以看到这样一条规则,这个是来自集群外部的 IP 去访问集群 CLUSTER IP (KUBE-CLUSTER-IP,即集群内所有 service IP) 时, 将非集群 IP 地址,转换为集群内的 IP 地址 (做源地址转换)
[DetectLocalByCIDR (10.244.0.0/16)] Jump Not Local: [-A KUBE-SERVICES -m comment --comment "Kubernetes service cluster ip + port for masquerade purpose" -m set --match-set KUBE-CLUSTER-IP dst,dst ! -s 10.244.0.0/16 -j KUBE-MARK-MASQ]
而这个步骤分布在所有模式下 (iptables&ipvs),这里还是没说到两个概念 LocalMode 和 ProxyMode,实际上这两个模式的区别为:
kube-proxy 为 kube node 上生成一些 NAT 规则,如下所示
-A KUBE-FIREWALL -j KUBE-MARK-DROP
-A KUBE-LOAD-BALANCER -j KUBE-MARK-MASQ
-A KUBE-MARK-MASQ -j MARK --set-xmark 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-POSTROUTING -m comment --comment "Kubernetes endpoints dst ip:port, source ip for solving hairpin purpose" -m set --match-set KUBE-LOOP-BACK dst,dst,src -j MASQUERADE
-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
-A KUBE-SERVICES ! -s 10.244.0.0/16 -m comment --comment "Kubernetes service cluster ip + port for masquerade purpose" -m set --match-set KUBE-CLUSTER-IP dst,dst -j KUBE-MARK-MASQ
-A KUBE-SERVICES -m addrtype --dst-type LOCAL -j KUBE-NODE-PORT
-A KUBE-SERVICES -m set --match-set KUBE-CLUSTER-IP dst,dst -j ACCEPT
可以看到这里做了几个链,在 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
} else if proxier.localDetector.IsImplemented() {
// This masquerades off-cluster traffic to a service VIP. The idea
// is that you can establish a static route for your Service range,
// routing to any node, and that node will bridge into the Service
// for you. Since that might bounce off-node, we masquerade here.
// If/when we support "Local" policy for VIPs, we should update this.
writeLine(proxier.natRules, proxier.localDetector.JumpIfNotLocal(append(args, "dst,dst"), string(KubeMarkMasqChain))...)
// KubeMarkMasqChain is the mark-for-masquerade chain
KubeMarkMasqChain utiliptables.Chain = "KUBE-MARK-MASQ"
// 具体拼接的就是 -j 链名的操作
func (d *detectLocalByCIDR) JumpIfNotLocal(args []string, toChain string) []string {
line := append(args, "!", "-s", d.cidr, "-j", toChain)
klog.V(4).Info("[DetectLocalByCIDR (", d.cidr, ")]", " Jump Not Local: ", line)
return line
}
继续往下 KUBE-POSTROUTING 可以看到对应伪装是一个动态的源地址改造,而 RETURN 则不是被标记的请求
Chain KUBE-POSTROUTING (1 references)
target prot opt source destination
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 /* Kubernetes endpoints dst ip:port, source ip for solving hairpin purpose */ match-set KUBE-LOOP-BACK dst,dst,src
RETURN all -- 0.0.0.0/0 0.0.0.0/0 mark match ! 0x4000/0x4000
MARK all -- 0.0.0.0/0 0.0.0.0/0 MARK xor 0x4000
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */
这整体就是 ClusterCIDR 在 kube-proxy 中的应用,换句话说还需要关注一个 LocalCIDR