Operator
Service Mesh 是蚂蚁集团下一代技术架构的核心,也是蚂蚁集团内部双十一应用云化的重要一环,本文主要分享在蚂蚁集团当前的体量下,如何支撑应用从现有微服务体系大规模演进到 Service Mesh 架构,并平稳落地。
为什么需要 Service Mesh ?
使用 Service Mesh 之前
SOFAStack 作为蚂蚁集团微服务体系下服务治理的核心技术栈,通过提供若干中间件来实现服务发现和流量管控等能力,例如:
Cloud Engine 应用容器
SOFABoot 编程框架(已开源)
SOFARPC(已开源)
经过若干年的严苛金融场景的锤炼,SOFAStack 已经具备极高的可靠性和可扩展性。通过开源共建,也已形成了良好的社区生态,能够与其他开源组件相互替换和集成。在研发迭代上,中间件类库已经与业务解耦。
但是,由于运行时两者在同一个进程内,这意味着在基础库升级时,需要推动业务方升级对应的中间件版本。
使用 Service Mesh 之后
蚂蚁团队引入的 Service Mesh,将原先通过类库形式提供的服务治理能力进行提炼和优化后,下沉到与业务进程协同,但独立运行的 Sidecar Proxy 进程中,大量的 Sidecar Proxy 构成了一张规模庞大的服务网络,为业务提供一致的、高质量的用户体验。同时,也实现了服务治理能力在业务无感的条件下独立进行版本迭代的目标。
应用 Service Mesh 的挑战
Service Mesh 提供的能力很美好,但现实的挑战同样很多,例如:
数据面技术选型和私有协议支持。
控制面与蚂蚁集团内部现有系统对接。
配套监控运维体系建设。
在调用链路增加两跳的情况下,如何优化请求延迟和资源使用率等等。
什么是 Operator?
如果说 Kubernetes 是 “操作系统” 的话,Operator 是 Kubernetes 的第一层应用,它部署在 Kubernetes 里,使用 Kubernetes “扩展资源” 接口的方式向更上层用户提供服务。
本文将从 MOSN(Sidecar Proxy)的运维和风险管控方面,分享 Operator 的实践经验。
Sidecar 运维
注入
创建注入
已经完成容器化改造且运行在 Kubernetes 中的应用,接入到 Service Mesh 体系中的方式中,最简单的方式为:在应用发布阶段,通过 Mutating Webhook 拦截 Pod 创建请求,在原始 Pod Spec 的基础上,为 Pod 注入一个新的 MOSN 容器。该方案也是以 Istio 为代表的 Service Mesh 社区方案所采用的,示例如下:
初始配置
在资源分配上,起初依据经验值,在应用 8 GB 内存的场景下,为 Sidecar 分配了 512 MB 内存,即
App: req=8G, limit=8G
Sidecar: req=512M, limit=512M
但是,这种分配方案带来了一些问题:
部分流量比较高的应用,其 MOSN 容器出现了严重的内存不足甚至 OOM。
注入进去的 Sidecar 容器额外向调度器申请了一部分内存资源,这部分资源脱离了业务的 Quota 管控。
应对策略
为了消除内存 OOM 风险和避免业务资源容量规划上的偏差,蚂蚁团队制定了新的“共享内存”策略。该策略主要内容:
Sidecar 的内存 request 被置为 0,不再向调度器额外申请资源。
Limit 被设置为应用的 1/4,保障 Sidecar 在正常运行的情况下,有充足的内存可用。
为了确实达到“共享”的效果,蚂蚁集团针对 Kubelet 做了调整,使之在设置 Sidecar 容器 Cgroups Limit 为应用 1/4 的同时,保证整个 Pod 的 Limit 没有额外增加。
新风险及解决方案
在上述应对策略下,会出现新的风险,蚂蚁也提出了对应的解决方案,说明如下:
风险:Sidecar 与应用“共享”分配到的内存资源,导致在异常情况(比如内存泄露)下,Sidecar 跟应用抢内存资源。
解决方案:通过扩展 Pod Spec(即相应的 apiserver、Kubelet 链路),为 Sidecar 容器额外设置了
Linux oom_score_adj
这个属性,以保障在内存耗尽的情况下,Sidecar 容器会被 OOM Killer 更优先选中,从而让 Sidecar 比应用能够更快速重启,从而更快恢复到正常服务。风险:在 CPU 资源的分配上,可能出现 MOSN 抢占不到 CPU 资源从而导致请求延迟大幅抖动。
解决方案:确保在注入 Sidecar 时,根据 Pod 内的容器数量,为每个 Sidecar 容器计算出相应的 cpushare 权重,通过工具扫描并修复全站所有未正确设置的 Pod。
原地注入
原地注入的背景为下述几个方面:
接入方式:相对比较简单的接入方式是在创建 Pod 的时候注入 Sidecar。此时应用只需先扩容,再缩容,就可以逐步用带有 Sidecar 的 Pod,替换掉旧的没有 Sidecar 的 Pod。
存在的问题: 在大量应用、大规模接入的时候,需要集群有较大的资源 Buffer 来供应用实例进行滚动替换,否则替换过程将变得十分艰难且漫长。
蚂蚁目标:双十一大促不加机器,提高机器使用率。
为了解决存在的问题并实现预期目标,蚂蚁团队提出了“原地注入”的概念,即在 Pod 不销毁,不重建的情况下,原地把 Sidecar 注入进去。
原地注入的步骤如下图所示:
在 PaaS 提交工单,选择一批需要原地注入的 Pod。
PaaS 调用中间件接口,关闭业务流量并停止应用容器。
PaaS 以 Annotation 的形式打开 Pod 上的原地注入开关。
Operator 观察到 Pod 原地注入开关打开,渲染 Sidecar 模版,注入到 Pod 中,并调整 CPU、Memory 等参数。
Operator 将 Pod 内的容器期望状态置为运行。
Kubelet 将 Pod 内的容器重新拉起。
PaaS 调用中间件接口,打开业务流量。
升级
蚂蚁团队将 RPC 等能力从基础库下沉到 Sidecar 之后,基础库升级与业务绑定的问题虽然消除了,但是这部分能力的迭代需求依然存在,只是从升级基础库变成了如何升级 Sidecar。
替换升级
最简单的升级就是替换,即销毁 Pod 并重新创建,这样新建出来的 Pod 所注入的 Sidecar 自然就是新版本了。
但通过替换的升级方式,与创建注入存在相似的问题,即需要大量的资源 Buffer,并且,这种升级方式对业务的影响最大,也最慢。
非平滑升级
为了避免销毁重建 Pod,蚂蚁团队通过 Operator 实现了“非平滑升级”能力,示例如下。
非平滑升级步骤:
PaaS 关流量,停容器。
Operator 替换 MOSN 容器为新版本,重新拉起容器。
PaaS 重新打开流量。
原地升级 Pod 打破了 Kubernetes Immutable Infrastructure 的设计,为了能够实现预期目标,蚂蚁团队修改了 apiserver validation 和 admission 相关的逻辑,以允许修改运行中的 Pod Spec,也修改了 Kubelet 的执行逻辑以实现容器的增、删、启、停操作。
平滑升级
为了进一步降低 Sidecar 升级对应用带来的影响,蚂蚁团队针对 MOSN Sidecar 开发了“平滑升级”能力,以实现在 Pod 不重建、流量不关停,应用无感知的条件下对 MOSN 进行版本升级。
平滑升级原理:Operator 通过注入新 MOSN,等待 MOSN 自身连接和 Metrics 数据迁移的完成,再停止并移除旧 MOSN,来达到应用无感、流量无损的效果。
努力方向:
提高成功率。
改进 Operator 的状态机来提升性能。
回滚
为了确保大促活动万无一失,蚂蚁团队还提供了 Sidecar 回滚的保底方案,以备在识别到 Service Mesh 出现严重问题的情况下,迅速将应用回滚到未接入 Sidecar 的状态,通过应用原先的能力继续提供业务服务。
风险管控
主要从下述几个角度来分析:
技术风险:关于 Sidecar 的所有运维操作,都要具备三板斧能力。在灰度能力上,Operator 为升级等运维动作增加了显式的开关,确保每个执行动作符合用户和 SRE(Site Reliability Engineer,简称 SRE)的期望,避免不受控制地或不被察觉地自动执行变更操作。
监控:在基本的操作成功率统计、操作耗时统计、资源消耗等指标之外,仍需以快速发现问题、快速止血为目标,继续完善精细化监控。
Operator 目前对外提供的几个运维能力,细节上都比较复杂,一旦出错,影响面又很大,因此单元测试覆盖率和集成测试场景覆盖率,也会是后续 Service Mesh 稳定性建设的一个重要的点去努力完善。
对未来的思考
演进到 Service Mesh 架构后,保障 Sidecar 自身能够快速、稳定地迭代十分重要。未来蚂蚁会向下述几个方向进行发力:
继续增强 Operator 的能力。
可能通过以下几个优化手段,来做到更好的风险控制:
对 Sidecar 模板做版本控制,由 Service Mesh 控制面,而非用户来决定某个集群下某个应用的某个 Pod 应该使用哪个版本的 Sidecar。这样既可以统一管控全站的 Sidecar 运行版本,又可以将 Sidecar 二进制和其 Container 模板相绑定,避免出现意外的、不兼容的升级。
提供更加丰富的模板函数,在保持灵活性的同时,简化 Sidecar 模板的编写复杂度,降低出错率。
设计更完善的灰度机制,在 Operator 出现异常后,快速熔断,避免故障范围扩大。
持续思考:整个 Sidecar 的运维方式能否更加“云原生”。