做者| 赵明山(立衡)
git
OpenKruise 是阿里云开源的云原生应用自动化管理套件,也是当前托管在 Cloud Native Computing Foundation ( CNCF ) 下的 Sandbox 项目。它来自阿里巴巴多年来容器化、云原生的技术沉淀,是阿里内部生产环境大规模应用的基于 Kubernetes 之上的标准扩展组件,也是紧贴上游社区标准、适应互联网规模化场景的技术理念与最佳实践。
OpenKruise 在 2021.5.20 发布了最新的 v0.9.0版本( ChangeLog ),上一篇文章咱们介绍了新增 Pod 重启、删除防御等重磅功能,今天向你们介绍另外一个核心特性,即 SidecarSet 基于上一个版本扩展了特别针对 Service Mesh 场景的支持。
github
SidecarSet 是 Kruise 提供的独立管理 Sidecar 容器的 workload。用户经过 SidecarSet 可以便利的完成对 Sidecar 容器的自动注入和独立升级,详情请参考:OpenKruise 官网
api
默认状况下,Sidecar 的独立升级顺序是先中止旧版本的容器,而后再建立新版本的容器。这种方式尤为适合不影响 Pod 服务可用性的 Sidecar 容器,例如日志收集 agent ,可是对于不少代理或运行时的 Sidecar 容器,如 Istio Envoy,这种升级方法就有问题了。Envoy 做为 Pod 中的一个 Proxy 容器代理了全部的流量,这种场景下若是直接重启升级,Pod 服务的可用性必然会受到影响,所以须要考虑应用自身的发布和容量状况,没法彻底独立于应用作 Sidecar 的发布。
app
阿里巴巴集团内部拥有上万的 Pod 都是基于 Service Mesh 来实现相互间的通讯,因为 Mesh 容器升级会致使业务 Pod 的不可用,于是 Mesh 容器的升级将会极大阻碍 Service Mesh 的迭代。针对这种场景,咱们同集团内部的 Service Mesh 团队一块儿合做实现了 Mesh 容器的热升级能力。本文将重点介绍在实现 mesh 容器热升级能力的过程当中 SidecarSet 是扮演了怎样的重要角色。
tcp
Mesh 容器不能像日志采集类容器直接原地升级,其缘由在于:Mesh 容器必需要不间断地对外提供服务,而独立升级方式会致使 Mesh 服务存在一段不可用时间。虽然社区中已有一些知名的 Mesh 服务如 Envoy 、Mosn 等默认可以提供平滑升级的能力,可是这些升级方式没法与云原生进行恰当地结合,且 kubernetes 自己也缺少对此类 Sidecar 容器的升级方案。
ide
OpenKruise SidecarSet 为此类 Mesh 容器提供了 Sidecar 热升级机制,可以经过云原生的方式助力 Mesh 容器实现无损热升级。post
apiVersion: apps.kruise.io/v1alpha1 kind: SidecarSet metadata: name: hotupgrade-sidecarset spec: selector: matchLabels: app: hotupgrade containers: - name: sidecar image: openkruise/hotupgrade-sample:sidecarv1 imagePullPolicy: Always lifecycle: postStart: exec: command: - /bin/sh - /migrate.sh upgradeStrategy: upgradeType: HotUpgrade hotUpgradeEmptyImage: openkruise/hotupgrade-sample:empty
SidecarSet 热升级机制主要包含注入热升级 Sidecar 容器和 Mesh 容器平滑升级两个过程。
性能
针对热升级类型的 Sidecar 容器,在 Pod 建立时 SidecarSet Webhook 将会注入两个容器:
ui
上述 Empty 容器在 Mesh 容器运行过程当中,并无作任何实际的工做。
阿里云
热升级流程主要分为一下三个步骤:
仅需上述三个步骤便可完成热升级中的所有流程,若对 Pod 执行屡次热升级,则重复执行上述三个步骤便可。
SidecarSet 热升级机制不只完成了 Mesh 容器的切换,而且提供了新老版本的协调机制( PostStartHook ),可是至此还只是万里长征的第一步,Mesh 容器同时还须要提供 PostSartHook 脚原本完成 Mesh 服务自身的平滑升级(上述 Migration 过程),如:Envoy 热重启、Mosn 无损重启。
Mesh 容器通常都是经过监听固定端口来对外提供服务,此类 Mesh 容器的migration 过程能够归纳为:经过 UDS 传递 ListenFD 和中止 Accpet 、开始排水。针对不支持热重启的 Mesh 容器能够参考此过程完成改造,逻辑图以下:
不一样 Mesh 容器对外提供的服务以及内部实现逻辑各有差别,进而具体的 Migration也有所不一样,上述逻辑只是对其中一些要点作了一些总结,但愿能对有须要的各位有所裨益,同时在 Github 上面咱们也提供了一个热升级 Migration Demo 以供参考,下面将对其中的一些关键代码进行介绍。
1. 协商机制
Mesh 容器启动逻辑首先就须要判断第一次启动仍是热升级平滑迁移过程,为了减小Mesh 容器沟通成本,Kruise 在两个 sidecar 容器中注入了两个环境变量 SIDECARSET_VERSION 和 SIDECARSET_VERSION_ALT ,经过判断两个环境变量的值来判断是不是热升级过程以及当前 sidecar 容器是新版本仍是老版本。
// return two parameters: // 1. (bool) indicates whether it is hot upgrade process // 2. (bool ) when isHotUpgrading=true, the current sidecar is newer or older func isHotUpgradeProcess() (bool, bool) { // 当前sidecar容器的版本 version := os.Getenv("SIDECARSET_VERSION") // 对端sidecar容器的版本 versionAlt := os.Getenv("SIDECARSET_VERSION_ALT") // 当对端sidecar容器version是"0"时,代表当前没有在热升级过程 if versionAlt == "0" { return false, false } // 在热升级过程当中 versionInt, _ := strconv.Atoi(version) versionAltInt, _ := strconv.Atoi(versionAlt) // version是单调递增的int类型,新版本的version值会更大 return true, versionInt > versionAltInt }
2. ListenFD 迁移
经过 Unix Domain Socket 实现 ListenFD 在不一样容器间的迁移,此步一样也是热升级中很是关键的一步,代码示例以下:
// 为了代码的简洁,全部的失败都将不捕获 /* 老版本sidecar经过Unix Domain Socket迁移ListenFD到新版本sidecar */ // tcpLn *net.TCPListener f, _ := tcpLn.File() fdnum := f.Fd() data := syscall.UnixRights(int(fdnum)) // 与新版本sidecar容器经过 Unix Domain Socket创建连接 raddr, _ := net.ResolveUnixAddr("unix", "/dev/shm/migrate.sock") uds, _ := net.DialUnix("unix", nil, raddr) // 经过UDS,发送ListenFD到新版本sidecar容器 uds.WriteMsgUnix(nil, data, nil) // 中止接收新的request,而且开始排水阶段,例如:http2 GOAWAY tcpLn.Close() /* 新版本sidecar接收ListenFD,而且开始对外服务 */ // 监听 UDS addr, _ := net.ResolveUnixAddr("unix", "/dev/shm/migrate.sock") unixLn, _ := net.ListenUnix("unix", addr) conn, _ := unixLn.AcceptUnix() buf := make([]byte, 32) oob := make([]byte, 32) // 接收 ListenFD _, oobn, _, _, _ := conn.ReadMsgUnix(buf, oob) scms, _ := syscall.ParseSocketControlMessage(oob[:oobn]) if len(scms) > 0 { // 解析FD,并转化为 *net.TCPListener fds, _ := syscall.ParseUnixRights(&(scms[0])) f := os.NewFile(uintptr(fds[0]), "") ln, _ := net.FileListener(f) tcpLn, _ := ln.(*net.TCPListener) // 基于接收到的Listener开始对外提供服务,以http服务为例 http.Serve(tcpLn, serveMux) }
阿里云服务网格( Alibaba Cloud Service Mesh,简称 ASM)提供了一个全托管式的服务网格平台,兼容社区 Istio 开源服务网格。当前,基于 OpenKruise SidecarSet 的热升级能力,ASM 实现了数据平面 Sidecar 热升级能力( Beta ),用户能够在应用无感的状况下完成服务网格的数据平面版本升级,正式版也将于近期上线。除热升级能力外,ASM 还支持配置诊断、操做审计、访问日志、监控、服务注册接入等能力,全方位提高服务网格使用体验,欢迎您前往试用。
总结
云原生中 Mesh 容器的热升级一直都是迫切却又棘手的问题,本文中的方案也只是阿里巴巴集团在此问题上的一次探索,在反馈社区的同时也但愿可以抛砖引玉,引起各位对此中场景的思考。同时,咱们也欢迎更多的同窗参与到 OpenKruise 社区来,共同建设一个场景更加丰富、完善的 K8s 应用管理、交付扩展能力,可以面向更加规模化、复杂化、极致性能的场景。