Tekton 任务失败自动重试 3 次,如何避免把压测数据污染

解读

面试官真正关心的是:在 CI/CD 流水线里做性能测试时,如何既利用 Tekton 的“retryPolicy: 3”保证任务健壮性,又保证每一次重跑不会把脏数据(错误样本、半吊子指标、残留虚拟用户)混进正式报告,进而误导容量评估与 SLA 判定。
国内主流做法是把“可重试”与“可回溯”拆开:重试只负责把脚本跑通,数据落地前必须过“幂等闸门”。答到“幂等”“隔离”“后置标记”这三层,就能体现性能测试工程师对数据质量和平台稳定性的双重把控。

知识点

  1. Tekton 重试机制:TaskRun 的 retryPolicy 会在 Pod 异常退出时重新创建同名 Pod,但 PVC、Results、Sidecar 日志会被复用。
  2. 压测数据污染来源:
    – 脚本重复写同一张结果表,主键冲突或时间戳重叠;
    – 残留并发线程没被优雅终止,第二次重试时叠加;
    – 失败事务只回滚一半,产生“半条”采样。
  3. 性能测试数据治理原则:
    – 采样唯一性(UUID + 流水线 runID);
    – 结果可丢弃(失败批次标记为 invalid,不进入基线计算);
    – 环境可重置(脚本级 teardown 把负载发生器归零)。
  4. 国内常用技术栈:InFluxDB + Grafana、MySQL 结果表、Kafka 指标流、Prometheus Remote Write;均需提供“幂等写入”或“后置清洗”能力。
  5. 合规要求:金融与运营商客户要求保留原始脏数据 90 天以备审计,因此“物理删除”不可行,只能逻辑隔离。

答案

核心思路:让重试只负责“把脚本跑完”,数据先写“隔离区”,成功后再“晋升”到正式库;失败批次全程打标,不参与聚合。
落地四步:

  1. 入口幂等
    在 Task 启动前用 initContainer 生成全局唯一 runID(Tekton 自带 $(context.pipelineRun.name) 拼接 taskRetryCount),并注入到 JMeter/Gatling 启动参数;所有采样记录均带该 runID 字段。
  2. 脚本内事务化
    把“发压→采样→回写”包进一个逻辑事务:
    – 发压前清理上一次残留进程(pkill -f java.*Gatling);
    – 发压后通过 shutdown hook 保证虚拟用户归零;
    – 结果写入时先写临时表 *_tmp,成功后再 rename 到正式表;若检测到 retryCount>0,先 truncate 本 runID 对应的 tmp 分区。
  3. 结果晋升闸门
    Task 最后一步执行“晋升脚本”:
    – 检查 InFluxDB 的 last point 时间戳与预期一致;
    – 校验 MySQL 采样行数 >= 预期并发 * 持续时间 * 采样率;
    – 通过 tekton-results API 把 exitCode=0 写回;
    只有晋升成功,才把数据标记为 valid=1;否则保留脏数据但 valid=0,后续基线 SQL 统一加 where valid=1。
  4. 失败重试上限与告警
    在 Pipeline 层设置 retries=3,并配一个 finally Task:
    – 若最终仍失败,通过企业微信/飞书机器人发送“性能测试数据已标记为无效,请手动触发回滚”;
    – 同时调用 GitLab API 把 MR 打红叉,阻止合并。

通过以上机制,重试不会污染正式压测基线,又能保留现场供开发排查,符合国内金融、电商、运营商对“数据可审计”与“结果可回溯”的双重要求。

拓展思考

  1. 如果压测对象是无状态微服务,但下游 Kafka Topic 不支持幂等写,如何设计“可回滚”指标?
    答:在 Producer 端开启 transactional.id=runID,Task 成功时 commitTransaction,失败时 abortTransaction;Kafka 层开启 enable.idempotence=true,即可在重试时自动丢弃未提交批次。
  2. 当 retryPolicy 与 finally Task 同时存在,finally 会被执行几次?
    Tekton 官方行为:finally 只在整个 PipelineRun 结束时运行一次,不会因重试重复执行;因此把“晋升”动作放在 finally 里并不安全,仍需放在主 Task 最后一步。
  3. 国内部分银行仍使用物理 KVM+Oracle 传统架构,PVC 挂载速度成为瓶颈,重试时 JMeter 引擎启动耗时 2~3 min,导致 5 min 压测窗口被吃掉一半,如何优化?
    可采用“预热 Pod 池”思路:提前启动带 JMeter 镜像的 Standby Pod,通过 NodeAffinity 固定到压力机节点,TaskRun 启动时只替换 ConfigMap 与入口脚本,把冷启动时间压到 15 s 以内;同时把重试次数降到 1 次,减少资源争抢。