关于k8s:YurtTunnel-详解|如何解决-K8s-在云边协同下的运维监控挑战

87次阅读

共计 8106 个字符,预计需要花费 21 分钟才能阅读完成。

简介:随同着 5G、IoT 等技术的疾速倒退,边缘计算被越来越宽泛地利用于电信、媒体、运输、物流、农业、批发等行业和场景中,成为解决这些畛域数据传输效率的要害形式。与此同时,边缘计算状态、规模、复杂度的日益增长,边缘计算畛域的运维伎俩、运维能力对边缘业务翻新速度的撑持日趋乏力。于是,Kubernetes 迅速成为边缘计算的要害因素,帮忙企业在边缘更好地运行容器,最大化利用资源、缩短研发周期。
作者|何淋波(新胜)

背景


随同着 5G、IoT 等技术的疾速倒退,边缘计算被越来越宽泛地利用于电信、媒体、运输、物流、农业、批发等行业和场景中,成为解决这些畛域数据传输效率的要害形式。与此同时,边缘计算状态、规模、复杂度的日益增长,边缘计算畛域的运维伎俩、运维能力对边缘业务翻新速度的撑持日趋乏力。于是,Kubernetes 迅速成为边缘计算的要害因素,帮忙企业在边缘更好地运行容器,最大化利用资源、缩短研发周期。

然而,如果将原生 Kubernetes 间接利用到边缘计算场景下,依然须要解决诸多问题,比方云与边个别位于不同网络立体,同时边缘节点广泛位于防火墙外部,采纳云 (核心) 边协同架构,将导致原生 K8s 零碎的运维监控能力面临如下挑战:

  • K8s 原生运维能力缺失(如 kubectl logs/exec 等无奈执行)
  • 社区支流监控运维组件无奈工作(如 Prometheus/metrics-server)

为了帮忙企业解决原生 Kubernetes 在边缘场景下对于利用生命周期治理、云边网络连接、云边端运维协同、异构资源反对等方方面面的挑战,基于 K8s 实现的边缘计算云原生开源平台 OpenYurt 应运而生,其也是 CNCF 在边缘云原生幅员中的重要组成部分。本文将具体介绍,作为 OpenYurt 外围组件之一的 Yurt-Tunnel 如何是扩大原生 K8s 零碎在边缘场景下相干能力的。

Yurt-Tunnel 设计思路


因为边缘能够拜访云端,因而能够思考在云边构建能够反向穿透的隧道,从而保障云 (核心) 能够基于隧道被动拜访边缘。过后咱们也考察了很多开源的隧道计划,从能力以及生态兼容性等方面,最初咱们抉择基于 ANP​设计并实现了 Yurt-Tunnel 整体解决方案,具备平安,非侵入、可扩大、传输高效等长处。

实现形式

在 K8s 云边一体化架构中构建一个平安、非侵入、可扩大的反向通道解决方案,计划中至多须要包含如下能力。

  • 云边隧道构建
  • 隧道两端证书的自治理
  • 云端组件申请被无缝倒流到隧道

Yurt-tunnel 的架构模块如下图:

3.1 云边隧道构建

当边缘的 yurt-tunnel-agent 启动时,会依据拜访地址与 yurt-tunnel-server 建设连贯并注册,并周期性检测连贯的衰弱状态以及重建连贯等。

# https://github.com/openyurtio/apiserver-network-proxy/blob/master/pkg/agent/client.go#L189
# yurt-tunnel-agent 的注册信息:
"agentID": {nodeName}
"agentIdentifiers": ipv4={nodeIP}&host={nodeName}"

当 yurt-tunnel-server 收到云端组件的申请时,须要把申请转发给对应的 yurt-tunnel-agent。因为除了转发初始申请之外,该申请 session 后续还有数据返回或者数据的继续转发(如 kubectl exec)。因而须要双向转发数据。同时须要反对并发转发云端组件的申请,象征须要为每个申请生命周期建设一个独立的标识。所以设计上个别会有两种计划。

计划 1: 初始云边连贯仅告诉转发申请,tunnel-agent 会和云端建设新连贯来解决这个申请。通过新连贯能够很好的解决申请独立标识的问题,同时并发也能够很好的解决。然而为每个申请都须要建设一个连贯,将耗费大量的资源。

计划 2: 仅利用初始云边连贯来转发申请,大量申请为了复用同一条连贯,所以须要为每个申请进行封装,并减少独立标识,从而解决并发转发的诉求。同时因为须要复用一条连贯,所以须要解耦连贯治理和申请生命周期治理,即须要对申请转发的状态迁徙进行独立治理。该计划波及到封包解包,申请解决状态机等,计划会简单一些。

OpenYurt 抉择的 ANP 组件,采纳的是上述计划 2,这个和咱们的设计初衷也是统一的。

# https://github.com/openyurtio/apiserver-network-proxy/blob/master/konnectivity-client/proto/client/client.pb.go#L98
# 云边通信的数据格式以及数据类型
type Packet struct {
  Type PacketType `protobuf:"varint,1,opt,name=type,proto3,enum=PacketType" json:"type,omitempty"`
  // Types that are valid to be assigned to Payload:
  //  *Packet_DialRequest
  //  *Packet_DialResponse
  //  *Packet_Data
  //  *Packet_CloseRequest
  //  *Packet_CloseResponse
  Payload              isPacket_Payload `protobuf_oneof:"payload"`
}

申请转发链路构建封装在 Packet_DialRequest 和 Packet_DialResponse 中,其中 Packet_DialResponse.ConnectID 用于标识 request,相当于 tunnel 中的 requestID。申请以及关联数据封装在 Packet_Data 中。Packet_CloseRequest 和 Packet_CloseResponse 用于转发链路资源回收。具体能够参照下列时序图:

RequestInterceptor 模块的作用

从上述剖析能够看出,yurt-tunnel-server 转发申请之前,须要申请端先发动一个 Http Connect 申请来构建转发链路。然而为 Prometheus、metrics-server 等开源组件减少相应解决会比拟艰难,因而在 Yurt-tunnel-server 中减少申请劫持模块 Interceptor,用来发动 Http Connect 申请。相干代码如下:

# https://github.com/openyurtio/openyurt/blob/master/pkg/yurttunnel/server/interceptor.go#L58-82
    proxyConn, err := net.Dial("unix", udsSockFile)
    if err != nil {return nil, fmt.Errorf("dialing proxy %q failed: %v", udsSockFile, err)
    }

    var connectHeaders string
    for _, h := range supportedHeaders {if v := header.Get(h); len(v) != 0 {connectHeaders = fmt.Sprintf("%s\r\n%s: %s", connectHeaders, h, v)
      }
    }

    fmt.Fprintf(proxyConn, "CONNECT %s HTTP/1.1\r\nHost: %s%s\r\n\r\n", addr, "127.0.0.1", connectHeaders)
    br := bufio.NewReader(proxyConn)
    res, err := http.ReadResponse(br, nil)
    if err != nil {proxyConn.Close()
      return nil, fmt.Errorf("reading HTTP response from CONNECT to %s via proxy %s failed: %v", addr, udsSockFile, err)
    }
    if res.StatusCode != 200 {proxyConn.Close()
      return nil, fmt.Errorf("proxy error from %s while dialing %s, code %d: %v", udsSockFile, addr, res.StatusCode, res.Status)
    }
​

3.2 证书治理

为了保障云边通道的长期平安通信,同时也为了反对 https 申请转发,yurt-tunnel 须要自行生成证书并且放弃证书的主动轮替。具体实现如下:

# 1. yurt-tunnel-server 证书:
# https://github.com/openyurtio/openyurt/blob/master/pkg/yurttunnel/pki/certmanager/certmanager.go#L45-90
- 证书存储地位: /var/lib/yurt-tunnel-server/pki
- CommonName: "kube-apiserver-kubelet-client"  // 用于 kubelet server 的 webhook 校验
- Organization: {"system:masters", "openyurt:yurttunnel"} // 用于 kubelet server 的 webhook 校验和 yurt-tunnel-server 证书的 auto approve
- Subject Alternate Name values: {x-tunnel-server-svc, x-tunnel-server-internal-svc 的 ips 和 dns names}
- KeyUsage: "any"

