作者| 赵明山(立衡)
前言
OpenKruise 是阿里云开源的云原生利用自动化治理套件,也是以后托管在 Cloud Native Computing Foundation (CNCF) 下的 Sandbox 我的项目。它来自阿里巴巴多年来容器化、云原生的技术积淀,是阿里外部生产环境大规模利用的基于 Kubernetes 之上的规范扩大组件,也是紧贴上游社区规范、适应互联网规模化场景的技术理念与最佳实际。
OpenKruise 在 2021.5.20 公布了最新的 v0.9.0 版本(ChangeLog),上一篇文章咱们介绍了新增 Pod 重启、删除防护等重磅性能,明天向大家介绍另一个外围个性,即 SidecarSet 基于上一个版本扩大了特地针对 Service Mesh 场景的反对。
背景:如何独立降级 Mesh 容器
SidecarSet 是 Kruise 提供的独立治理 Sidecar 容器的 workload。用户通过 SidecarSet 可能便当的实现对 Sidecar 容器的 主动注入 和独立降级,详情请参考:OpenKruise 官网
默认状况下,Sidecar 的独立降级程序是先进行旧版本的容器,而后再创立新版本的容器。这种形式尤其适宜不影响 Pod 服务可用性的 Sidecar 容器,例如日志收集 agent,然而对于很多代理或运行时的 Sidecar 容器,如 Istio Envoy,这种降级办法就有问题了。Envoy 作为 Pod 中的一个 Proxy 容器代理了所有的流量,这种场景下如果间接重启降级,Pod 服务的可用性必然会受到影响,因而须要思考利用本身的公布和容量状况,无奈齐全独立于利用做 Sidecar 的公布。
阿里巴巴团体外部领有上万的 Pod 都是基于 Service Mesh 来实现相互间的通信,因为 Mesh 容器降级会导致业务 Pod 的不可用,因此 Mesh 容器的降级将会极大妨碍 Service Mesh 的迭代。针对这种场景,咱们同团体外部的 Service Mesh 团队一起单干实现了 Mesh 容器的热降级能力。本文将重点介绍在实现 mesh 容器热降级能力的过程中 SidecarSet 是表演了怎么的重要角色。
SidecarSet 助力 Mesh 容器无损热降级
Mesh 容器不能像日志采集类容器间接原地降级,其起因在于:Mesh 容器必须要不间断地对外提供服务,而独立降级形式会导致 Mesh 服务存在一段不可用工夫。尽管社区中已有一些出名的 Mesh 服务如 Envoy、Mosn 等默认可能提供平滑降级的能力,然而这些降级形式无奈与云原生进行失当地联合,且 kubernetes 自身也不足对此类 Sidecar 容器的降级计划。
OpenKruise SidecarSet 为此类 Mesh 容器提供了 Sidecar 热降级机制,可能通过云原生的形式助力 Mesh 容器实现无损热降级。
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
- upgradeType : HotUpgrade 代表该 sidecar 容器的类型是 Hot upgrade,即热降级计划。
- HotUpgradeEmptyImage : 当热降级 Sidecar 容器时,业务须要提供一个 empty 容器用于热降级过程中的容器切换。Empty 容器同 Sidecar 容器具备雷同的配置(镜像地址除外),例如 command , lifecycle , probe 等。
SidecarSet 热降级机制次要蕴含注入热降级 Sidecar 容器和 Mesh 容器平滑降级两个过程。
注入热降级 Sidecar 容器
针对热降级类型的 Sidecar 容器,在 Pod 创立时 SidecarSet Webhook 将会注入两个容器:
- {Sidecar.name} -1: 如下图所示 envoy -1,这个容器代表正在理论工作的 sidecar 容器,例如:envoy :1.16.0
- {Sidecar.name} -2: 如下图所示 envoy-2,这个容器是业务提供的 HotUpgradeEmptyImage 容器,例如:empty :1.0
上述 Empty 容器在 Mesh 容器运行过程中,并没有做任何理论的工作。
Mesh 容器平滑降级
热降级流程次要分为一下三个步骤:
- Upgrade: 将 Empty 容器替换为最新版本的 Sidecar 容器,例如:envoy-2.Image = envoy:1.17.0
- Migration : 执行 Sidecar 容器的 PostStartHook 脚本,实现 mesh 服务的平滑降级
- Reset: Mesh 服务平滑降级后,将老版本 Sidecar 容器替换为 Empty 容器,例如:envoy-1.Image = empty : 1.0
仅需上述三个步骤即可实现热降级中的全副流程,若对 Pod 执行屡次热降级,则反复执行上述三个步骤即可。
Migration 外围逻辑
SidecarSet 热降级机制不仅实现了 Mesh 容器的切换,并且提供了新老版本的协调机制(PostStartHook),然而至此还只是万里长征的第一步,Mesh 容器同时还须要提供 PostSartHook 脚本来实现 Mesh 服务本身的平滑降级(上述 Migration 过程),如:Envoy 热重启、Mosn 无损重启。
Mesh 容器个别都是通过监听固定端口来对外提供服务,此类 Mesh 容器的 migration 过程能够概括为:通过 UDS 传递 ListenFD 和进行 Accpet、开始排水。针对不反对热重启的 Mesh 容器能够参考此过程实现革新,逻辑图如下:
热降级 Migration Demo
不同 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)
}
已知 Mesh 容器热降级案例
阿里云服务网格(Alibaba Cloud Service Mesh,简称 ASM)提供了一个全托管式的服务网格平台,兼容社区 Istio 开源服务网格。以后,基于 OpenKruise SidecarSet 的热降级能力,ASM 实现了数据立体 Sidecar 热降级能力(Beta),用户能够在利用无感的状况下实现服务网格的数据立体版本升级,正式版也将于近期上线。除热降级能力外,ASM 还反对配置诊断、操作审计、拜访日志、监控、服务注册接入等能力,全方位晋升服务网格应用体验,欢迎您返回试用。
总结
云原生中 Mesh 容器的热降级始终都是迫切却又辣手的问题,本文中的计划也只是阿里巴巴团体在此问题上的一次摸索,在反馈社区的同时也心愿可能抛砖引玉,引发各位对此中场景的思考。同时,咱们也欢送更多的同学参加到 OpenKruise 社区来,独特建设一个场景更加丰盛、欠缺的 K8s 利用治理、交付扩大能力,可能面向更加规模化、复杂化、极致性能的场景。
- 热降级 Migration Demo:_https://github.com/openkruise…
- Github:_https://github.com/openkruise…
- Official:_https://openkruise.io/_
- 钉钉交换群: