蚂蚁金服 Service Mesh 大规模落地系列 - 运维篇

Service Mesh-运维

《蚂蚁金服 Service Mesh 大规模落地系列》将会从核心、RPC、消息、无线网关、控制面、安全、运维、测试等模块对 Service Mesh 双十一大规模落地实践进行详细解析,文末包含往期系列文章。本文为该系列文章的第三篇 - 运维篇。

引言

Service Mesh 是蚂蚁金服下一代架构的核心,也是蚂蚁金服内部向云原生演进的重要一环。本文为 Service Mesh 系列文章的运维篇,作者:黄家琦 (花名:嘉祁),蚂蚁金服运维专家,Service Mesh SRE,主要关注云原生基础设施、中间件及 Service Mesh 的稳定性,同时也是 Pythoner,sofa-bolt-python 作者。

本文将主要分享大规模服务网格在蚂蚁金服当前体量下落地到支撑蚂蚁金服双十一大促过程中,运维角度所面临的挑战与演进。内容包括云原生化的选择与问题,对资源模型的挑战,大规模下运维设施的演进,以及周边技术风险能力的建设。

Service Mesh 在2019年得到了大规模的应用与落地,截止目前,蚂蚁金服的 Service Mesh 数据平面 MOSN 已接入应用数百个,接入容器数量达数十万,是目前已知的全世界最大的 Service Mesh 集群。同时,在刚刚结束的双十一大促中,Service Mesh 的表现也十分亮眼,RPC 峰值 QPS 达到了几千万,消息峰值 TPS 达到了几百万,且引入 Service Mesh 后的平均 RT 增长幅度控制在 0.2 ms 以内。

拥抱云原生

Service Mesh 在软件形态上,是将中间件的能力从框架中剥离成独立软件。而在具体部署上,保守的做法是以独立进程的方式与业务进程共同存在于业务容器内。我们在蚂蚁金服内部的做法,则从开始,就选择了拥抱云原生。

Sidecar 模式

业务容器内独立进程的好处在于与传统的部署模式兼容,易于快速上线;但独立进程强侵入业务容器,对于镜像化的容器更难于管理。而云原生化,则可以将 Service Mesh 本身的运维与业务容器解耦开来,实现中间件运维能力的下沉。在业务镜像内,仅仅保留长期稳定的 Service Mesh 相关 JVM 参数,从而仅通过少量环境变量完成与 Service Mesh 的联结。同时考虑到面向容器的运维模式的演进,接入 Service Mesh 还同时要求业务完成镜像化,为进一步的云原生演进打下基础。

独立进程 兼容传统的部署模式;改造成本低;快速上线 侵入业务容器;镜像化难于运维
Sidecar 面向终态;运维解耦 依赖 K8s 基础设施;运维环境改造成本高;应用需要镜像化改造

在接入 Service Mesh 之后,一个典型的 POD 结构可能包含多个 Sidecar:

  • MOSN:RPC Mesh, MSG Mesh, …(扩展中);
  • 其它 Sidecar;

MOSN:https://github.com/sofastack/sofa-mosn

Sidecar 模式

这些 Sidecar 容器,与业务容器共享相同的网络 Namespace,使得业务进程可以以本地端口访问 Service Mesh 提供的服务,保证了与保守做法一致的体验。

基础设施云原生支撑

我们也在基础设施层面同步推进了面向云原生的改造,以支撑 Service Mesh 的落地。

业务全面镜像化

首先是在蚂蚁金服内部推进了全面的镜像化,我们完成了内部核心应用的全量容器的镜像化改造。改造点包括:

  • 基础镜像层面增加对于 Service Mesh 的环境变量支撑;
  • 应用 Dockerfile 对于 Service Mesh 的适配;
  • 推进解决了存量前后端分离管理的静态文件的镜像化改造;
  • 推进了大量使用前端区块分发的应用进行了推改拉的改造;
  • 大批量的 VM 模式的容器升级与替换;

容器 POD 化

除了业务镜像层面的改造,Sidecar 模式还需要业务容器全部跑在 POD 上,来适应多容器共享网络。由于直接升级的开发和试错成本很高,我们最终选择将接入 Service Mesh 的 数百个应用的数万个非 K8s 容器,通过大规模扩缩容的方式,全部更换成了 K8s PODs。

