
作者 | 郑超
使用场景
在应用的部署和运维过程中,用户常常需要获取应用的日志,或直接登录到应用的运行环境中进行调试。在 Kubernetes 环境中,我们通常使用 kubectl log,kubectl exec 等指令来实现这些需求。如下图所示,在 kubectl 请求链路上, kubelet 将扮演服务器端,负责处理由 kube-apiserver(KAS) 转发来的请求,这就要求 KAS 和 kubelet 之间需要存在一条网络通路,允许 KAS 主动访问 kubelet。

图一:kubectl 执行流程
然而,在边缘计算场景中,边缘节点常位于本地专有网络中,这虽然保证了边缘节点的安全,但也造成位于云端管控节点的 KAS 无法直接访问位于边缘节点的 kubelet。因此,为了支持通过云端节点对边缘端应用进行运维操作,我们必须在云、边之间建立反向运维通道。
反向通道
Yurttunnel 是 OpenYurt 近期开源的一个重要组件,用来解决云边通信问题。反向通道是解决跨网络通信的一种常见方式,而 Yurttunnel 的本质就是一个反向通道。一个边缘集群下属的节点常位于不同的 network region 中,而位于同一个 region 内的节点之间是可以相互通信的,因此在设置反向通道时,我们只需保证在每个 region 内设置一个与 proxy server 相连的 agent 即可(如下图所示)。具体包括以下几个步骤:
-
在管控组件所在网络内,部署 proxy server。 -
proxy server 对外开放一个公网可以访问的 IP。 -
在每个 region 部署个 agent,并通过 server 的公网 IP 与 server 建立长连接。 -
管控组件对边缘节点的访问请求都将转发致 proxy server。 -
proxy server 再将请求通过对应的长连接发往目标节点。

ANP 并非开箱即用
-
如何转发云端节点的请求 -- 反向通道正常工作的一个前提是,管控节点发往边缘节点的请求必须先经过 proxy server。对于 Kubernetes 1.16 + 版本,KAS 能借由 EgressSelector 将发往节点的请求先发往指定的 proxy server。但对于 1.16 以前的版本,KAS 及其他管控组件(Prometheus 和 metrics server)只能直接访问节点,而不能绕道 proxy server。可以预见的是,部分用户在短期内,仍将使用 1.16 以前的版本,并且 Prometheus 和 metrics server 等管控组件在短期内也没有支持 EgressSelector 的计划。因此,我们要解决的第一个问题是,如何将管控组件发往节点的请求转发致 proxy server。 -
如何确保 server 副本覆盖所有的 region -- 在生产环境中,一个边缘集群常常包含上万个节点,并同时服务上百名用户,如果一个集群只有一个 proxy server, 那么,一旦 proxy server 出现故障,所有用户都将无法对位于边缘节点上的 pod 进行操作。因此,我们必须同时部署多个 proxy server 副本以保证集群高可用。同时,proxy server 的工作负荷将随着访问流量的增大而增大,用户的访问延时也势必增加。因此在部署 proxy server 时,我们还需考虑如何对 proxy server 进行水平拓展,以应对高并发场景。一个应对单点故障和高并发场景的经典解决方案是,部署多个 proxy server 副本,并使用负载均衡进行流量分发。然而在 OpenYurt 场景下,对于 KAS 发来的任意请求,LoadBalancer (LB) 会转发给哪个 server 副本是不可控的,因此,需要解决的第二个问题是,如何保证每个 server 副本都能与所有的 agent 建立连接。 -
如何将请求转发给正确的 agent -- 在运行过程中,proxy server 在收到请求后,需根据请求的 destination IP,将请求转发至位于对应 network region 内的 agent。然而,ANP目前的实现,假设所有的节点都位于一个网络空间内, server 会随机挑选一个 agent 转发请求。因此,我们需要解决的第三个问题是,如何将请求正确地转发给指定的 agent。 -
如何解除组件对节点证书的依赖 -- 在运行时,我们需要为 server 提供一套 TLS 证书,以实现 server 与 KAS,server 与 agent 间的安全通信。同时,我们也需要为 agent 准备一套 TLS client 证书,用以建立 agent 和 server 间的 gRPC 信道。ANP 目前的实现,要求 server 必须和 KAS 部署在同一个节点上,并且在启动时挂载节点 volume 共享 KAS tls 证书。同样,agent 也需要在启动时挂载 volume 共享 kubelet tls 证书。这无形中降低了部署的灵活性,造成了组建对节点证书的强依赖,在有些情况下,用户可能希望将 server 部署在非 KAS 所在节点上。因此,另一个需要关注的问题是,如何解除组件对节点证书的依赖。 -
如何缩小 Tunnel 带宽 -- ANP 的一个核心设计思想,是使用 gRPC 封装 KAS 所有对外 HTTP 请求。这里选择 gRPC,主要是看重其对流(stream)的支持和清晰的接口规范,此外,强类型的客户端和服务器端可以有效减少运行时错误,提高系统稳定性。然而,我们也发现,相较于直接使用 TCP 协议,采用 ANP 也会带来额外的开销增加带宽。从产品层面考虑,Tunnel 流量走的是公网,带宽的增加也意味着用户成本的增加。因此,一个非常重要的问题是,在提高系统稳定性的同时,我们是否也能缩小带宽?
Yurttunnel 设计解析
1. 制定 DNAT 规则转发云端节点的请求

