系统成功率99.99%+,美团CI/CD流水线引擎演进实践( 二 )


文章图片
图1流水线概念
2)基本概念组件:出于代码复用和业务共享的考虑 , 我们将某一工具的操作行为封装成一个组件 , 表示对于一项具体的加工或校验行为 。 通过组件方式 , 业务可以便捷地使用已集成的质量工具(如静态代码扫描、安全漏洞分析等) , 减少在同一工具上的重复开发成本;对于不满足需求的场景 , 业务可以自定义一个新的组件 。 组件作业:表示组件的一次运行实例 。 资源:为组件作业分配的一个可执行环境 。 流水线编排:表示流水线中不同组件执行的先后顺序 。 引擎:负责调度所有的组件作业 , 为其分配相应的执行资源 , 保证流水线执行按预期完成 。
2、主要挑战
1)调度效率瓶颈
对调度时间相对敏感 , 流水线大部分是短时作业(作业持续数十秒到分钟不等) , 如果调度时间过长 , 业务能明显感知到流水线执行变慢了 。 我们需要保证作业调度时间在一个可控的范围内 , 避免出现调度瓶颈 。 从业务场景考虑 , 调度逻辑存在一定的业务复杂性(如组件串并行判断、优先级抢占、降级跳过、复用上一次结果等) , 不仅仅是作业与资源的匹配计算 , 作业调度耗时存在一定的业务开销 。 引擎支撑公司每天近十万次的执行量 , 峰值量情况下 , 并发调度的作业量大 , 常见的开源工具(Jenkins/GitLabCI/Tekton等)都是采用单体调度模式 , 作业是串行调度的 , 容易出现调度瓶颈 。
2)资源分配问题
对于作业系统来说 , 作业数通常都是大于资源数的(真实部署情况 , 资源不是无限的) , 作业积压是系统设计时必须考虑的问题 。 如何在有限的资源下 , 尽可能提高作业的吞吐能力 , 同时降低在资源不足情况时造成对核心业务场景的影响 。 如果只依靠动态扩容 , 容易出现资源不足时无法扩容、作业排队等待的情况 。 特别是对于依赖流水线做研发卡控的业务 , 这会直接阻塞业务的上线流程 。 出于执行耗时的考虑 , 大部分资源采用预部署的方式 , 缩短资源申请和应用启动的准备时间 。 而对于预部署的资源 , 如何进行有效划分 , 既保证每类资源都有一定配额 , 同时也避免出现部分资源利用率过低 , 影响作业整体的吞吐能力 。 不是所有工具的执行资源都由引擎管理(如发布系统 , 部署任务的资源管理是单独的) , 在作业的资源分配上 , 还需要考虑不同的资源管理方式 。
3)工具差异化问题
公司内不同业务的差异化大 , 涉及的质效类工具众多 , 如何设计一个合适的插件化架构 , 满足不同工具的接入需求 。 不同工具实现形式差异化大 , 有些工具有独立的平台 , 可以通过接口方式进行集成 , 有些仅仅是一段代码片段 , 还需要提供相应的运行环境 。 面对不同的接入形态 , 引擎如何屏蔽不同工具带来的差异 , 使业务在编排流水线时不用关注到工具的实现细节 。 随着业务场景的不断丰富 , 组件执行还会涉及人工交互(审批场景)、支持重试、异步处理、故障恢复等能力 , 这些能力的扩展如何尽可能减少对系统的冲击 , 降低实现的复杂度 。
3、解决思路
1)拆分调度决策与资源分配 , 解决调度效率瓶颈
从上述分析 , 一个作业的实际调度耗时=单个作业的调度耗时*待调度的作业数 。 因为单个作业的调度耗时会受具体的业务逻辑影响 , 不确定性大 , 优化空间有限 。 而串行调度问题相对明确 , 在作业调度时间和数量不可控的情况下 , 是一个合适的优化方向 。
关于串行调度 , 业界常见的做法是按照业务线维度拆分多个集群 , 分摊总的调度压力 。 但这种方式存在的问题是资源分配不具备灵活性 , 很容易出现资源的分配不均 , 在整体资源不足时 , 无法从全局上考虑高优作业的资源分配 。 并且 , 多集群管理(新增集群/拆分现有集群)也是不小的运维负担 。