# 2. yurt-tunnel-agent 证书:# https://github.com/openyurtio/openyurt/blob/master/pkg/yurttunnel/pki/certmanager/certmanager.go#L94-112
- 证书存储地位: /var/lib/yurt-tunnel-agent/pki
- CommonName: "yurttunnel-agent"
- Organization: {"openyurt:yurttunnel"} // 用于 yurt-tunnel-agent 证书的 auto approve
- Subject Alternate Name values: {nodeName, nodeIP}
- KeyUsage: "any"

# 3. yurt-tunnel 证书申请 (CSR) 均由 yurt-tunnel-server 来 approve
# https://github.com/openyurtio/openyurt/blob/master/pkg/yurttunnel/pki/certmanager/csrapprover.go#L115
- 监听 csr 资源
- 过滤非 yurt-tunnel 的 csr(Organization 中没有 "openyurt:yurttunnel")
- approve 还未 Approved 的 csr

# 4. 证书主动轮替解决
# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/client-go/util/certificate/certificate_manager.go#L224

3.3 无缝导流云端组件申请到隧道


因为须要无缝把云端组件的申请转发到 yurt-tunnel-server,也象征不须要对云端组件进行任何批改。因而须要对云端组件的申请进行剖析,目前组件的运维申请次要有以下两种类型:

  • 类型 1: 间接应用 IP 地址拜访,如: http://{nodeIP}:{port}/{path}
  • 类型 2: 应用域名拜访, 如: http://{nodeName}:{port}/{path}

针对不同类型申请的导流,须要采纳不同计划。

计划 1: 应用 iptables dnat rules 来保障类型 1 的申请无缝转发到 yurt-tunnel-server

# 相干 iptables rules 保护代码: https://github.com/openyurtio/openyurt/blob/master/pkg/yurttunnel/iptables/iptables.go
# yurt-tunnel-server 保护的 iptables dnat rules 如下:
[root@xxx /]# iptables -nv -t nat -L OUTPUT
TUNNEL-PORT  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* edge tunnel server port */

[root@xxx /]# iptables -nv -t nat -L TUNNEL-PORT
TUNNEL-PORT-10255  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:10255 /* jump to port 10255 */
TUNNEL-PORT-10250  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:10250 /* jump to port 10250 */

[root@xxx /]# iptables -nv -t nat -L TUNNEL-PORT-10255
RETURN     tcp  --  *      *       0.0.0.0/0            127.0.0.1            /* return request to access node directly */ tcp dpt:10255
RETURN     tcp  --  *      *       0.0.0.0/0            172.16.6.156         /* return request to access node directly */ tcp dpt:10255
DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* dnat to tunnel for access node */ tcp dpt:10255 to:172.16.6.156:10264

计划 2: 应用 dns 域名解析 nodeName 为 yurt-tunnel-server 的拜访地址,从而使类型 2 申请无缝转发到 yurt-tunnel

# x-tunnel-server-svc 和 x -tunnel-server-internal-svc 的不同用处:
 - x-tunnel-server-svc: 次要 expose 10262/10263 端口,用于从公网拜访 yurt-tunnel-server。如 yurt-tunnel-agent
 - x-tunnel-server-internal-svc: 次要用于云端组件从外部网络拜访,如 prometheus,metrics-server 等

# dns 域名解析原理:
1. yurt-tunnel-server 向 kube-apiserver 创立或更新 yurt-tunnel-nodes configmap, 其中 tunnel-nodes 字段格局为: {x-tunnel-server-internal-svc clusterIP}  {nodeName},确保记录了所有 nodeName 和 yurt-tunnel-server 的 service 的映射关系
2. coredns pod 中挂载 yurt-tunnel-nodes configmap,同时应用 host 插件应用 configmap 的 dns records
3. 同时在 x -tunnel-server-internal-svc 中配置端口映射,10250 映射到 10263,10255 映射到 10264
4. 通过上述的配置,能够实现 http://{nodeName}:{port}/{path}申请无缝转发到 yurt-tunnel-servers

