--- url: /process-workflow/pipeline-design.md description: >- 承接上篇的四个坑,本文用具体的发布场景演示如何把流水线拆成"执行 / 决策 / 数据"三层,并给出一张从初创到规模化的梯队判断表,让不同阶段的团队知道"自己该做到什么程度"。 --- ## 从"四个坑"到"怎么避" 上篇讲完了四个坑,但读完之后大概率是这种反应—— > "我知道这些坑了,但我手头的项目已经在跑了,里面堆了一堆历史包袱。**总不能推倒重来吧?我现在能改什么?**" 这是个好问题。 要避坑,不一定要推倒重做。**真正要做的是把 workflow 这件事拆成清晰的"几层"**——把判断、数据、执行放在不同的位置,让上篇的四个坑在结构上就没有立足之地。 本文要做的就是这件事:用具体的发布场景演示如何拆,最后给一张判断表,让不同阶段的团队知道"自己该做到什么程度"。 *** ## 避坑的朴素思路:分清楚三层 ### 反面:所有事都堆在一个文件里 某团队的发布配置一开始长这样(简化版): ```yaml # 发布配置(早期版本) name: publish on: push: branches: [main] jobs: deploy: steps: - name: 拉代码 run: git clone ... - name: 跑测试 run: npm test - name: 检查是不是新功能 run: | if grep -q "feat:" commit.log; then echo "new_feature=true" >> $GITHUB_ENV fi - name: 检查是不是涉及支付链路 run: | if grep -q "payment" commit.log; then echo "touch_payment=true" >> $GITHUB_ENV fi - name: 业务规则判断:能不能发 run: | if [ "$new_feature" = "true" ] && [ "$touch_payment" = "true" ]; then echo "本次发布包含新功能 + 支付链路变更,禁止周五上线" exit 1 fi - name: 打镜像 run: docker build ... - name: 推镜像 run: docker push ... ``` 这个配置叠了三类事情: * **执行**——拉代码、跑测试、打镜像、推镜像。 * **决策**——"本次发布能不能发"(业务规则判断)。 * **数据**——新功能、支付链路等业务参数。 三类事揉在一个 YAML 里,每次业务规则一改,整个 pipeline 都要重测;改一次发布配置,要担心是不是会破坏业务逻辑。Jenkinsfile 越长,维护成本越高,最后没人敢动。 ### 正面:拆成三层 把这件事拆成清晰的三层: **第一层:执行层(只管"做什么动作")** 执行层只回答"做什么"——拉代码、跑测试、打镜像、推送。不关心业务规则、不关心业务参数。YAML 里只有动作,没有"为什么这样做"。 ```yaml # 执行层(pipeline YAML) name: execute on: push: branches: [main] jobs: build: steps: - name: 拉代码 run: git clone ... - name: 跑测试 run: npm test - name: 打镜像 run: docker build ... - name: 推镜像 run: docker push ... deploy: needs: build steps: - name: 拉决策结果 run: ./fetch-decision.sh # 从决策层拉"该不该发" - name: 推送到目标环境 run: ./deploy.sh ``` **第二层:决策层(只管"该不该发")** 决策层独立成一个服务或脚本。它从数据层取参数,按规则判断,把判断结果返回给执行层。 ```python # 决策层(独立脚本或服务) def should_publish(commit_info, params): new_feature = "feat:" in commit_info touch_payment = "payment" in commit_info is_friday = datetime.now().weekday() == 4 if new_feature and touch_payment and is_friday: return {"publish": False, "reason": "周五禁止支付链路变更发布"} return {"publish": True, "reason": "ok"} ``` 决策层可以单独演进——业务规则变了,只改这一处,不动执行层。 **第三层:数据层(业务参数独立存放)** 业务参数(订单阈值、发布窗口、用户分群)放在专门的配置中心或数据库里,不在 pipeline YAML 里出现。 ``` # 数据层(配置中心 / 数据库) publish: friday_blackout_categories: ["payment"] rolling_window_users: 0.1 emergency_stop: false ``` 决策层从数据层取数,执行层从决策层取结果。**谁也不从谁那里偷数据**。 ### 为什么这样拆能避坑 把上篇的四个坑放到三层结构里看,每个坑都没有立足之地: | 上篇的坑 | 在三层结构里为什么不成立 | | --- | --- | | 第一个坑:业务规则出现在不该出现的地方 | 业务规则只在决策层出现,执行层 YAML 不再写"if 新功能 + 支付链路 → 禁止发" | | 第二个坑:错误被拖到最后才暴露 | 执行层每个步骤独立校验;决策层独立运行,错了不影响执行 | | 第三个坑:决定的事和被决定的事搅在一起 | 执行层是动作,决策层是判断,数据层是参数——三者通过清晰接口对接,不直接相互嵌入 | | 第四个坑:流程反向吞噬工作 | 三层职责清晰:执行者改执行层、决策者改决策层、参数管理者改数据层。改一件事不会牵动其他事 | **三层结构不是新发明,是 workflow 在结构上的最小必要复杂度**。再简单一点,三个坑的毛病全回来;再复杂一点,就是过度设计。 *** ## 一个端到端的流水线实例 下面用一个真实风格的发布场景把三层串成一条线。 ### 场景 一个 SaaS 产品要做一次发布: 1. 开发者 push 代码到 main。 2. 系统自动跑测试、打镜像。 3. 系统判断这次发布能不能发(决策层)。 4. 系统按判断结果决定推送到哪个环境(执行层)。 5. 推送完成后监控指标(数据层反馈)。 ### 端到端流程 ``` [开发者] git push ↓ [执行层 1] 拉代码 + 跑测试 + 打镜像 ↓ (产物:镜像 + commit 信息) [决策层] should_publish(commit_info, params) ↓ (结果:{"publish": true, "env": "staging", "ratio": 0.1}) [执行层 2] 推到 staging(10% 流量灰度) ↓ (持续 30 分钟观察) [数据层] 监控指标 → 决定是否放量 / 回滚 ↓ [执行层 3] 全量发布 / 自动回滚 ``` 每层各管一件事: * **执行层**——把镜像从 A 推到 B,不关心该不该推。 * **决策层**——判断该不该推、推多少,调用数据层取参数。 * **数据层**——参数存放 + 监控指标采集 + 决策反馈。 ### 反例对照 如果三层不拆,会出现什么? 某团队把所有事塞在一个 Jenkinsfile 里: ```groovy // 反例:所有事塞在一起 pipeline { agent any stages { stage('Build') { steps { sh 'docker build .' } } stage('ShouldPublish') { // 业务规则硬编码在 Jenkinsfile steps { script { def commit = sh(script: 'git log -1 --pretty=%B', returnStdout: true) if (commit.contains('feat:') && commit.contains('payment') && isFriday()) { currentBuild.result = 'FAILURE' error('周五禁止支付链路发布') } } } } stage('Deploy') { // 业务参数散落在 Jenkinsfile steps { sh './deploy.sh staging 0.1' } } } } ``` 问题: 1. **业务规则硬编码在执行层**——改规则要改 Jenkinsfile,要重新走一遍 CI。 2. **业务参数散落在执行层**——订单阈值、灰度比例要改 Jenkinsfile。 3. **监控反馈没接进来**——出问题只能靠人盯日志。 4. **改一处牵动全局**——Jenkinsfile 越长,越没人敢动。 把三层拆开后,这些问题都没了。 *** ## 团队在不同阶段该做到什么程度 三层拆法不是越早做越好,也不是做得越细越好。下面是一张梯队判断表。 ### 阶段一:初创团队(5-10 人) **目标:让执行层不越界** * **执行层**:直接用 GitHub Actions YAML / GitLab CI / Jenkinsfile。**不要自建 CI 系统**。 * **决策层**:人工审批 + 一段简单判断脚本。复杂业务规则先用文档记下来。 * **数据层**:环境变量 / Secrets / `.env` 文件即可,**不要引入配置中心**。 **关键纪律**: * 执行层 YAML 里只写动作,不写业务规则。 * 业务规则放在注释或单独的文档里,人工 review。 * 业务参数放在 `.env` 或 Secrets,不写在 YAML 里。 **典型反例**: * 在 Jenkinsfile 里写"周五不上线"。 * 在 pipeline YAML 里塞业务阈值。 * 用同一个 Jenkinsfile 既管 dev 又管 prod。 ### 阶段二:成长期团队(20-50 人) **目标:让决策层和数据层独立** * **执行层**:开始分层(构建 pipeline / 测试 pipeline / 部署 pipeline 拆开)。可以引入 Jenkins + Jenkinsfile / GitLab CI / 自建 CI 平台。 * **决策层**:自动化审批 + 灰度规则脚本。可以引入简单的"发布决策服务"(哪怕是个内部工具)。 * **数据层**:引入配置中心(Apollo / Nacos / Vault / 内部参数服务)。业务参数独立存放。 **关键纪律**: * 决策层从数据层取参数,不从执行层取参数。 * 执行层只调用决策层的接口,不内嵌业务规则。 * 数据层参数变更要有审计日志。 **典型反例**: * 决策层写在执行层 YAML 里("如果 commit 信息里有 feat 就打镜像 tag v2")。 * 数据层参数硬编码在执行层("灰度比例 0.1 写在 Jenkinsfile 里")。 * 决策层直接读数据库,不走专门接口。 ### 阶段三:规模化团队(100+ 人) **目标:让决策层能复用、能独立演进** * **执行层**:CI/CD 平台化,统一调度(Tekton / Argo Workflows / 内部 CI 平台)。 * **决策层**:决策引擎(Open Policy Agent / 内部规则引擎)+ 灰度平台 + 自动化运维。 * **数据层**:完整的配置/参数治理 + 业务参数中台 + 监控 + 反馈。 **关键纪律**: * 决策层是独立的产品,有版本、有 owner、有演进路径。 * 数据层参数变更走完整的发布流程。 * 反馈层(监控、告警、A/B 结果)反哺决策层,形成闭环。 **典型反例**: * 决策层散落在多个 Jenkinsfile / GitHub Actions 里,没人能说清"现在到底有几条业务规则"。 * 数据层参数改了没人知道——配置中心有 200 个 key,没人维护。 * 决策层没有版本——业务规则改了,灰度回不去了。 ### 判断表 | 团队规模 | 执行层做到什么 | 决策层做到什么 | 数据层做到什么 | | --- | --- | --- | --- | | 初创(5-10) | 单 YAML,动作清单 | 人工 + 简单脚本 | `.env` / Secrets | | 成长期(20-50) | 分层 pipeline | 自动化审批 + 灰度脚本 | 配置中心 | | 规模化(100+) | CI/CD 平台化 | 决策引擎 + 规则平台 | 参数中台 + 反馈闭环 | **关键问题不是"我该不该上 K8s",而是"我现在的执行 / 决策 / 数据三层有没有分开"。** *** ## 收尾:方法论的朴素边界 最后讲几句方法论的边界,避免走偏。 **第一,上工具 ≠ 数智化。** 把 Jenkins 换成 Argo CD、GitHub Actions 换成 Tekton,不会自动让流程变好。如果三层不拆,换什么工具都是在同一团乱麻上叠加更复杂的工具。 **第二,画流程图 ≠ 流程设计。** 流程图画得再漂亮,**只要执行 / 决策 / 数据三层没分开**,上篇的四个坑迟早会回来。流程图是设计的结果,不是设计本身。真正的设计是"职责怎么划分、边界怎么定、接口怎么对"。 **第三,复杂度的最低必要原则。** 三层不是越多越好。**初创团队硬上配置中心和决策引擎,反而会让流程变慢**——因为决策层和数据层还没稳定下来,先把它们独立出去只会增加协调成本。 **什么时候该往上走一层?** 当现有形态开始出现上篇讲的四个坑里的任意一个,就该考虑往上走一层。但不要一次走完所有层,按"先执行层不越界 → 决策层独立 → 数据层独立"的顺序逐级演进。 *** 数智化不是终点,**是把流程做到让工作者感受不到流程的存在**。 下篇到此为止。回到系列起点:[流程即镜像:审批流程和 CI 流水线撞上的同一组坑(上)](./pipeline-mirror.md)。 *** ## 参考 * [流程驱动工作:从纸质审批到智能门闩](./workflow-design-philosophy.md) —— 上篇四个坑里"流程侧的糟心例"的源头 * [基础篇总结:从手动部署到声明式流水线](../sre/devops/foundation/foundation-summary.md) —— 上篇四个坑里"流水线侧的糟心例"的源头 * [流程即镜像:审批流程和 CI 流水线撞上的同一组坑(上)](./pipeline-mirror.md) —— 本文上篇