• kube-proxy参数ClusterCIDR做什么


    > 本篇文章发布于[cylon的收藏册](https://cylonchau.github.io/),转载请声明出处哦~
    >

    我们可以看到,*kube-proxy* 有一个 *--cluster-cidr* 的参数,我们就来解开这个参数究竟有没有用

    ```bash
    $ 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“。

    - ***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](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)

    ```go
    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](pkg/proxy/ipvs/proxier.go) 可以看到

    ```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 地址 (做源地址转换)

    ```bash
    [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***,实际上这两个模式的区别为:

    - **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 规则,如下所示

    ```bash
    -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*

    ```go
    } 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* 则不是被标记的请求

    ```go
    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

    ## 本篇文章发布于[cylon的收藏册](https://cylonchau.github.io/),转载请声明出处哦~

  • 相关阅读:
    去除 Zotero + Obsidian 复制粘贴参考文献表时的多余空行(ctrl+shift+C)
    【C语言刷LeetCode】378. 有序矩阵中第 K 小的元素(M)
    三、equals重写规范
    从头训练RNN语言模型,这样的loss正常吗?
    Leetcode第142题—环形链表Ⅱ
    wpf devexpress添加TreeListControl到项目
    数仓4.0(可视化报表)
    Xmake v2.8.3 发布,改进 Wasm 并支持 Xmake 源码调试
    Fink--3、Flink运行时架构(并行度、算子链、任务槽、作业提交流程)
    集合框架的线程安全问题ArrayList、HashSet、HashMap
  • 原文地址:https://blog.csdn.net/sinat_24092079/article/details/134023263