• kube-proxy参数ClusterCIDR做什么


    本篇文章发布于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
    
    • 1
    • 2

    可以看到,参数说明是说,如果配置,那么从外部发往 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)
    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这里我们以 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))...)
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    可以看到“不管使用了什么模式,都会更新一条 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]
    
    • 1

    而这个步骤分布在所有模式下 (iptables&ipvs),这里还是没说到两个概念 LocalModeProxyMode,实际上这两个模式的区别为:

    • LocalMode:集群 IP 伪装采用 ClusterCIDR 还是 NodeCIDRClusterCIDR 是使用集群 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 规则,如下所示

    -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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可以看到这里做了几个链,在 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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    继续往下 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 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这整体就是 ClusterCIDR 在 kube-proxy 中的应用,换句话说还需要关注一个 LocalCIDR

    本篇文章发布于cylon的收藏册,转载请声明出处哦~

  • 相关阅读:
    VS+Qt+opencascade三维绘图stp/step/igs/stl格式图形读取显示
    关于博主的介绍
    办理的流量卡怎么判断是否激活成功?看实名认证还是看充值?
    (万文)最全、最细前端面试问题总结(答题思路分析、答案解析)
    JavaSE---ArrayList与顺序表
    大数据架构
    测试左移和测试右移,我们为何要“上下求索”?
    移动端异构运算技术 - GPU OpenCL 编程(基础篇)
    FFmpeg 从视频流中抽取图片
    嵌入式系统关于晶振的问题汇总
  • 原文地址:https://blog.csdn.net/sinat_24092079/article/details/134023290