云端申请扩大:

如果用户须要拜访边缘的其余端口(10250 和 10255 之外),那么须要在 iptables 中减少相应的 dnat rules 或者 x-tunnel-server-internal-svc 中减少相应的端口映射,如下所示:

# 例如须要拜访边缘的 9051 端口
# 新增 iptables dnat rule:
[root@xxx /]# iptables -nv -t nat -L TUNNEL-PORT
TUNNEL-PORT-9051  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:9051 /* jump to port 9051 */

[root@xxx /]# iptables -nv -t nat -L TUNNEL-PORT-9051
RETURN     tcp  --  *      *       0.0.0.0/0            127.0.0.1            /* return request to access node directly */ tcp dpt:9051
RETURN     tcp  --  *      *       0.0.0.0/0            172.16.6.156         /* return request to access node directly */ tcp dpt:9051
DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* dnat to tunnel for access node */ tcp dpt:9051 to:172.16.6.156:10264

# x-tunnel-server-internal-svc 中新增端口映射
spec:
  ports:
  - name: https
    port: 10250
    protocol: TCP
    targetPort: 10263
  - name: http
    port: 10255
    protocol: TCP
    targetPort: 10264
  - name: dnat-9051 # 新增映射
    port: 9051
    protocol: TCP
    targetPort: 10264

当然上述的 iptables dnat rules 和 service 端口映射,都是由 yurt-tunnel-server 自动更新。用户只须要在 yurt-tunnel-server-cfg configmap 中减少端口配置即可。具体如下:

# 留神:因为证书不可控因素,目前新增端口只反对从 yurt-tunnel-server 的 10264 转发
apiVersion: v1
data:
  dnat-ports-pair: 9051=10264 # 新增端口 =10264(非 10264 转发不反对)
kind: ConfigMap
metadata:
  name: yurt-tunnel-server-cfg
  namespace: kube-system

近期布局

  • 反对 kube-apiserver 的 EgressSelector 性能
  • 验证 yurt-tunnel-server 多实例部署验证
  • 反对 yurt-tunnel-agent 配置多个 yurt-tunnel-server 地址
  • 反对证书存储目录自定义
  • 反对证书 Usage 定义更精细化,保障证书应用范畴可控
  • 反对 yurt-tunnel-server 拜访地址变动后,yurt-tunnel-server 证书可自动更新
  • 反对 yurt-tunnel-agent 对 yurt-tunnel-server 拜访地址的主动刷新
  • 反对非 NodeIP/NodeName 类型的申请转发(如非主机网络 Pod 的云拜访边)
  • 反对通过 Tunnel 由边缘 Pod 拜访云端 Pod
  • 反对 yurt-tunnel 的独立部署(非绑定 k8s)
  • 反对更多协定转发,如 gRPC, websocket, ssh 等

欢送退出 OpenYurt 社区


作为阿里云边缘容器服务 ACK@Edge 的内核,OpenYurt 曾经在 CDN、音视频直播、物联网、物流、工业大脑、城市大脑等数十个行业中失去商业化实际、服务规模达数百万 CPU 核。咱们可喜地看到,当初有越来越多的开发者、开源社区、企业和学信机构认可 OpenYurt 的理念,并且正在退出到独特建设 OpenYurt 的队伍中,比方 VMware、Intel、深服气、招商局、浙大、EdgeX Foundry 社区、eKuiper 社区等。咱们也欢送更多的敌人共建 OpenYurt 社区,凋敝云原生边缘计算生态,让真正意义上的云原生在更多边缘场景中发明价值。

原文链接

本文为阿里云原创内容,未经容许不得转载。

正文完
 0