美文网首页
Kubernetes网络

Kubernetes网络

作者: Tommy1989 | 来源:发表于2021-12-21 13:12 被阅读0次

    kube-proxy

    我们知道,Kubernetes中Pod的生命是短暂了,它随时可能被终止。即使使用了Deployment或者ReplicaSet保证Pod挂掉之后还会重启,但也没法保证重启后Pod的IP不变。从服务的高可用性与连续性的角度出发,我们不可能把Pod的IP直接暴露成service端口。因此我们需要一个更加可靠的“前端”去代理Pod,这就是k8s中的Service。引用官方文档的定义就是:

    Service 定义了这样一种抽象:逻辑上的一组 Pod,一种可以访问它们的策略 —— 通常称为微服务。 这一组 Pod 能够被 Service 访问到。

    也可以说,Service作为“前端”提供稳定的服务端口,Pod作为“后端”提供服务实现。Service会监控自己组内的Pod的运行状态,剔除终止的Pod,添加新增的Pod。配合k8s的服务发现机制,我们再也不用担心IP改变,Pod终止等问题了。kube-proxy则是实现Service的关键组件,到目前为止共有3种实现模式:userspaceiptables 或者 IPVS。其中userspace 模式非常陈旧、缓慢,已经不推荐使用。

    IP Tables模式

    iptables 是一个 Linux 内核功能,是一个高效的防火墙,并提供了大量的数据包处理和过滤方面的能力。它可以在核心数据包处理管线上用 Hook 挂接一系列的规则。在K8S中,kube-proxy 会把请求的代理转发规则全部写入iptable中,砍掉了kube-proxy转发的部分,整个过程发生在内核空间,提高了转发性能,但是iptable的规则是基于链表实现的,规则数量随着Service数量的增加线性增加,查找时间复杂度为O(n),也就是说,当Service数量到达一定量级时,CPU消耗和延迟将显著增加。

    IP VS模式

    IPVS 是一个用于负载均衡的 Linux 内核功能。IPVS 模式下,kube-proxy 使用 IPVS 负载均衡代替了 iptable。IPVS 的设计就是用来为大量服务进行负载均衡的,它有一套优化过的 API,使用优化的查找算法,而不是简单的从列表中查找规则。这样一来,kube-proxy 在 IPVS 模式下,其连接过程的复杂度为 O(1)。换句话说,多数情况下,他的连接处理效率是和集群规模无关的。另外作为一个独立的负载均衡器,IPVS 包含了多种不同的负载均衡算法,例如轮询、最短期望延迟、最少连接以及各种哈希方法等。而 iptables 就只有一种随机平等的选择算法。IPVS 的一个潜在缺点就是,IPVS 处理数据包的路径和通常情况下 iptables 过滤器的路径是不同的。

    通过下图,可以简单的看出Client pod 、kube proxy 、Pod之间的关系。Service的虚拟Ip会写到ip tables/ip vs中,被转发到真正的Pod地址。

    image.png

    IP Tables & IP VS 网络扭转


    image.png

    流程简述如下:

    1. kube-proxy DNAT + SNAT 用自己的 IP 替换源 IP ,用 Pod IP 替换掉目的 IP
    2. 数据包转发到目标 Pod
    3. Pod 将 Ingress Node 视为源,并作出响应
    4. 源 / 目的地址在 Ingress Node 替换为客户端地址(目的) ,服务地址(Ingress Node)

    性能对比

    image.png
    image.png

    总结下IPVS & IPTables

    1.在小规模的集群中,两者在CPU和响应时间上,差别不大,无论是keep alive 还是non keep alive 。

    2.在大规模集群中,两者会慢慢在响应时间以及CPU上都会出现区别,不过在使用keep alive的情况下,区别还是主要来源于CPU的使用。

    3.ipvs 基于散列表,复杂度 O(1),iptables 基于链表,复杂度 O(n)

    4.ipvs 支持多种负载均衡调度算法;iptables 只有由 statistic 模块的 DNAT 支持概率轮询。

    PS:要注意,在微服务的情况下,调用链会非常长,两者影响效果会更显著一些。

    eBPF

    什么是eBPF?

    Linux内核一直是实现监视/可观察性,网络和安全性的理想场所。不幸的是,这通常是不切实际的,因为它需要更改内核源代码或加载内核模块,并导致彼此堆叠的抽象层。 eBPF是一项革命性的技术,可以在Linux内核中运行沙盒程序,而无需更改内核源代码或加载内核模块。通过使Linux内核可编程,基础架构软件可以利用现有的层,从而使它们更加智能和功能丰富,而无需继续为系统增加额外的复杂性层。

    eBPF导致了网络,安全性,应用程序配置/跟踪和性能故障排除等领域的新一代工具的开发,这些工具不再依赖现有的内核功能,而是在不影响执行效率或安全性的情况下主动重新编程运行时行为。对于云原生领域,Cilium 已经使用eBPF 实现了无kube-proxy的容器网络。利用eBPF解决iptables带来的性能问题。

    通过的 eBPF 实现,可以保留原始源 IP,并且可以选择执行直接服务器返回 (DSR)。即返回流量可以选择最优路径,而无需通过原始入口节点环回,如下图:


    image.png

    简述流程如下:

    1. BPF program 将数据发送给 K8s service; 做负载均衡决策并将数据包发送给目的 pod 节点
    2. BPF program 程序将 DNAT 转换成 Pod 的IP
    3. Pod 看到客户端真正的 IP
    4. Pod 做出响应; BPF 反向 DNAT
    5. 如果网络准许,数据包直接返回。否则将通过 Ingress Node (ingress controller Pod 所在服务器)

    性能

    网络吞吐

    测试环境:两台物理节点,一个发包,一个收包,收到的包做 Service loadbalancing 转发给后端 Pods。


    image.png

    可以看出:

    1. Cilium XDP eBPF 模式能处理接收到的全部 10Mpps(packets per second)。
    2. Cilium tc eBPF 模式能处理 3.5Mpps。
    3. kube-proxy iptables 只能处理 2.3Mpps,因为它的 hook 点在收发包路径上更后面的位置。
    4. kube-proxy ipvs 模式这里表现更差,它相比 iptables 的优势要在 backend 数量很多的时候才能体现出来

    CPU 利用率

    测试生成了 1Mpps、2Mpps 和 4Mpps 流量,空闲 CPU 占比(可以被应用使用的 CPU)结果如下:


    image.png

    结论与上面吞吐类似。

    • XDP 性能最好,是因为 XDP BPF 在驱动层执行,不需要将包 push 到内核协议栈
    • kube-proxy 不管是 iptables 还是 ipvs 模式,都在处理软中断(softirq)上消耗了大量 CPU

    eBPF简化服务网格

    首先看下Service Mesh

    image.png

    这是一张经典的service mesh图,在eBPF之前,kubernetes服务网格解决方案是要求我们在每一个应用pod上添加一个代理Sidecar容器,如:Envoy/Linkerd-proxy。

    :即使在一个非常小的环境中,比如说有20个服务,每个服务运行5个pod,分在3个节点上,你也有100个代理容器。无论代理的实现多么小和有效,这种纯粹的重复都会浪费资源。每个代理使用的内存与它需要能够通信的服务数量有关。

    问题:

    为什么我们需要所有这些 sidecar?这种模式允许代理容器与 pod 中的应用容器共享一个网络命名空间。网络命名空间是 Linux 内核的结构,它允许容器和 pod 拥有自己独立的网络堆栈,将容器化的应用程序相互隔离。这使得应用之间互不相干,这就是为什么你可以让尽可能多的 pod 在 80 端口上运行一个 web 应用 —— 网络命名空间意味着它们各自拥有自己的 80 端口。代理必须共享相同的网络命名空间,这样它就可以拦截和处理进出应用容器的流量。

    eBPF的sidecar less proxy model

    基于eBPF的Cilium项目,最近将这种“无 sidecar”模式带到了服务网格的世界。除了传统的 sidecar 模型,Cilium 还支持每个节点使用一个 Envoy 代理实例运行服务网格的数据平面。使用上面的例子,这就把代理实例的数量从 100 个减少到只有 3 个

    image.png

    支持eBPF的网络允许数据包走捷径,绕过内核部分网络对战,这可以使kubernetes网络的性能得到更加显著的改善,如下图:


    image.png

    在服务网格的情况,代理在传统网络中作为sidecar运行,数据包到达应用程序的路径相当曲折:入栈数据包必须穿越主机TPC/IP栈,通过虚拟以太网连接到达pod的网络命名空间。从那里,数据包必须穿过pod的网络对战到达代理,代理讲数据包通过回环接口转发到应用程序,考虑到流量必须在连接的两端流经代理,于非服务网格流量相比,这将导致延迟的显著增加。

    而基于eBPF的kubernetes CNI实现,如Cilium,可以使用eBPF程序,明智的勾住内核中的特定点,沿着更加直接的路线重定向数据包。因为Cilium知道所有的kubernetes断点和服务的身份。当数据包到达主机时,Cilium 可以将其直接分配到它所要去的代理或 Pod 端点。

    总结

    通过以上的内容,我想大家对kube-proxy和基于eBPF的CNI插件Cilium已经有了一些了解。可以看到在云原生领域,Cilium 已经使用eBPF 实现了无kube-proxy的容器网络。利用eBPF解决iptables带来的性能问题。另外以上也只是介绍了eBPF的网络方面,其实eBPF在跟踪、安全、负载均衡、故障分析等领域都是eBPF的主战场,而且还有更多更多的可能性正在被发掘。

    相关文章

      网友评论

          本文标题:Kubernetes网络

          本文链接:https://www.haomeiwen.com/subject/jflxqrtx.html