痛点
每一次容器申请失败直接造成研发测试停滞, 同时带来答疑及问题排查(程序猿最怕的就是在代码写得正嗨的时候被人给打断, 所以一般我都带耳机), 涉及到测试链路上各个系统随着集团 pouch 化的全面推进, 半年来测试环境日容器申请量暴增 10 倍以上, 低成功率导致研发低效的问题越来越凸显, 每天累计造成集团上百小时的研发测试停滞, 损失不可接受, 也渐渐成为了 pouch 化推进过程中的一个阻力
因此, 测试环境稳定性亟待大幅提升经过答疑汇总和错误分析, 主要集中在两个方面:
已成功申请的资源不可用
测试环境宿主机较差(过保机器), 且虚拟比高, 容易发生故障
宿主机故障时, 其上的容器不会被自动迁移, 很可能导致再次部署重启时失败
调度系统巡检会将故障宿主机置为不可调度, 由于其上仍有容器, 不能下线修复后重用, 造成机器资源越来越少
新申请资源时成功率低
测试环境机器被分为优先级不同的资源池, 资源池间机器资源不共享
测试环境机器的容量 / 余量不透明, 没有告警, 造成因资源不足的调度失败
因为测试环境与线上环境有很大不同, 资源调度系统之前没有针对测试场景优化, 成功率不高
目标
容器申请成功率: 99.9%
方案
指标数据
从一开始我们就觉的数据非常重要, 没有相关的稳定性数据, 那我们就无的放矢, 根据数据我们就能找到需要优化的点以及持续优化的动力所以项目开始阶段就做了挺长时间的数据收集工作
测试环境链路数据收集: 从上至下包括 Normandy(基础应用运维平台), 黄蜂(资源申请平台),Zeus(二层调度),Sigma(集团资源调度系统); 其中我们最关心的就是最终容器交付的成功率, 以及失败 case 失败 case 可以帮助我们分析整个系统中到底哪些地方存在问题, 成功率趋势则帮助我们检验新的修复优化是否真的有效且稳定, 也是最终成果的衡量指标
测试环境链路稳定性数据展示平台: 其实上下游的每个系统都有自己的数据, 但是没有整合, 有的用阿里表哥, 有的是发邮件, 有的则没有展示出来, 所以做这样一个小东西的目的就是将上下游系统的数据统一整合在一个页面上, 更便于查看分析问题
每日 / 周 / 月错误分析: 收集每天的错误数量及样例, 便于分析问题
已申请容器不可用
容器自动置换
容器自动置换是为了解决已申请的容器不可用问题, 简单来说就是在另一台好的宿主机上扩一个新容器, 然后将原来在故障宿主机上的旧容器下线
整个流程如下: Sigma(资源调度系统)自动巡检出故障宿主机 (比如磁盘满 / 硬件故障等), 通知 Atom(故障机替换) 置换该故障宿主机上容器, Atom 向 Normandy(基础应用运维平台)发起机器置换流程
通过自动置换将故障机腾空, 然后下线修复
新申请容器失败
合理化资源池分配
屏蔽底层系统失败
因为测试环境与线上环境差异很大, 一般测试环境使用的机器都是线上淘汰机, 同时为了节省预算, 每台宿主机的虚拟比都很高, 导致在创建和使用容器时都特别容易失败, 所以有必要做一个容器 buffer 池屏蔽掉底层失败对用户的影响
buffer 池的整个逻辑非常简单清晰: 在测试环境容器生产链路靠近用户的一端嵌入 buffer 池, 预生产一批容器, 在用户需要的时候分配给他即使申请 buffer 容器失败, 依然可以按原生产链路继续生产容器每次从 buffer 池申请一个容器后, buffer 池会自动异步补充一个相同规格的容器进来, 以维持 buffer 池的容量
如何确定 buffer 哪些规格的容器及池子的容量是另一个关键点: 需要统计每种规格 - 镜像 - 资源池的历史申请量, 按比例分配每种 buffer 的容量同时为了保证即使在底层系统中断服务时, 整个系统依然对用户可用, 还需要确定高峰期的容器申请量, 可允许中断时长以及测试环境机器资源, 用来确定整个 buffer 池子的容量
还需要考虑的一点是, 用户也分为普通用户(研发测试人员), 系统用户(比如自动化测试系统等), 他们的优先级也不同, 需要优先保证普通用户可用
同时为了最大程度的降低引入 buffer 池后可能对用户造成的影响, buffer 池内加了许多动态开关, 用于及时屏蔽某些功能比如可针对具体应用设置是否需要修改容器主机名, 此操作非常耗时, 如果不改主机名, 则平均不到 1 秒内会申请成功; 如果某个应用不想使用 buffer, 也可立即屏蔽; 如果 buffer 池本身出现问题, 可以快速降级, 在整个链路中去掉 buffer 功能
另外 buffer 池在交付 buffer 容器前会额外做一次检查, 判断容器是否可用, 避免容器交付后, 因为容器不可用而导致的服务部署失败, 用户使用不了等问题 buffer 池内部也会定期清理脏容器 (不可用, 数据不一致等) 和补充新的 buffer 容器
总结
上图展示测试环境最近 2 个月的容器申请成功率趋势, 包括 buffer 池全量前后一个月
从图中可以看到, 11 月末 12 月初的两天成功率极低, 均是因为调度失败, 之后根据资源池余量预测及报警及时调整了各个资源池的容量, 提前消除了调度失败的可能, 在此之后, 成功率波幅都减少很多
另一点, 从 buffer 全量后, 成功率波幅明显比 buffer 全量前大幅减小, 波动次数明显减少, 成功率趋于稳定
buffer 池全量后的一周内, 由于 buffer 池内部的 bug 以及 buffer 命中率较低, 成功率浮动较大, 在 bug 修复以及提高 buffer 池命中率后, 成功率基本稳定
上图展示近两个月的每日成功率趋势图, 纵向对比了用户视角 (有 buffer) 与底层系统视角 (无 buffer) 从图中可以看出, buffer 池确实屏蔽了许多底层系统失败, 除了其中一天 buffer 池被穿透导致成功率大跌
展望
虽然经过一系列改造优化后, 成功率有了明显的提升, 但是依然有很多地方需要完善:
资源池容量自动调配: 目前算法简单, 有些情况无法解决, 比如大规模的新增或删除容器造成对余量趋势的误判另外也要避免引入自动调配后造成宿主机标签的混乱
buffer 池模版动态的增减以及每种 buffer 的数量动态变化当前 buffer 池一个难题就是如何覆盖到低频的应用镜像, 这种镜像虽然低频但是容易申请失败, 一旦这种容器大量申请, 容易穿透 buffer 池, 造成大量失败
扩大 buffer 池的容量, 需要根据机器资源伸缩
除了对以前工作的完善, 测试环境依然有许多要做的事情: 比如如何提高整个测试环境资源的利用率, 如何减少容器交付耗时(从用户申请到用户可用), 如何推动应用的可调度化等等, 希望能够和大家一起探讨
来源: https://yq.aliyun.com/articles/509566