Harness 中的超时继承与传播语义

张开发
2026/4/19 1:23:16 15 分钟阅读

分享文章

Harness 中的超时继承与传播语义
Harness 中的超时继承与传播语义:像搭积木一样管理你的DevOps流水线时间关键词:Harness 超时继承 Harness 超时传播 DevOps CI/CD 流水线配置 时间管理 异常处理摘要:你有没有玩过乐高?搭好一架大飞机,翅膀上的小螺旋桨坏了,整个飞机会不会停下来?或者我们给螺旋桨单独规定“1分钟必须装好”,如果装不好是只换螺旋桨还是拆整架飞机?在DevOps的世界里,Harness流水线就像你搭的乐高,超时配置就像给每个零件和整个成品定的“完成时间闹钟”——超时继承就是小零件的时间闹钟可以跟着大部件走,超时传播就是零件闹钟响了要不要影响大部件。这篇文章会用搭乐高的故事,把这两个复杂的技术概念讲得明明白白,还会给你看Harness里的真实配置、Python模拟的代码、数学模型,甚至教你做一个迷你超时管理系统!背景介绍目的和范围我们为什么要研究这个?想象一下,你周末用Harness搭了个“给猫咪做罐头生产线”的流水线:采购原材料(大部件,比如鸡胸肉、南瓜)清洗原材料(大部件下的小步骤,比如洗南瓜、洗鸡胸肉)烹饪原材料(大部件,比如蒸南瓜、煮鸡胸肉)分装密封(大部件,比如装罐、贴标签)质检交付(最后一步)如果整个流水线定了“1小时做完”的闹钟,但洗鸡胸肉这个小步骤卡住了——比如水龙头坏了修了50分钟。按照默认规则,1小时到了整个流水线就炸了,可是你明明已经完成了采购、洗南瓜,说不定还蒸了一半南瓜!如果能给小步骤单独定个“5分钟必须洗完”的闹钟,炸的时候只重新洗鸡胸肉,会不会更省时间?或者采购环节卡住了,能不能让采购的超时直接影响后面所有环节的等待时间?这就是我们要研究的超时继承(小步骤默认用大部件的闹钟)和超时传播(小闹钟响了要不要炸大闹钟,大闹钟炸了要不要影响后面所有)——它们是DevOps流水线时间管理的核心,直接决定了你的流水线能不能更快(失败了只重试最小单元)、更稳(不会因为一个小问题浪费所有成果)、更可控(清楚知道每个环节能花多久)。这篇文章讲什么?这篇文章会:用搭乐高的故事讲清楚Harness超时继承与传播的核心概念对比Harness不同资源类型(Pipeline、Stage、Step、Step Group)的超时属性画ER图、架构图、交互图,理清它们之间的关系用Python模拟Harness的超时逻辑,让你亲手摸到超时是怎么工作的给出Harness里YAML配置的真实例子,教你快速上手分析实际应用场景(比如微服务部署、机器学习训练)聊一聊这个技术的发展历史和未来趋势最后留几个有趣的思考题,让你自己当DevOps工程师这篇文章不讲什么?Harness的基础使用(比如怎么注册账号、怎么拖拖拽拽搭Pipeline)其他CI/CD工具的超时管理(比如Jenkins、GitHub Actions的,只会偶尔提一下对比)太底层的Harness实现源码(只讲对外暴露的语义和逻辑,不会扒Go代码)预期读者这篇文章适合所有对DevOps感兴趣的人:小学生级别的DevOps新手:没关系,我们用乐高讲故事,你肯定能懂Harness的初级用户:你已经会搭Pipeline了,但超时配置总是搞混?这篇文章帮你理清Harness的高级用户:你想优化流水线的失败重试策略?这篇文章给你工具和灵感DevOps架构师:你想设计自己的超时管理系统?这篇文章的数学模型和Python模拟可以给你参考文档结构概述我们的文章会像搭乐高一样,从最小的零件(Step超时)开始,慢慢搭成最大的成品(Pipeline超时):核心概念与联系:用乐高讲故事,定义5个核心资源类型的超时属性,对比它们的差异,画ER图、架构图、交互图核心算法原理 具体操作步骤:讲超时继承的“向上查找规则”和超时传播的“四种失败策略”,用Python写个迷你系统模拟数学模型和公式 详细讲解 举例说明:用数学公式计算每个Step的实际可用时间,举3个不同场景的例子项目实战:Harness真实配置和Python迷你系统:教你怎么在Harness里写YAML配置超时,怎么运行我们的Python迷你系统实际应用场景:微服务部署、机器学习训练、大数据ETL这三个场景怎么用超时继承与传播工具和资源推荐:Harness官方文档、超时配置检查工具、其他CI/CD工具的对比文章未来发展趋势与挑战:超时的智能预测、动态调整、跨Pipeline传播总结:回顾核心概念和关系思考题:动动小脑筋,自己当DevOps工程师附录:常见问题与解答、扩展阅读术语表核心术语定义为了让大家都懂,我们先把所有术语翻译成乐高语言:Harness术语乐高语言翻译标准定义Pipeline整个乐高成品(比如一架大飞机)Harness中最顶层的资源,由一个或多个Stage组成,代表一个完整的DevOps流程Stage大飞机的主要部件(比如机身、左翅膀、右翅膀、尾翼)Pipeline中的中间资源,由一个或多个Step或Step Group组成,代表流程中的一个大阶段Step Group主要部件下的一组小零件(比如左翅膀上的所有小螺旋桨和机翼片)Stage中的中间资源,由一个或多个Step组成,用来把相关的Step组织在一起Step最小的零件(比如左翅膀上的一个小螺旋桨)Harness中最底层的执行单元,代表具体的操作(比如Git Clone、Docker Build、Kubernetes Deploy)Timeout(超时)零件/部件/成品的“完成时间闹钟”资源允许的最大执行时间,超过这个时间资源会触发超时失败Timeout Inheritance(超时继承)小零件/部件的闹钟默认用大部件的闹钟如果子资源没有配置自己的超时,会自动使用最近的已配置超时的父资源的超时Timeout Propagation(超时传播)小零件的闹钟响了要不要换整个大部件,大部件的闹钟响了要不要拆整个成品子资源超时失败后,对父资源和后续资源的影响方式Failure Strategy(失败策略)闹钟响了怎么办的规则(比如换零件、换部件、拆成品、继续玩)Harness中定义资源失败(包括超时失败、代码错误失败等)后采取的行动相关概念解释Retry(重试):乐高零件坏了,重新拿一个新的装上去(对应资源失败后重新执行一次或多次)Skip(跳过):这个零件暂时装不好,先装其他的(对应资源失败后跳过,继续执行后续资源)Abort(终止):这个零件坏了,整个乐高不搭了(对应资源失败后终止整个Pipeline)Rollback(回滚):装了新零件后飞机飞不起来,拆下来装回旧的(对应部署类资源失败后,回滚到之前的正常版本)Actual Time Budget(实际可用时间预算):零件实际能花的时间(比如整个飞机定了1小时,机身已经花了40分钟,左翅膀的实际可用时间就是20分钟)缩略词列表缩略词全称中文翻译CIContinuous Integration持续集成CDContinuous Delivery/Deployment持续交付/部署DevOpsDevelopment + Operations开发运维一体化YAMLYAML Ain’t Markup Language一种人类可读的数据序列化语言,Harness用它来配置PipelineETLExtract Transform Load数据抽取、转换、加载K8sKubernetes一个开源的容器编排平台核心概念与联系故事引入:搭乐高飞机的时间烦恼小明是个乐高迷,周末他想用爸爸刚买的“超级航天飞机”乐高套装拼一架大飞机,准备周一拿到学校去炫耀。爸爸给他定了个规则:整个航天飞机必须在2小时内拼完,不然周一不能带去学校(这就是Pipeline的超时配置)。航天飞机套装有四个大袋子,对应四个Stage:袋子A:机身(Stage A,爸爸单独定了1小时的闹钟,因为机身最大最难拼)袋子B:左翅膀(Stage B,没有单独定闹钟)袋子C:右翅膀(Stage C,没有单独定闹钟)袋子D:尾翼+螺旋桨(Stage D,没有单独定闹钟)每个大袋子里又有小袋子,对应Step Group或Step:袋子A1:机身框架(Step Group A1,小明自己定了30分钟的闹钟)Step A1a:拼机身底部(没有单独定闹钟)Step A1b:拼机身顶部(没有单独定闹钟)袋子A2:驾驶舱(Step Group A2,没有单独定闹钟)Step A2a:拼驾驶舱外壳(小明自己定了10分钟的闹钟)Step A2b:装宇航员小人(没有单独定闹钟)袋子D1:螺旋桨组(Step Group D1,没有单独定闹钟)Step D1a:拼左主螺旋桨(没有单独定闹钟)Step D1b:拼右主螺旋桨(没有单独定闹钟)Step D1c:拼尾翼小螺旋桨(小明自己定了5分钟的闹钟)现在开始拼啦!小明首先拆了袋子A1,拼机身底部:袋子A1的闹钟是30分钟(自己定的)机身底部Step A1a没有定闹钟,所以用袋子A1的30分钟(超时继承)小明拼得很顺利,Step A1a花了10分钟,Step A1b花了15分钟,袋子A1总共花了25分钟,提前完成了!接下来拆袋子A2,拼驾驶舱外壳Step A2a:Step A2a自己定了10分钟的闹钟(超时继承最近的自己的配置)哎呀!小明找不到驾驶舱的透明玻璃片了!翻了5分钟,找到了,但拼的时候又卡了3分钟——总共花了8分钟,还差2分钟!哦,刚好找到了透明胶带,小明想:“透明胶带虽然不是原装的,但先粘上去应急吧,反正爸爸不仔细看。”——Step A2a刚好在10分钟内完成了!然后装宇航员小人Step A2b,花了2分钟,袋子A2总共花了12分钟——袋子A没有定Step Group的总时间,所以袋子A的闹钟是1小时(自己定的),袋子A总共花了25+12=37分钟,提前完成了!接下来拆袋子B拼左翅膀:袋子B没有定闹钟,所以用袋子A的1小时?不对不对!应该用整个Pipeline的2小时?哦,我们马上讲超时继承的“向上查找规则”,这里先卖个关子!核心概念解释(像给小学生讲故事一样)现在我们用小明拼乐高的例子,正式解释五个核心资源类型的超时属性,以及超时继承、超时传播这两个核心概念:核心概念一:Harness的五个资源类型和它们的超时属性Harness的资源类型就像乐高的零件层级:Step Step Group Stage Pipeline,还有一个特殊的资源类型叫Stage Group(不过Harness现在用得少了,我们这篇文章暂时不讲)。每个资源类型都有自己的超时属性,我们用乐高语言和标准定义一起讲:资源类型一:Step(最小的乐高零件)乐高语言翻译:最小的零件,比如左翅膀上的一个小螺旋桨、一个透明玻璃片、一个宇航员小人的头盔标准定义:Harness中最底层的执行单元,代表具体的操作,比如:CI类Step:Git Clone(从GitHub下载代码)、Maven Build(编译Java代码)、Docker Build(打包Docker镜像)CD类Step:Kubernetes Deploy(把Docker镜像部署到K8s)、Helm Upgrade(升级Helm Chart)、Rollback(回滚到之前的版本)通用类Step:Shell Script(执行Shell命令)、HTTP Request(发送HTTP请求)、Wait(等待一段时间)超时属性:timeout:单个Step的“完成时间闹钟”,单位可以是秒(s)、分钟(m)、小时(h),比如timeout: 10m代表这个Step必须在10分钟内完成timeoutInheritance:是否继承父资源的超时,Harness默认是true(继承),如果设为false,子资源必须自己配置超时,不然会报错failureStrategies:Step失败(包括超时失败、代码错误失败等)后怎么办的规则,我们后面讲超时传播的时候会详细讲资源类型二:Step Group(一组相关的乐高零件)乐高语言翻译:一组相关的小零件,比如左翅膀上的所有小螺旋桨和机翼片、驾驶舱的外壳和宇航员小人标准定义:Stage中的中间资源,由一个或多个Step组成,用来把相关的Step组织在一起,比如:CI类Step Group:“测试组”,包含单元测试、集成测试、端到端测试CD类Step Group:“部署前置检查组”,包含K8s集群健康检查、数据库连接检查超时属性:timeout:Step Group的“总完成时间闹钟”,比如timeout: 30m代表这个Step Group里的所有Step加起来(或者并行的话取最长的)必须在30分钟内完成timeoutInheritance:是否继承父资源的超时,默认truefailureStrategies:Step Group失败后怎么办的规则parallelism:Step Group里的Step是串行执行还是并行执行,Harness默认是串行(parallelism: 1),如果设为parallelism: 0或更大的数,就是并行执行——这个属性会影响超时的计算哦!资源类型三:Stage(大飞机的主要部件)乐高语言翻译:大飞机的主要部件,比如机身、左翅膀、右翅膀、尾翼标准定义:Pipeline中的中间资源,由一个或多个Step或Step Group组成,代表流程中的一个大阶段,比如:CI类Stage:“Build Stage”(构建阶段)、“Test Stage”(测试阶段)CD类Stage:“Deploy to Dev Stage”(部署到开发环境)、“Deploy to Prod Stage”(部署到生产环境)超时属性:timeout:Stage的“总完成时间闹钟”timeoutInheritance:是否继承父资源的超时,默认truefailureStrategies:Stage失败后怎么办的规则strategy:Stage的执行策略,比如矩阵策略(Matrix Strategy,比如同时部署到多个K8s集群)、滚动策略(Rolling Strategy,比如逐步替换生产环境的Pod)——这个属性也会影响超时的计算哦!资源类型四:Pipeline(整个乐高成品)乐高语言翻译:整个乐高成品,比如一架大飞机、一辆赛车、一座城堡标准定义:Harness中最顶层的资源,由一个或多个Stage组成,代表一个完整的DevOps流程,比如:完整的CI/CD Pipeline:“代码提交 → 构建 → 测试 → 部署到开发 → 部署到预发 → 部署到生产”大数据ETL Pipeline:“从MySQL抽取数据 → 清洗数据 → 转换数据 → 加载到Snowflake”超时属性:timeout:Pipeline的“总完成时间闹钟”timeoutInheritance:没有这个属性,因为Pipeline是最顶层的资源,没有父资源可以继承failureStrategies:Pipeline失败后怎么办的规则(其实Pipeline的失败策略主要由Stage的失败策略决定,但也可以在Pipeline层面配置全局的默认策略)核心概念二:超时继承(Timeout Inheritance)——小零件的闹钟默认用大部件的闹钟什么是超时继承?用小明拼乐高的例子讲:小明的左主螺旋桨Step D1a没有定自己的闹钟,那它的闹钟是多少呢?首先看它的父资源:螺旋桨组Step Group D1,也没有定闹钟;然后看Step Group D1的父资源:尾翼+螺旋桨Stage D,也没有定闹钟;最后看Stage D的父资源:整个航天飞机Pipeline,定了2小时的闹钟;所以Step D1a的闹钟就是2小时!这就是超时继承的核心逻辑:如果子资源没有配置自己的超时,会自动向上查找最近的已配置超时的父资源的超时,一直查到Pipeline为止。如果Pipeline也没有配置超时,Harness会用默认超时——不同资源类型的默认超时不一样哦!不同资源类型的默认超时Harness官方给的默认超时如下(2024年最新版,可能会变,以官方文档为准):资源类型默认超时乐高语言翻译Step10分钟一个小零件必须在10分钟内装好Step Group无(会继承父资源)一组小零件没有自己的总闹钟,用大部件的Stage无(会继承父资源)一个大部件没有自己的总闹钟,用整个成品的Pipeline6小时整个乐高成品必须在6小时内拼完超时继承的“向上查找规则”我们把刚才的逻辑整理成正式的向上查找规则,用流程图的话会更清楚,但这里先讲文字版:第一步:检查当前资源有没有配置自己的timeout属性,如果有,就用这个时间作为当前资源的基础超时;第二步:如果当前资源没有配置自己的timeout,检查timeoutInheritance属性是不是true(默认是true);如果timeoutInheritance是false,直接报错,说当前资源必须配置自己的超时;如果timeoutInheritance是true,向上查找父资源;第三步:对父资源重复第一步和第二步,直到找到一个已配置timeout的资源,或者查到Pipeline为止;第四步:如果查到Pipeline也没有配置timeout,就用当前资源类型的默认超时(比如Step的默认超时是10分钟)。我们用小明拼乐高的例子,演示几个Step的向上查找过程:例子一:Step D1c(尾翼小螺旋桨):检查Step D1c有没有配置自己的timeout?有!timeout: 5m;所以基础超时就是5分钟。例子二:Step A2b(装宇航员小人):检查Step A2b有没有配置自己的timeout?没有;检查timeoutInheritance?默认true;向上找父资源:Step Group A2(驾驶舱);检查Step Group A2有没有配置自己的timeout?没有;检查Step Group A2的timeoutInheritance?默认true;向上找父资源:Stage A(机身);检查Stage A有没有配置自己的timeout?有!timeout: 1h;所以Step A2b的基础超时就是1小时。例子三:Step B1(左翅膀的第一个小机翼片):检查Step B1有没有配置自己的timeout?没有;检查timeoutInheritance?默认true;向上找父资源:假设是Step Group B1(左翅膀框架),没有配置timeout;向上找父资源:Stage B(左翅膀),没有配置timeout;向上找父资源:Pipeline,有!timeout: 2h;所以Step B1的基础超时就是2小时。核心概念三:超时传播(Timeout Propagation)——小闹钟响了要不要炸大闹钟什么是超时传播?我们继续用小明拼乐高的例子讲:小明的尾翼小螺旋桨Step D1c定了5分钟的闹钟,但是他拼的时候手滑了,把小螺旋桨的叶片弄坏了——翻了3分钟备用零件袋,没有找到!现在5分钟到了,闹钟响了!小明现在有几个选择:重试几次:再翻一遍备用零件袋?或者用剪刀把坏的叶片修一修?(对应Retry策略)跳过这个小螺旋桨:反正尾翼小螺旋桨不影响飞机在桌子上“飞”(对应Skip策略)换整个螺旋桨组Step Group D1:尾翼小螺旋桨找不到,左主螺旋桨和右主螺旋桨也不装了,换一袋新的螺旋桨组?(对应Abort Step Group,然后Retry Step Group)换整个尾翼+螺旋桨Stage D:螺旋桨组找不到,尾翼也不装了,换一袋新的尾翼+螺旋桨?(对应Abort Stage D,然后Retry Stage D)拆整个航天飞机Pipeline:今天拼不完了,周一不带去学校了!(对应Abort Pipeline)这就是超时传播的核心逻辑:子资源超时失败后,对父资源和后续资源的影响方式,由子资源(或父资源、或全局)配置的Failure Strategy(失败策略)决定。Harness的四种核心失败策略Harness有四种核心失败策略,我们用乐高语言和标准定义一起讲:失败策略名称乐高语言翻译标准定义适用场景Retry(重试)零件坏了,重新拿一个新的装上去,或者修一修坏的零件,最多试N次资源失败后,重新执行一次或多次,每次重试之间可以等待一段时间暂时性错误,比如网络超时、数据库连接超时、Git Clone超时(可能是GitHub抽风)Skip(跳过)这个零件暂时装不好,先装其他的,最后再想办法资源失败后,跳过当前资源,继续执行后续资源非关键资源,比如端到端测试中的某个非核心功能测试、部署前置检查中的某个可选检查Abort(终止)这个零件坏了,整个乐高/大部件/一组零件不搭了资源失败后,终止当前资源的执行,然后根据配置决定是终止父资源还是继续关键资源,比如Docker Build失败、单元测试失败、生产环境部署失败Manual Intervention(人工干预)这个零件坏了,喊爸爸来看看怎么办资源失败后,暂停Pipeline的执行,等待人工确认是重试、跳过还是终止不确定的错误,比如预发环境部署后的冒烟测试失败,需要人工检查是不是代码问题还是环境问题失败策略的“配置层级”和“覆盖规则”失败策略可以在四个层级配置:全局Pipeline层级:在Pipeline的default属性里配置,所有没有自己配置失败策略的资源都会用这个Stage层级:在Stage的failureStrategies属性里配置,会覆盖全局Pipeline的配置,Stage下的所有没有自己配置失败策略的Step/Step Group都会用这个Step Group层级:在Step Group的failureStrategies属性里配置,会覆盖Stage的配置,Step Group下的所有没有自己配置失败策略的Step都会用这个Step层级:在Step的failureStrategies属性里配置,会覆盖所有父资源的配置,优先级最高覆盖规则:子资源的失败策略会覆盖父资源的失败策略,最底层的Step优先级最高。我们用小明拼乐高的例子,演示失败策略的覆盖:全局Pipeline的默认失败策略是:只要有资源失败,就终止整个Pipeline(对应乐高语言:只要有一个零件坏了,整个飞机不搭了)Stage A(机身)的失败策略是:如果是Step失败,先重试2次,每次等待1分钟,还不行就终止Stage A(对应乐高语言:如果机身的某个小零件坏了,先试2次,每次等1分钟找备用零件,还不行就换整个机身)Step A2a(驾驶舱外壳)的失败策略是:如果超时失败,先重试1次,每次等待30秒,还不行就跳过Step A2a(对应乐高语言:如果驾驶舱外壳的透明玻璃片找不到,先等30秒再找一遍,还不行就用透明胶带粘上去,跳过“找原装透明玻璃片”这个步骤)现在Step A2a超时了:首先看Step A2a自己的失败策略:重试1次,等待30秒;重试后还是超时了;Step A2a的失败策略说:还不行就跳过;所以跳过Step A2a,继续执行Step A2b(装宇航员小人)。如果Step A2a没有自己的失败策略,就会用Stage A的失败策略:重试2次,每次等待1分钟,还不行就终止Stage A;如果Stage A也没有自己的失败策略,就会用全局Pipeline的失败策略:终止整个Pipeline。核心概念之间的关系(用小学生能理解的比喻)超时继承与传播的关系:像一个团队的“作息时间”和“请假规则”我们换一个比喻,把Harness的资源类型比作一个学校的班级团队:Pipeline:整个学校,定了“全校每天8小时上课”的作息时间(全局超时),定了“如果有班级严重违纪,全校停课”的请假规则(全局失败策略)Stage:每个班级,比如三年级1班,定了“三年级1班每天7.5小时上课”的作息时间(自己的超时,覆盖全校的),定了“如果有学生严重违纪,班级停课1天”的请假规则(覆盖全校的)Step Group:每个班级的小组,比如三年级1班的第一组,没有定自己的作息时间(继承班级的),定了“如果有学生请假,小组继续上课”的请假规则(覆盖班级的)Step:每个学生,比如小明,定了“小明每天最多花10分钟写数学作业”的作息时间(自己的超时),定了“如果小明数学作业写不完,先问同桌1次,还不行就跳过数学作业”的请假规则(覆盖所有的)现在我们来看超时继承与传播的关系:超时继承是团队的“作息时间分配”:每个学生/小组/班级的作息时间,默认跟着上级走,除非自己单独定超时传播是团队的“请假处理流程”:如果某个学生/小组/班级违反了作息时间(超时了),按照谁定的规则处理,子规则覆盖父规则五个资源类型的关系:像一个“俄罗斯套娃”我们再换一个比喻,把Harness的资源类型比作一个俄罗斯套娃:Pipeline:最大的套娃Stage:最大的套娃里的中等套娃Step Group:中等套娃里的小套娃Step:最小的套娃现在我们来看超时继承与传播在俄罗斯套娃里的表现:超时继承:每个套娃的“保质期”,默认跟着外面的套娃走,除非自己单独贴标签超时传播:如果某个套娃坏了(超时了),按照标签上的规则处理——要么换这个套娃,要么换外面的套娃,要么把所有套娃都扔掉五个资源类型的超时属性对比(markdown表格)为了让大家更清楚,我们把五个资源类型的超时属性、默认超时、配置层级优先级整理成一个markdown表格:资源类型是否有父资源是否有timeout属性是否有timeoutInheritance属性默认超时配置层级优先级(覆盖顺序)failureStrategies配置层级Step是(Step Group或Stage)是是(默认true)10分钟1(最高)Step → Step Group → Stage → PipelineStep Group是(Stage)是是(默认true)无(继承父资源)2Step Group → Stage → PipelineStage是(Pipeline)是是(默认true)无(继承父资源)3Stage → PipelinePipeline否是否6小时4(最低)Pipeline(全局默认)Stage Group是(Pipeline)是是(默认true)无(继承父资源)3Stage Group → Pipeline五个资源类型的ER实体关系图(Mermaid)现在我们用Mermaid画一个ER实体关系图,展示五个资源类型之间的“父子关系”:containscontainscontainscontainscontainscontainsPIPELINEstringidstringnamedurationtimeoutfailure_strategiesdefaultFailureStrategies

更多文章