图三:Yurttunnel 请求转发流程
2. 动态获取 Server 副本数
-
在启动 yurttunnel server 时,我们会将副本数(serverCount)传入每个 server 副本中,并且为每个副本指定一个 server ID; -
agent 连接 LB 后,LB会随机选择一个 server 副本并让其与 agent 建立长连接; -
与此同时,server 会通过该通道向 agent 返回一个 ACK package,这个 package 中将包含 serverCount 和 serverID; -
agent 通过解析 ACK package,可以获悉 server 副本的个数,并在本地记录已连接的 serverID; -
如果 agent 发现,本地连接的 server 副本数小于 serverCount,则会再次向 LB 发送连接请求,直至本地记录的 serverID 数与 server Count 数一致为止。
3. 为 ANP 添加代理策略
-
在每个边缘节点上部署一个 agent。 -
agent 在 server 处登记时,使用 agent 所在节点的 IP 作为 agentID。 -
server 在转发请求时,通过匹配请求目标 IP 和 agentID,将请求转发至对应的 agent。
4. 动态申请安全证书
5. 压缩 Tunnel 带宽,节约成本
表 1: 原生 TCP V.S. ANP (kubectl logs kube-proxy)
表 2: 原生 TCP V.S. ANP (large log file)
Yurttunnel 系统架构

图四:Yurttunnel 系统架构
-
Yurttunnel Server - 负责将 apiserver,prometheus,metrics server 等管控组件发往节点的请求,转发至对应的 agent。具体包括以下子组件: -
ANP Proxy Server - 对 ANP gRPC server 的封装,负责管理与 Yurttunnel Agent 之间的长连接,并转发请求。 -
Iptable Manager - 修改管控节点的 DNAT 规则,以确保管控组件的请求能被转发至 Yurttunnel Server。 -
Cert Manager - 为 Yurttunnel Server 生成 TLS 证书。 -
Request Interceptor - 将 KAS 对节点的 HTTP 请求封装到符合 ANP 规则的 gRPC 包里。 -
Yurttunnel Agent - 与 Yurttunnel Server 主动建立连接,并将 Yurttunnel Server 发来的请求转发给 Kubelet。具体包括两个子组件: -
ANP Proxy Agent - 对 ANP gRPC agent 的封装,相较于上游,我们额外加入了 gzip compressor 以压缩数据。 -
Cert Manager - 为 Yurttunnel Agent 生成 TLS 证书。 -
Yurttunnel Server Service - 通常是一个 SLB,负责将管控组件发来的请求分发给合适的 Yurttunnel Server 副本,保证 Yurttunnel 的高可用和负载均衡。
总结与展望
更多精彩

识别二维码观看直播
走出舒适圈,从来都不简单
Bilibili资深运维工程师:DCDN在游戏应用加速中的实践


点此阅读作者更多好文!



