OpenYurt 深度解读:如何构建 Kubernetes 原生云边高效协同网络?

作者 | 郑超

导读:OpenYurt 是阿里巴巴开源的云边协同一体化架构,与同类开源方案相比,OpenYurt 拥有可实现边缘计算全场景覆盖的能力。在之前的一篇文章中,我们介绍了 OpenYurt 是如何在弱网和断网场景下实现边缘自治的。本文作为 OpenYurt 系列文章的第四篇,我们将着重介绍 OpenYurt 的另一个核心能力——云边通信J 1 j x 4 m k,以及相关组件 Yurttunnel。

使用场景

在应用的部署和运维过程中,用户常常需要获取应用的日志,或直接登录到应用的运行环境中进行调试。在 Kubernet* K 2 m )es 环] y P =境中,我们通常使用 kubectl log,kubectl exec 等指令来实现这些需求。如下图所示,在 kubectl r * , _ U W c Ml 请求链路上, kubelet 将扮演服务器端,负责处理由 kube-apiserver(KAS) 转发来的请求,这就要求 KAS和 kubelet 之间需要存在一条网络通路,允许 KAS 主动访问 kubelet


图一:kubectl 执行流程

I . 8 V Q O N而,在边缘计算场景中,边缘节点常位于本地专有网络中,这虽然保证了边缘节点安全,但也造成位于云端管控节点的 KAS 无法直接访问位于边缘节点的 kubelet。因此,为了支持通过云端节点对边缘端应用进行运维操作,我们必须在云、边之间建立反向运维通道。

F K q m S ] S向通道