经过这两轮改造,我们在基础设施层面同步完成了面向云原生的改造。

资源的演进

Sidecar 模式的带来一个重要的问题,如何分配资源。

理想比例的假设

最初的资源设计基于内存无法超卖的现实。我们做了一个假设:

  • MOSN 的基本资源占用与业务选择的规格同比例这一假设。

CPU 和 Memory 申请与业务容器相应比例的额外资源。这一比例最后设定在了 CPU 1/4,Memory 1/16。

此时一个典型 Pod 的资源分配如下图示:

典型 Pod 的资源分配

这一方式带来了两个问题:

  1. 蚂蚁金服已经实现了业务资源的 Quota 管控,但 Sidecar 并不在业务容器内,Service Mesh 容器成为了一个资源泄漏点;
  2. 业务很多样,部分高流量应用的 Service Mesh 容器出现了严重的内存不足和 OOM 情况;

完美分割的不完美

不止于此,为了快速支撑 Service Mesh 在非云环境的铺开,上线了原地接入 Service Mesh。而原地接入 Service Mesh 的资源无法额外分配,在内存不能超卖的情况下,采取了二次分割的分配方式。此时的 POD 内存资源被切分为1/16内存给 Sidecar,与15/16给业务容器。除了以上两个问题,还带来一些新的问题:

  • 业务可见内存不一致,业务监控偏差,业务进程 OOM 风险。

讨论之后,我们追加了一个假设:

  • Service Mesh 容器占用的资源实质是在接入 Service Mesh 之前业务已使用的资源。接入 Service Mesh 的过程,同时也是一次资源置换。

共享

基于这个假设,推进了调度层面支持 POD 内的资源超卖,新的资源分配方案如下图,Service Mesh 容器的 CPU、MEM 都从 POD 中超卖出来,业务容器内仍然可以看到全部的资源。

新的资源分配方案

考虑到内存超卖也引入了 POD OOM 的风险,因此对于 Sidecar 容器还调整了 OOM Score,保证在内存不足时,Service Mesh 进程能够发挥启动比 Java 业务进程更快的优势,降低影响。

新的分配方案解决了同时解决了以上两个问题,并且平稳支持了大促前的多轮压测。

重建

但新的分配方案上线时,Service Mesh 已经在弹性建站时同步上线。同时我们还发现在一些场景下,Service Mesh 容器无法抢占到 CPU 资源,导致业务 RT 出现了大幅抖动,原因是在 CPU Share 模式下,POD 内默认并没有等额的分配 CPU Quota 给 Sidecar。

于是还有两个问题待解决:

  • 存量的已分配 Sidecar 仍有 OOM 风险;
  • Sidecar 无法抢占到 CPU;

我们已经无法承受更换全部 POD 的代价。最终在调度的支持下,通过对 Pod Annotation 的手动重新计算+修改,在 POD 内进行了全部资源的重分配,来修复这两个风险。最终的修复容器总数约 25w 个。

变更与规模化下的运维挑战

Service Mesh 的变更包括了接入与升级,所有变更底层都是由 Operator 组件来接受上层写入到 POD annotation 上的标识,对相应 POD Spec 进行修改来完成,这是典型的云原生的方式。由于蚂蚁金服的资源现状与运维需要,又发展出了原地接入与平滑升级。与 Operator 有关的具体细节在 Operator 篇中会详细介绍,请持续关注本公众号。

接入

最初的 Service Mesh 接入只提供了创建时注入 Sidecar。之后引入原地接入的原因,是为了支撑大规模的快速接入与回滚。

  • 创建接入:
    • 资源替换过程需要大量 Buffer;
    • 回滚困难;
  • 原地接入:
    • 不需要重新分配资源;
    • 可原地回滚;

原地接入/回滚需要对 POD Spec 进行精细化的修改,实践中发现了很多问题,当前能力只做了小范围的测试。

升级

Service Mesh 是深度参与业务流量的,因此最初的 Sidecar 的升级方式也需要业务伴随重启。看似简单的这个过程中,我们也遇到了一个严重问题:

  • Pod 内的容器启动顺序随机导致业务无法启动。

这个问题最终依赖于调度层修改了启动逻辑,POD 内需要优先等待所有 Sidecar 启动完成,于是带来第二个问题:

  • Sidecar 启动慢了,上层超时。

