CNI 网络流量 5.4 Cilium 流量分析(三) -- k8s services

关闭 kube-proxy
Cilium cm

kube-proxy-replacement: strict

可以看到 EnableSocketLB 等都为 True,在 ebpf 中 ENABLE_SOCKET_LB_TCP 和 ENABLE_SOCKET_LB_UDP 都是打开的状态。

    if option.Config.KubeProxyReplacement == option.KubeProxyReplacementProbe ||
        option.Config.KubeProxyReplacement == option.KubeProxyReplacementStrict {

        log.Infof("Trying to auto-enable %q, %q, %q, %q, %q features",
            option.EnableNodePort, option.EnableExternalIPs,
            option.EnableSocketLB, option.EnableHostPort,
            option.EnableSessionAffinity)

        option.Config.EnableHostPort = true
        option.Config.EnableNodePort = true
        option.Config.EnableExternalIPs = true
        option.Config.EnableSocketLB = true
        option.Config.EnableHostServicesTCP = true
        option.Config.EnableHostServicesUDP = true
        option.Config.EnableSessionAffinity = true
    }

控制面 与 services

  1. Cilium-agent deamon 运行 k8sWatcher,其中有 K8sServiceHandler,此 K8sServiceHandler 接收 service event 进行业务处理
d.k8sWatcher.RunK8sServiceHandler()

func (k *K8sWatcher) RunK8sServiceHandler() {
    go k.k8sServiceHandler()
}
  1. 有以下内容会触发 service event
  • UpdateService
  • EnsureService // policyBackend 变化时
  • DeleteService
  • updateEndpoints
  • deleteEndpoints
  • MergeExternalServiceUpdate
  • MergeExternalServiceDelete
  • MergeClusterServiceDelete
  • updateSelfNodeLabels
  1. 处理 services,将 k8s.service 转换格式,并存到 lbmap,如果是 nodePort,是单独的一条 lb
s.upsertServiceIntoLBMaps(svc, onlyLocalBackends, prevBackendCount,
        newBackends, obsoleteBackendIDs, prevSessionAffinity, prevLoadBalancerSourceRanges,
        obsoleteSVCBackendIDs, scopedLog)

$ cilium    bpf  lb list
SERVICE ADDRESS      BACKEND ADDRESS (REVNAT_ID) (SLOT)
10.96.0.10:53        10.0.1.230:53 (2) (2)                         
                     0.0.0.0:0 (2) (0) [ClusterIP, non-routable]   
                     10.0.2.15:53 (2) (1)                          
10.99.181.236:8080   10.0.0.65:80 (4) (1)                          
                     0.0.0.0:0 (4) (0) [ClusterIP, non-routable]   
                     10.0.0.225:80 (4) (2)                         
                     10.0.1.109:80 (4) (3)                         
10.96.0.1:443        172.18.22.111:6443 (1) (1)                    
                     0.0.0.0:0 (1) (0) [ClusterIP, non-routable]   
10.96.0.10:9153      10.0.1.230:9153 (3) (2)                       
                     0.0.0.0:0 (3) (0) [ClusterIP, non-routable]   
                     10.0.2.15:9153 (3) (1) 

host-Reachable Services

根据上述配置,介绍下 cilium 支持的 host-Reachable Services。该功能依赖 内核 5.9
如下图所示,在 pod1 访问 services 时,通过查看目的 ip,发现是 service 后,直接选择一个 backend,实际上省略了 dnat 流程。
[图片]

Host-reachable service 可以简单理解为集群内 service 是应该访问到的,通过 给 socket 挂 ebpf 实现直接访问到 endpoint 的能力。

一个应用的访问大致流程:

  • socket:创建一个新的套接字;
  • setsockopt:设置套接字选项;
  • bind:将套接字绑定到本地 IP 地址和端口;
  • listen:监听传入的连接请求;
  • accept:接受传入的连接请求,并返回新的已连接套接字;
  • getpeername:获取已连接套接字的对端 IP 地址和端口;
  • recvmsg:从已连接套接字接收数据;
  • post_bind4:在已连接套接字上执行一些后续的操作;
  • sendmsg:将数据发送到已连接的套接字;
  • close:关闭套接字。

在 cilium 中有以下挂载点
__section(“cgroup/connect4”),__section(“cgroup/post_bind4”),__section(“cgroup/bind4”),__section(“cgroup/sendmsg4”),__section(“cgroup/getpeername4”),__section(“sockops”)