Yurttunnel 是 OpenYurt 近期开源的一个重要组件,用来解决云边通信问题。反向通道是解决跨` | W F y E ! 5网络通信的一种常见方式,而 Yurttu I s Mnnel 的本质就是一个反向通道。一个边缘集群下属的节点常位于不同的 network region 中,而位于同一个 region 内的节点之间是可以相互通信的,因此在设置反向通道时,我们只需保证在每个 region 内设置一个与 proxy serv! Y }er 相连的 agent 即可(如下图所示)。具体包括以下几个步骤:

  • V 6 5 w管控组件所s e Y在网络内,部署 proxy seH 1 M w A 7 t 2 $rver。
  • proxy server 对外开放一个公网可以访问的 IP。
  • u X 4 8 [ Q每个 region 部署个 agent,并通过 server 的公网 IP 与 server 建立长连接。
  • 管控组件对边缘节点的访问请求都将转发致 proxt I Zy server。
  • proxy server 再将请求通过对应的长连接发往目标节点。

OpenYurt 深度解读:如何构建 Kubernetes 原生云边高效协同网络?
图二

在 Yurs 0 @ w ! ~ttunnel 中,我们选择使用上游项目apiserver-network-proxy(ANP)来实现 se} % N H . ^ drvn h O $er 和 agent 间的通信。ANP 是基于 kubers R # A $nE N : a t 6 ]etes 1.16 Alpha 新功能EgressSelector开发,意在实现 Kubernetes 集群组件的跨 intranet 通信(例如,mastj ^ E %er 位于管控 VPC,而 kubelet 等其他组件位于用户 VPC)。

读者可能会好奇,既然 OpenYurt 是基于ACK@Edge开源的,而在生产环境中,ACK@Edge的云边运维通道使用的是自研组件 tunnellib,那为f ) @ 0 W H # X m什么在开源版本里我们要选用一个新的组件呢? 这里不得不再y Z d ~ { {次提到 OpenYurt 的核心设计理念g e J ( % 1 “Extend upstream Kubernetes to Edge”

诚然,tunnellib 经过了复m w $ t杂线上环境的考验,组件性% c q v M能稳定,但我们更希望能与上游保持最大的技术公约数,让 OpenYurt 的用户体验更接近原 0 Q $ ) P生 Kubernete! U U as ;同时,在ACK@Edge的开发和运维过程中,我们发现,边缘集群的许多需求也同样存在于其他场景中(例如,大多数云厂商也需要实现节点跨网络通信),并且3 H @ j G : a m运维通道的传输效率也能进Q o = N 0 {一步优J | / C U f化(4.5章将详述优化过程)。因此,秉承开放分享、平等普惠的开源精神,我们希望能将开发和运维过b R K Y程中积累的的宝贵经验K 0 i [ k V在上游社区与更多的开发者分享。

ANP 并非开箱即用

然而 ANP 项目仍处于起步阶段,功能尚未完善,许多问题仍4 p _ 8 0 I有待解决。我们从实践中发现的主要问题包括:

  • 如何转发云端节点的请求--反向通道正常工作的! J v k一个前提是,管控节点发往边缘节点的请求必须先经过 proxy seJ } i rver。对于 Kubernetes 1.16 + 版本,KAS 能借由 EgressSe4 . - 6 l * &lector 将发往节点的请求先发往指定的 proxy server。但对于 1.16 以前的版本,KAS 及其他管控组件(Prometheus - q cs 和 metrics server)只能直接访问节点,而不能绕道 proxy server。可以预见的是,部分用户在短期内,仍将使用 1.16 以前的版本,并且 Prometheus 和 metrics server 等管控组件在短期内也没有支持 EgressSelector 的计划。因此,我们要解决的第一个问题是,如何将管控组件发往节点的请求转发致 proxy serverX s ]
  • r e D !何确保 server 副本覆O k n h盖所有的 region--在生产环境中,一个边缘集群常常包5 $ f C ] [ v含上万个节点,并同时服务上百名用户,如果一个集群只有一$ S 4 / { S s个 proxy server, 那么,一旦K l c H ( L proxy server 出现故障,所有用户都将无法对位于边缘| 9 X C j i a J [节点上的 pod 进行操作。因此,我们必须同时部署多个 proxyw O N Y d server 副本以保证集群高可用。同时c 6 `,proxy server 的工作负荷将随着访问流量的增L h ) p Y - O大而增大,用户的访问延时也势必增加。因此在部署 proxy server( G 7 : u e 2 时,我们3 L P f w u还需考虑如何对 proxyj B Q # server 进行水平拓展,以应对高并发场景。一个应对单点故障O l { : Q K N & d和高并发场景的经典解决方案是,部署多个 proxy server 副本,并使用负载均衡进行流量分发。然而在 OpenYurt 场景下,对于 KAS 发来的任意请求,LoadBalancer (LB)* 0 X j ; + R 会转发给哪个 server 副本是不可控的,因此,需要解决的第二个问题是,如何保证每个& d + o H ! server 副本都能与所有的 agent 建立连接
  • 如何将请求转发给正确的 a# r x 7 6 S agent --在运行过程中,proxy server 在收到请W w ) =求后,需根据请求的 destination IP,将请求转发至位于对应 network region 内的 ag/ V Vent。然而,ANP目前的实现,假设所有的节点都位于一个] r E i 8 ` w网络空间内, server 会随机挑选一个 agen2 T { Q $ . 0 L st 转发请求。因此,我们需要解决的第三个0 | B问题是,如何将请求正确地转发给指定的 agent
  • 如何解除组件对节点证书的依赖--在运行时,我们需要为 se^ + ] n J Q I Erver 提供一套 TLS 证书,以实现 server 与 KAS,server 与 agent 间的安全通信。同时,我们也需要为 agent 准备一套 TLS client 证书,用以建立 agent 和 server 间的 gRPC 信道。ANP 目前的实现,要求 server 必须和 KAS 部署在同一个节点上,并且在启动时挂载节点 volume 共享 KAS tls 证书。同样,agent 也需要在启动时挂载 volume 共享 kube= R U ! g 3 r Fl3 ` U 5 k C . 9et tls 证书。这无形中降低了部署# F m Y *的灵活性,造成了组建对节点证书的强E 6 T r {依赖,在有些情况下,用户可能希望将 s@ { r % H E 4 Verver 部署在非 KAS 所在节点上。因此,另一个需要关注的问题是,如何解除组件对节点证书的依赖
  • 如何缩小 Tunnel 带宽 -- ANP 的一个核心设计思想,是使用 gRPC 封装 KAS 所有对外 HTTP 请求。这里选择 gRPC,主要是看重其对流(strO j A h Y 5 qeam)的支持和清晰的接口规范,此外,强类Q p i J 4 g = N型的客户端和服务器端可以有效减少运行时错误` 9 F : t q L x q,提高系统稳定性。然而,我们也发现,相较于直接使用 TCP 协议,采用 ANPD y w 0 M r 也会带来额外的开销增加带宽。从产品层面考虑,Tunnel 流量走的是公网,带宽的增加也意味着用户成本的增加。因j I u u 0 /此,一个非常重要的问题是,在提高系统稳定性的同时,我们是否也能缩小带宽

Yurttunnel 设计解析

1. 制定DNAT规则转发云端节点的请求

如前文所述,ANP 是基于上游新功能 EgressSelector 开发的,该功能允许用户在启动 KAS 时通过传入 egress configuration 来要求 Kn & q d g @ _ 3AS 将 eN 8 - q [gress 请求转发到指定的 proy J Nxy ser8 f H ; Jver。但由于我们需要兼顾新老版本的 Kubernetes 集群,并且考虑到,其他管控组件(Prometheus 和 metric server)并不支持 EgressSelector 特性,{ H - o f t A Q我们K R N _ [ | ` , [需要保证在无法使用 EgressSelectorf = ( Q ^ K X ] 的情况下也能将 KAS egress 请求转发致 proxy server。为此,我们在每一个云端管控节点上都部署一个 Yurttunnel Serv{ d m F z A C ger 副本,并在 Server 中内- . P J ~嵌一个新组件 Iptabel Manager。Iptable Manager 会通过在宿主机的 Iptable 中的 OUTPUT 链中添加 DNAT 规则,将管控组件对节点的请求转发致 Yurttunnel Server。

同时,当启用 EgressSelector 后,KAS 对外请求都遵循一2 } X个统一的格式,因此我们新增一个组件, ANP interceptor。ANP interceptor 会负责截取从 mz @ N |aster 发来的 http 请求,并将其封装成 EgressSelector 格式。Yurttunnel 请求转发的具体流程见图三。

OpenYurt 深度解读:如何构建 Kubernetes 原生云边高效协同网络?
图三:Yur| f x ] Fttunnel 请求转发流程

2. 动态获取 Serh k 2 K F {vc $ x ( c Qer副本数

在上一节中,我们提到,我们将采用负载均衡的方式来管理 yurttunnel server,所有的 ingress 请求都会通过3 M Y U g m R N LB 分发给一个 servI %er 副本。由于我们无法预测 LB 会挑选哪个 server 副本,我们必须保证每个 server 副本都要与所有的 agent 建立连接。这里,我们将使用 ANP 自带的功能实现这一需求,具体流程如下:

  • 在启动 yurttunnel server 时,我们会将副本数(serverCount)传入每个 server 副本中,并且为每个副本指定一个 s; C J Q ierver ID;
  • agent 连接 LB 后,LB会随机选择一个 server 副本并Y 6 Q D /让其与 agent 建立长连接;
  • 与此同时,server 会通P W O p } M过该通道向 agent 返回一个 ACK pack~ b % 8 y j 5 vage,这个 package 中将包含 serverCount 和 serverID;
  • agent 通过解析 ACK package,可以获悉 server 副本$ n m 8 v的个数,并在本地记录已连接的 serverID= e Y i ! [ M | O
  • 如果 agent 发现,本地连接的 server 副本数小于 serverCount,则会再次向 LB 发送连接请求,直至本地记录的 server3 K = ~ HID 数与 server Count 数一致为止。

该机) b h ` 5 U Y制虽然帮助我] = _ T & p h们实现了 serverz Y % H 副本的全网段覆盖。但同时,也存在不可忽视的缺点,由于 agent 无法选择与哪个 server 副本建立连接,因此,为了连接所有的 server 副本,ag} - A F T ^ Jent 必须反复访问 LB。在这个过程中,server 由于还没有与所有的 agent 建立连接,KAS 发来的请求可能无法转发至对应的节点。一个潜在的解决方案是,为每个 server 副本创m S F L K A建一个独立的 LB,负# V 5 W V ;责与 agent 之间的连接,同时在 agent 端记录所有8 y 5 Y ) [ c server 副本对应 LB 的信息,这一方案能o ] } 3帮助 agent 快速地与所有的 server 副本建立连接。该方案的具体实现细节,目前仍在与上游社区的开发者讨论中。

3. 为ANc i VP 添加3 I ` | d代理策略

在 Opel V }nYurt 的网络模型下,边缘节点分布在不同的 network region 中,随机选择的 agent 可能无法将请求转发至位于其他 region 内的节点上。因此我们不得不修改 ANP server 底层代理转发的逻辑。然而,根据长期的经验,我们相信,proxy server 支持不同的代理策略,例如,将请求转发至指定数据中心,region,或者指定主机,是一个较为通用的需求。经过和 ANP 社区开发者讨论,我们I 5 ` K w决定重构 ANP 管理 agent 连接的接口,允许用户根据G , x 6 @ G * 0需求实现新的代理策略,并计划将该 feature 最终合入上游代码库。目前重构工作仍在进行中,在 Yurtt% O m ( ~ - 9unnS { O L x qel 第一个开源版本中,我们暂r : E : ;时采用以下配置:

  • 在每个边缘节点上部署一t A L个 agent。
  • agent 在 server 处登记时,使用 agent 所在节点的 Ic 6 nP 作为 agentID。
  • sc ^ Z g T R ervN d R 3 N ! W - Aer 在转发请! ( } } $ p s求时,通过匹配请求目标 IP 和 agentID,将请求转发至对应的 agT P 5 went。

我们计划在 OpenYurt 后续发4 $ 0 i 3布 Yurt Unit(边缘节点分区M % _管控)& S n _ e 2 O I之后,配合新增的 ANP 代理转发策略,实现 agent 的分区部署,和请求的分区转发。

4. 动态申请安全证书

为了解除 yurttunnelh s o T Y ` U A h 组件( { z H Y i对节点证书的依赖,我们在 yurttunnel 中新增 cert manager 组建,cert manager 会在 server 和 agenv , y m ( s ^ q zt 运行后,向 KAS 提交 certificate signning request(9 B Z ? = [ [ =CSR)。server 将使用申请到的证书来确保其与 KAS 和 agent 间的安全通信,agent 会使? s 1 _ L C n f用申请到的证书确保其与 server 间} E b Z e gRPC 信道的安全。由于 agent 和 kubelet 间T m N 2 !是通过 tcp 协议连接,因此,我们无需| S @ d G Z为 agent 和 kubelet 间的连接准备证书。

5. 压缩 Tunnel 带宽,节约成本

在 3.5 中,我们提到,使用 gRPC 封装 Tunnel 虽然可以提高传输稳定性,但同时也会增加公网流量。这是否意味着稳定性和性能 { 8 R ^ c $,我们只能二选一Y G ` 1?通过对不同用户场景的分析,J t k我们发现,在大多数情况下,用户使用运维通道是为了获取容器日志(即 kubectl log),而传统日志文件,存在许多相同的文本信息,因此我们推断使用 gzip 等压缩算法能有效缩小带宽。为 M [ M x a g @了验证这一假设,我们在 ANP 底层的 gRPC 库中添加了 gzip compressor,并且对比了与使用原生 TCP 连接场景下的数据传输量。

我们考虑的第一个实验场景是,分别通过 TCP 连接和 ANP 获取同一 kubeproxy 容器的日志,我们截取了这一过程中 TunnelB + C 上双向 package 和 bytes 总量。


表 1: 原生 TCP V.S. ANP (kubectl logs kube-proxy)

如表 1 所示,通过使用 ANP, 总传输数据量下降了 29.93%

经过长时间运行,容器的日志文本常常可以达到十几兆,为了模拟获取大文本日志的场景。我们创建了一包含 10.5M systemd log(即 jour) } 7 Ynalctl)的 ubuntu 容器,同样我们分别使用原生 TCP 连接和 ANP 传输该日志文件,并测量了 Tunnel 上的数据总量。

OpenYurt 深度解读:如何构建 Kubernetes 原生云边高效协同网络?
表 2: 原生 TCP V.S. ANP (large log file)

如表 2 所示,S 6 l *在日志文本较大的情况下,通过使用 ANP, 总传输数据量下降了 40.85%

由此3 - E / .可见,相较于原生 TCP 连接,ANP 不仅能提供更高的传输稳定性L 8 v} n f : O ,还可以大幅降低公网流量。考虑到边缘集群动辄上万的节点规模,新的解1 $ R b r决方案将帮助用户在U 8 R e { X公网流量方面节约大量开销。

Yurttunnel系统架构

OpenYurt 深度解读:如何构建 Kubernetes 原生云边高效协同网络?
图四:Yurttunnel 系统架构

综上,Yurttunnel 主要包含以下组件:

  • Yurttunnel Server- 负责将; J [ w ` q apiserver,prometheus,metrics server 等管控组件发往节点的请求,转发至对应的 agent。具体包括以下子组件:

    • ANP Proxy4 k & * - 9 ` A % Server - 对 ANP gRPC server 的封装,负责管理与 Yurttunnel Agent 之间的- J T ~ l 0长连接,并转发请求。
    • Iptable Manager - 修改管控节点的 DNAT 规则,以确保管控组件的请求能被转发Z l + # ] D @ 至 Yurttunnel Server。
    • Cert Manager - 为 Yurtd J V R T ^ S b %tunnel Server 生成 TL/ E |S 证书。
    • Request Interceptor - 将 KAS 对节点的 HTTP 请求封装到符合 ANP 规则的 gRPC 包里。
  • Yurttunnel Agent- 与 Yurttunnel Server 主动建立连接,并将 Yurttunnel Server 发来的请求转发给 Kubelet。具体包括两个子组件:

    • ANP Proxy Agent - 对 ANP gRPC agent 的封装,相较于上游,我们u . S 2 8 g s额外$ | ^ L t 7 7 B E加入了 gzip compresso{ ( u r 以压缩数据。
    • Cert Manager - 为 Yurttunnel Agent 生成) y [ _ N z 9 ! - TLS 证书。
  • Yur~ i f 6 ; H r 9ttunnel Server Service- 通常是一个 SLB,负责将管控组件发来的请求分发给合适的 Yurt] ? P ; ktunw R T Knel Server 副本,保证 Yurttunnel 的高可用和负载均衡。

总结与展望

Yurttunnel 作为 OpenYurt 近期开源的重要组件,打通了 OpenYurt 集群的W 2 a z P云边通道,为边缘集群上的j + [ 3 + 9容器运维提供了一个统一的入口。通过对上游解m ( b u ` h h z决方案进行改造@ 0 _ , I (,Yurttunnel 不仅提供了更高的传输稳定性,也大幅降低了数据传输量。

OpenYurt 于U o 7 x * Y ` 2020 年 5 月 29 日正式对外开源,借助社区和广大开发者的力量快速成长@ 0 - y 1 = P S,开% d 3 h & e Z )源仅 3 个月后就正式成为 CNCF 沙箱级别边缘计算云2 ( y P ~ / v E p原生项目。未来,OpenYurt 将延续 “Extending your upstream Kubernetes to edge” 的核心设计理念,在选择与上游保持最大技术公约数的同时,发扬开源分享的精神,与广大开发者一起推进 Kubernetes 社区的进步。

OpenYurt 已入围 “2020 年度 10 大新锐开源项目”,欢迎大家点击链接:https://www.infoq.cn/talk/sQ7eKfv1KW1A0kUafBgv,选择“25 号”,为 OpenYurt 加油助力!

“阿里巴巴云原生关注微服务、ServerQ B ! ^ eless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”