此问题仍在解决中。

Sidecar 中,MOSN 提供了更为灵活的平滑升级机制:由 Operator 控制启动第二个 MOSN Sidecar,完成连接迁移,再退出旧的 Sidecar。小规模测试显示,整个过程业务可以做到流量不中断,几近无感。目前平滑升级同样涉及到 POD Spec 的大量操作,考虑到大促前的稳定性,目前此方式未做大规模使用。

规模化的问题

在逐渐达到大促状态的过程中,接入 Service Mesh 的容器数量开始大爆炸式增加。容器数量从千级别迅速膨胀到10w+,最终达到全站数十万容器规模,并在膨胀后还经历了数次版本变更。

快速奔跑的同时,缺少相应的平台能力也给大规模的 Sidecar 运维带来了极大挑战:

  • 版本管理混乱:
    • Sidecar 的版本与应用/ Zone 的映射关系维护在内部元数据平台的配置中。大量应用接入后,全局版本,实验版本,特殊 Bugfix 版本等混杂在多个配置项中,统一基线被打破,难于维护。
  • 元数据不一致:
    • 元数据平台维护了 POD 粒度的 Sidecar 版本信息,但是由于 Operator 是面向终态的,会出现元数据与底层实际不一致的情况,当前仍依赖巡检发现。
  • 缺少完善的 Sidecar ops 支撑平台:
    • 缺少多维度的全局视图;
    • 缺少固化的灰度发布流程;
    • 依赖于人工经验配置管理变更;
  • 监控噪声巨大;

当然,Service Mesh 与 PaaS 的开发团队都已经在建设相应的一些能力,这些问题正得到逐步的缓解。

技术风险建设

监控能力

我们的监控平台为 Service Mesh 提供了基础的监控能力和大盘,以及应用维度的 Sidecar 的监控情况。包括:

  • 系统监控:
    • CPU;
    • MEM;
    • LOAD;
  • 业务监控:
    • RT;
    • RPC 流量;
    • MSG 流量;
    • Error 日志监控;

Service Mesh 进程还提供了相应的 Metrics 接口,提供服务粒度的数据采集与计算。

巡检

在 Service Mesh 上线后,巡检也陆续被加入:

  • 日志 Volume 检查;
  • 版本一致性;
  • 分时调度状态一致;

预案与应急

Service Mesh 自身具备按需关闭部分功能的能力,当前通过配置中心实现:

  • 日志分级降级;
  • Tracelog 日志分级降级;
  • 控制面(Pilot)依赖降级;
  • 软负载均衡长轮询降级;

对于 Service Mesh 依赖的服务,为了防止潜在的抖动风险,也增加了相应的预案:

  • 软负载均衡列表停止变更;
  • 服务注册中心高峰期关闭推送;

Service Mesh 是非常基础的组件,目前的应急手段主要是重启:

  • Sidecar 单独重启;
  • POD 重启;

变更风险防控

除了传统的变更三板斧之外,我们还引入了无人值守变更,对 Service Mesh 变更做了自动检测,自动分析与变更熔断。

无人值守变更防控主要关注变更后对系统和业务和影响,串联了多层检测,主要包括:

  • 系统指标:包括机器内存、磁盘、CPU;
  • 业务指标:业务和 Service Mesh 的 RT、QPS 等;
  • 业务关联链路:业务上下游的的异常情况;
  • 全局的业务指标;

经过这一系列防控设施,可以将全站性的 Service Mesh 变更风险在单一批次变更内发现和阻断,避免了风险放大。

未来

Service Mesh 在快速落地的过程中,遇到并解决了一系列的问题,但同时也要看到还有更多的问题还亟待解决。做为下一代云原生化中间件的核心组件之一,Service Mesh 的技术风险能力还需要持续的建议与完善。 未来需要在下面这些方面持续建设:

  • 大规模高效接入与回滚能力支撑;
  • 更灵活的变更能力,包括业务无感的平滑/非平滑变更能力;
  • 更精准的变更防控能力;
  • 更高效,低噪声的监控;
  • 更完善的控制面支持;
  • 应用维度的参数定制能力;

欢迎有志于中间件 Service Mesh 化与云原生稳定性的同学加入我们,共同建设 Service Mesh 的未来。

往期系列阅读