pod 访问 service 时

__section("cgroup/connect4")
|- sock4_connect
  |- __sock4_xlate_fwd
    |- lb4_key              生成 lb4_key,根据目的地址和 目的 port
    |- lb4_lookup_service   使用 lb4_key 在 LB4_SERVICES_MAP_V2 查找 service
    |- lb4_affinity_backend_id_by_netns 判断是否是 affinity svc
    |- __lb4_lookup_backend  指定一个 endpoint
    |-> ctx->user_ip4 = backend->address;   将 endpoint 的 ip + port 设置到 socket 中
    |-> ctx_set_port(ctx, backend->port);

Socket 连接时,直接连 endpoint 的 ip + port,没有 nat 等操作了

DSR services

当通过 nodePort 或 ExternalIP 访问集群服务时,如果 后端 pod 不在访问的 节点,那么通常会 snat 成 访问节点默认路由网卡的 ip,然后转发到 pod 所在节点;pod 回复时回复到之前的访问节点,再 nat 成访问节点的回复报文;
这样通常有一个问题,即 pod 收到的报文源 IP 不是真正的 clientIP,这通常对一些需要通过访问者 IP 有需求的应用有一定的影响;虽然 k8s 提供 externalTrafficPolicy=Local 保持 clinetIP,但是这只能当访问后端在 访问节点时有效。
在这里插入图片描述

测试环境
clientIP: 172.20.150.110
node1: 172.18.22.111; node2: 172.18.22.112
svc: nginx-service   NodePort    10.99.181.236   <none>        8080:32334/TCP   6d1h
pod 在 node1 上 10.0.0.48

当发送请求到 node1 时 curl 172.18.22.111:32334
pod 抓包
172.20.150.110.36800 > 10.0.0.225.http
10.0.0.225.http > 172.20.150.110.36800

当发送请求到 node2 时 curl 172.18.22.112:32334
172.18.22.112.55864 > 10.0.0.225.http          // 非真实clientIP,而是 node2 ip
10.0.0.225.http > 172.18.22.112.55864

[图片]

修改 cilium 配置
bpf-lb-mode: dsr

当发送请求到 node2 时 curl 172.18.22.112:32334
172.20.150.110.41534 > 10.0.0.225.http          // 真实clientIP
172.20.150.110.41534 > 10.0.0.225.http
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
multus-cni 是一个 Kubernetes 的多网络 CNI 插件,它是 Kubernetes 上负责多网络管理的代表性插件之一。它允许 Kubernetes 集群中的每个 Pod 拥有多个网络接口,并能针对每个网络接口对应的网络策略和路由进行不同的配置。 multus-cni 的源码主要分为三部分:CNI 插件相关代码、配置文件相关代码和网络资源相关代码。CNI 插件相关代码包括主程序 main.go、CNI 配置解析器 conf.go、IPAM 相关代码和网络审计相关代码。配置文件相关代码包括 multus.conf 和各种 JSON/YAML 配置文件的解析器。网络资源相关代码主要负责通过 Kubernetes API 获取和管理 Pod、NetworkAttachmentDefinition 和 Service 等网络资源信息。 multus-cni 的核心是 CNI 插件相关代码中的 main.go,它主要负责 CNI 插件的初始化和执行。CNI 插件的执行流程大概可以总结为如下三步:首先,multus-cni 解析 CNI 配置文件并获取 Pod 相关的网络资源信息;接着,multus-cni 调用下层 CNI 插件(比如 flannel、calico、ovs 等)完成网络接口的创建和配置;最后,multus-cni 继续执行其他 CNI 插件(比如 ipvlan、macvlan、bridge 等)完成其他网络接口的创建和配置。 此外,multus-cni 通过 Kubernetes API 获取和管理 NetworkAttachmentDefinition 和 Service 等网络资源信息。在 Kubernetes 中,NetworkAttachmentDefinition 用于定义和配置网络接口,而 Service 用于定义和管理 Kubernetes 集群中的服务。multus-cni 通过获取、解析和应用这些网络资源信息,实现了多网络的管理和配置。 总的来说,multus-cni 是一个非常优秀的多网络 CNI 插件,它利用 Kubernetes API 实现了多网络的管理和配置,并同时支持插件化扩展。它的源码比较清晰,适合对 Kubernetes 网络原理比较熟悉的开发者学习和探究。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值