Harness 教程 11:多阶段流水线详解:并行步骤、阶段依赖、条件执行与复杂交付流程设计:国内网络环境落地版

67 阅读17分钟

一:教程定位

在前 10 篇教程中,我们已经完成了 Harness 的基础环境搭建、CI/CD 流水线、触发器、测试制品管理、Kubernetes 部署、多环境参数化、审批与定时触发、日志排查、CLI/YAML 管理,以及基础回滚策略。

第 11 篇开始进入更接近企业真实交付流程的内容:多阶段流水线设计

很多团队刚开始使用 Harness 时,流水线一般是线性的:

拉代码 ↓ 安装依赖 ↓ 单元测试 ↓ 构建镜像 ↓ 推送 Harbor ↓ 部署 dev

这种方式适合入门,但企业项目通常更复杂:

前端和后端要分别构建 单元测试和代码扫描可以并行 多个服务可以并行构建 dev 和 test 可以自动部署 pre 和 prod 需要审批 不同环境有不同触发策略 某些阶段只在 main 或 release 分支运行 某些检查失败要终止,某些通知失败可以忽略 多个并行阶段需要在某个点同步后再继续

因此,本篇重点讲解 Harness 多阶段流水线的工程化设计。


二:适合人群

本文面向中级用户,适合:

DevOps 工程师 平台工程师 SRE 测试负责人 后端/前端技术负责人 需要设计企业级 CI/CD 流程的团队

建议已经具备:

了解 Harness Pipeline、Stage、Step 了解 GitLab/Gitee 触发器 了解 Docker 镜像构建和 Harbor 推送 了解 Kubernetes 部署 了解 Runtime Input 和 Input Set 了解审批和失败策略 能看懂基础 YAML


三:学习目标

完成本文后,你应该能够:

理解多阶段流水线的拆分原则 区分串行阶段、并行阶段、DAG 依赖阶段 设计 Build、Test、Scan、Package、Deploy 多阶段流程 在同一 Stage 内配置并行步骤 在 Pipeline 中配置并行 Stage 使用 dependsOn 设计 DAG 风格阶段依赖 理解 DAG 功能开关和传统 parallel 语法的区别 使用 when 条件控制阶段是否执行 根据 Git 分支、触发类型、环境变量控制发布路径 使用 Barrier 同步并行阶段 理解国内网络环境下并发构建对 Delegate、K8s、Harbor 的压力 设计 dev/test/pre/prod 的多阶段交付流程 排查并行阶段、条件执行、依赖关系导致的问题


四:国内网络环境下的多阶段流水线架构

本文继续采用国内企业常见架构:

GitLab / Gitee ↓ Webhook Harness Pipeline ↓ Harness Delegate ↓ Kubernetes CI 构建命名空间 ↓ Harbor / ACR / TCR / SWR ↓ Kubernetes dev/test/pre/prod

在多阶段流水线中,执行链路会变成:

代码提交 ↓ 并行质量检查 ├── 单元测试 ├── 代码扫描 └── 依赖安全检查 ↓ 构建与制品发布 ├── 前端镜像 └── 后端镜像 ↓ 部署 ├── dev 自动部署 ├── test 自动部署或审批部署 ├── pre 审批部署 └── prod 审批 + 灰度 + 回滚策略

国内网络环境中要额外关注:

多个并行构建会同时拉 npm/maven 依赖 多个并行构建会同时拉基础镜像 多个并行推送会同时写 Harbor 多个并行部署会同时访问 Kubernetes API Delegate 并发能力是否足够 Kubernetes CI namespace 资源是否足够 企业代理是否会成为瓶颈 Harbor 是否有存储和并发限制


五:多阶段流水线设计原则

1. 按交付生命周期拆分 Stage

推荐:

Source / Checkout Build Unit Test Static Scan Package Publish Artifact Deploy Dev Integration Test Deploy Test Approval Deploy Pre Approval Deploy Prod Verify Notify

不推荐把所有操作都放到一个 Stage 里。

原因:

日志不好看 失败定位困难 条件控制困难 重试范围太大 审批和部署耦合严重 无法清晰复用

2. 同类任务可并行

适合并行:

前端构建和后端构建 单元测试和静态扫描 多个微服务构建 多个环境的非生产验证 多个通知渠道

不适合并行:

构建镜像之前部署 测试未通过就部署 生产审批和生产部署并行 数据库变更和应用部署乱序 依赖服务未就绪就跑集成测试

3. 阶段之间要明确依赖

典型依赖:

Unit Test 依赖 Build 或 Checkout Docker Build 依赖 Unit Test 成功 Deploy Dev 依赖 Docker Push 成功 Integration Test 依赖 Deploy Dev 成功 Deploy Prod 依赖 Approval 成功

4. 生产发布路径必须保守

生产阶段应该:

必须审批 必须使用不可变镜像 Tag 必须有回滚策略 必须有健康检查 必须有失败处理 不能和 dev/test 部署混在一起


六:传统流水线与多阶段流水线对比

1. 传统线性流水线

CI Build ↓ Unit Test ↓ Docker Build ↓ Deploy Dev ↓ Deploy Prod

优点:

简单 适合入门 执行顺序清晰

缺点:

执行时间长 不能充分并行 复杂场景会变得臃肿

2. 多阶段并行流水线

Checkout ↓ 并行质量检查 ├── Unit Test ├── SonarQube Scan └── Dependency Scan ↓ Build And Publish ↓ Deploy Dev ↓ Integration Test ↓ Approval ↓ Deploy Prod

优点:

速度更快 结构更清晰 便于扩展 便于不同阶段设置不同失败策略

缺点:

设计复杂度更高 资源消耗更高 排障需要理解执行图 并行阶段需要控制资源

3. DAG 依赖流水线

DAG 适合更复杂的依赖关系,例如:

Build Frontend ───────→ Deploy Frontend Build Backend ─→ Unit Test ─→ Deploy Backend Database Migration ─────────→ Integration Test Deploy Frontend + Deploy Backend ─→ E2E Test

DAG 的核心思想是:

每个 Stage 明确声明 dependsOn 没有依赖的 Stage 立即开始 有依赖的 Stage 等依赖成功后开始 YAML 中 Stage 顺序不再决定执行顺序

注意:

DAG Pipeline 需要 Harness 功能开关 如果你的账号未启用 DAG,可先使用传统 parallel 结构和 Barrier 实现大部分需求


七:本篇示例目标

我们要设计一条中级多阶段流水线:

Pipeline: nodejs-multi-stage-delivery

Stage 1: Prepare Step: 打印上下文、检查网络

Stage 2: Quality Checks 并行 Step: - Unit Test - SonarQube Scan - Dependency Audit

Stage 3: Build And Publish Step: - Docker Build And Push Harbor

Stage 4: Deploy Dev Step: - K8s Rolling Deploy - Health Check

Stage 5: Integration Test Step: - API Smoke Test

Stage 6: Deploy Test 条件: - main 分支或手动触发才执行

Stage 7: Approval For Prod 条件: - release/* 或 tag 触发才执行

Stage 8: Deploy Prod 条件: - Approval 成功才执行

扩展版本:

Frontend Build 和 Backend Build 并行 dev/test 部署可以并行 pre/prod 严格串行 通知阶段 Always 执行 失败阶段收集诊断信息


八:推荐仓库结构

harness-demo-app/ ├── app/ │ ├── package.json │ ├── server.js │ └── server.test.js ├── frontend/ │ └── package.json ├── backend/ │ └── package.json ├── docker/ │ ├── Dockerfile │ ├── Dockerfile.frontend │ └── Dockerfile.backend ├── k8s/ │ ├── templates/ │ │ ├── deployment.yaml │ │ └── service.yaml │ ├── values-dev.yaml │ ├── values-test.yaml │ └── values-prod.yaml ├── scripts/ │ ├── check-network-cn.sh │ ├── unit-test.sh │ ├── sonar-scan.sh │ ├── dependency-audit.sh │ ├── smoke-test.sh │ ├── collect-debug-info.sh │ └── notify.sh └── harness/ └── pipelines/ └── nodejs-multi-stage-delivery.yaml


九:准备脚本:网络检查

scripts/check-network-cn.sh

#!/usr/bin/env bash
set -e

echo "===== Check GitLab ====="
curl -k -I --connect-timeout 10 https://gitlab.company.com || true

echo "===== Check Harbor ====="
curl -k -I --connect-timeout 10 https://harbor.company.com/v2/ || true

echo "===== Check npm mirror ====="
curl -I --connect-timeout 10 https://registry.npmmirror.com || true

echo "===== Check SonarQube ====="
curl -k -I --connect-timeout 10 https://sonarqube.company.com || true

echo "===== Check Kubernetes ====="
kubectl get ns || true

说明:

该脚本适合放在 Prepare Stage 用于提前发现 GitLab、Harbor、npm、SonarQube、Kubernetes 网络问题 在国内企业环境中非常有用


十:准备脚本:单元测试

scripts/unit-test.sh

#!/usr/bin/env bash
set -e

echo "开始执行单元测试"

cd app

npm config set registry https://registry.npmmirror.com
npm install
npm test

echo "单元测试通过"

十一:准备脚本:SonarQube 扫描

scripts/sonar-scan.sh

#!/usr/bin/env bash
set -e

SONAR_HOST_URL="${SONAR_HOST_URL:?SONAR_HOST_URL is required}"
SONAR_TOKEN="${SONAR_TOKEN:?SONAR_TOKEN is required}"

echo "开始 SonarQube 扫描"

sonar-scanner \
  -Dsonar.host.url=${SONAR_HOST_URL} \
  -Dsonar.token=${SONAR_TOKEN}

echo "SonarQube 扫描完成"

十二:准备脚本:依赖安全检查

scripts/dependency-audit.sh

#!/usr/bin/env bash
set -e

echo "开始依赖安全检查"

cd app

npm config set registry https://registry.npmmirror.com
npm audit --audit-level=high || {
  echo "发现 high 级别以上依赖风险"
  exit 1
}

echo "依赖安全检查通过"

注意:

npm audit 依赖 npm registry 的安全数据能力 如果企业 npm 私服不支持 audit,可替换为 Sonatype Nexus IQ、OWASP Dependency-Check、Trivy、Snyk 或企业内部扫描工具


十三:准备脚本:冒烟测试

scripts/smoke-test.sh

#!/usr/bin/env bash
set -e

APP_URL="${APP_URL:?APP_URL is required}"

echo "开始冒烟测试:${APP_URL}"

curl -f "${APP_URL}/health"

curl -f "${APP_URL}/"

echo "冒烟测试通过"

如果部署在 K8s 内部,可以使用 port-forward:

#!/usr/bin/env bash
set -e

APP_NAME="${APP_NAME:-harness-demo-app}"
NAMESPACE="${NAMESPACE:-dev}"

kubectl port-forward svc/${APP_NAME} 18080:80 -n ${NAMESPACE} >/tmp/port-forward.log 2>&1 &
PF_PID=$!

sleep 5

curl -f http://127.0.0.1:18080/health
curl -f http://127.0.0.1:18080/

kill ${PF_PID}

echo "冒烟测试通过"

十四:Stage 1:Prepare

Prepare Stage 用于统一输出上下文和检查基础环境。

推荐内容:

打印 Pipeline 信息 打印触发信息 打印分支和 Commit 检查 GitLab/Harbor/npm/SonarQube/K8s 网络

Step 示例:

echo "Pipeline: <+pipeline.name>"
echo "Execution ID: <+pipeline.executionId>"
echo "Trigger Type: <+pipeline.triggerType>"
echo "Branch: <+codebase.branch>"
echo "Commit SHA: <+codebase.commitSha>"
echo "Image Tag: <+pipeline.variables.image_tag>"

chmod +x scripts/check-network-cn.sh
scripts/check-network-cn.sh

Prepare Stage 不建议做耗时操作。


十五:Stage 2:Quality Checks 并行步骤

质量检查阶段适合并行执行:

Unit Test SonarQube Scan Dependency Audit Lint

原因:

这些步骤互相不强依赖 都只依赖源码 并行可以显著缩短流水线时间 任何一个失败都应阻断后续镜像构建和部署

并行 Step 设计

在 Harness Visual Editor 中,可以在 Execution 中添加 Parallel 分支。

逻辑结构:

Quality Checks ├── Unit Test ├── SonarQube Scan └── Dependency Audit

如果任一并行步骤失败,Quality Checks Stage 应失败。

国内环境注意

并行质量检查会同时访问:

npm 镜像源 SonarQube 企业代理 CI 构建 Pod 资源

建议:

CI namespace 配置 ResourceQuota Node 镜像提前同步到 Harbor SonarScanner 镜像提前同步到 Harbor 不要所有项目同一时间高并发扫描 SonarQube


十六:Stage 3:Build And Publish

Build And Publish Stage 在 Quality Checks 全部通过后执行。

职责:

构建 Docker 镜像 推送到 Harbor 输出镜像地址

镜像 Tag 策略:

dev:latest + pipeline sequenceId test:commit sha pre/prod:release tag 或 Git tag

示例:

harbor.company.com/devops/harness-demo-app:<+pipeline.variables.image_tag>

构建前置条件:

单元测试必须成功 扫描必须成功 依赖安全检查必须成功


十七:Stage 4:Deploy Dev

Deploy Dev 可以在 Build And Publish 后自动执行。

适合条件:

feature/* 分支:可选部署 dev dev 分支:自动部署 dev main 分支:自动部署 dev/test release/* 分支:部署 pre/prod 前必须审批

建议条件:

<+pipeline.variables.deploy_dev> == "true"

或基于分支:

<+codebase.branch> == "dev" || <+codebase.branch> == "main"

部署后必须做:

rollout status Pod Ready 检查 /health 健康检查 冒烟测试


十八:Stage 5:Integration Test

Integration Test 应该依赖 Deploy Dev。

职责:

验证服务是否可以访问 验证关键 API 验证数据库连接 验证服务间调用 验证消息队列或缓存

示例脚本:

set -e

export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}

APP_NAME="harness-demo-app"
NAMESPACE="dev"

kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=180s

kubectl port-forward svc/${APP_NAME} 18080:80 -n ${NAMESPACE} >/tmp/port-forward.log 2>&1 &
PF_PID=$!

sleep 5

curl -f http://127.0.0.1:18080/health
curl -f http://127.0.0.1:18080/api/users

kill ${PF_PID}

echo "集成测试通过"

十九:Stage 6:Deploy Test

Deploy Test 可以设计为:

main 分支自动执行 feature/* 分支跳过 手动运行时可通过 Runtime Input 控制

条件示例:

<+codebase.branch> == "main" || <+pipeline.triggerType> == "MANUAL"

更稳妥的做法:

使用 Pipeline 变量 deploy_test 值为 true 时执行 Input Set 控制不同环境

variables:
  - name: deploy_test
    type: String
    value: <+input>

条件:

<+pipeline.variables.deploy_test> == "true"


二十:Stage 7:Approval For Prod

生产审批阶段必须严格。

执行条件:

<+pipeline.variables.deploy_prod> == "true"

审批内容:

应用名称 部署环境 镜像版本 Git 分支 Commit SHA 测试结果 变更单 回滚方案 执行人

审批策略:

prod-release-approvers minimum approvals = 1 或 2 disallowPipelineExecutor = true timeout = 1h


二十一:Stage 8:Deploy Prod

Deploy Prod 必须依赖 Approval For Prod 成功。

生产部署必须具备:

不可变镜像 Tag K8s Rolling Deploy 或 Canary 健康检查 失败策略 回滚策略 通知 执行记录

生产条件:

<+pipeline.variables.deploy_prod> == "true"

同时增加 Tag 校验:

set -e

IMAGE_TAG="<+pipeline.variables.image_tag>"

if [ "${IMAGE_TAG}" = "latest" ]; then
  echo "生产环境禁止使用 latest"
  exit 1
fi

echo "生产镜像 Tag 校验通过:${IMAGE_TAG}"

二十二:条件执行设计

Harness 条件执行可以用于 Stage、Step、Step Group。

常见条件:

1. 只在 main 分支执行

<+codebase.branch> == "main"

2. release 分支执行

<+codebase.branch>.startsWith("release/")

3. 手动触发才执行

<+pipeline.triggerType> == "MANUAL"

4. Git Webhook 触发才执行

<+pipeline.triggerType> == "WEBHOOK"

5. 某个变量为 true 才执行

<+pipeline.variables.deploy_prod> == "true"

6. 上一个 Stage 成功后执行

默认 broad condition 就是 Success。

如果要显式引用:

<+pipeline.stages.quality_checks.status> == "SUCCEEDED"

注意:

表达式路径建议从 Harness UI 中复制 不同 Harness 版本中 status 路径可能略有差异 先用 echo 验证,再用于关键条件


二十三:并行 Stage 设计

1. 传统并行 Stage

传统 Harness Pipeline 可以通过 parallel 结构组织并行 Stage。

逻辑示例:

Parallel:

  • Build Frontend
  • Build Backend
  • Build Worker

适合:

多个服务并行构建 前端后端并行构建 多个非生产环境并行部署 多个测试套件并行执行

注意:

并行 Stage 都会消耗资源 并行构建会增加 Harbor、npm、K8s CI namespace 压力 失败策略要明确

2. 并行后汇聚

例如:

Build Frontend ┐ Build Backend ├── Integration Test Build Worker ┘

如果未启用 DAG,可以使用传统结构:

Parallel Build Group ├── Build Frontend ├── Build Backend └── Build Worker Integration Test

Integration Test 会在 Parallel Group 完成后执行。


二十四:DAG 阶段依赖设计

如果你的 Harness 账号开启了 DAG Pipeline,可以用 dependsOn 明确阶段依赖。

示例:

Build Frontend → Deploy Frontend Build Backend → Unit Test → Deploy Backend Deploy Frontend + Deploy Backend → E2E Test

DAG YAML 核心:

dependsOn:
  - build_backend

没有依赖的 Stage:

dependsOn: []

含义:

该 Stage 在 Pipeline 开始后立即运行

依赖多个 Stage:

dependsOn:
  - deploy_frontend
  - deploy_backend

含义:

这两个 Stage 都成功后才执行


二十五:DAG Pipeline YAML 示例

以下示例用于说明依赖关系。字段结构在实际账号中建议先用 Visual Editor 创建,再切 YAML 校验。

pipeline:
  name: nodejs-dag-multi-stage
  identifier: nodejs_dag_multi_stage
  projectIdentifier: harness_demo
  orgIdentifier: devops_lab
  tags:
    tutorial: harness-11
    mode: dag
  stages:
    - stage:
        name: Prepare
        identifier: prepare
        type: Custom
        spec:
          execution:
            steps:
              - step:
                  name: Print Context
                  identifier: print_context
                  type: ShellScript
                  spec:
                    shell: Bash
                    source:
                      type: Inline
                      spec:
                        script: |
                          echo "prepare"
        dependsOn: []

    - stage:
        name: Unit Test
        identifier: unit_test
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  name: Unit Test
                  identifier: unit_test_step
                  type: Run
                  spec:
                    shell: Sh
                    command: |
                      cd app
                      npm config set registry https://registry.npmmirror.com
                      npm install
                      npm test
        dependsOn:
          - prepare

    - stage:
        name: Sonar Scan
        identifier: sonar_scan
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  name: Sonar Scan
                  identifier: sonar_scan_step
                  type: Run
                  spec:
                    shell: Sh
                    command: |
                      sonar-scanner
        dependsOn:
          - prepare

    - stage:
        name: Build Image
        identifier: build_image
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  name: Build And Push
                  identifier: build_and_push
                  type: BuildAndPushDockerRegistry
                  spec:
                    connectorRef: harbor_company
                    repo: harbor.company.com/devops/harness-demo-app
                    dockerfile: docker/Dockerfile
                    context: .
                    tags:
                      - <+pipeline.sequenceId>
        dependsOn:
          - unit_test
          - sonar_scan

    - stage:
        name: Deploy Dev
        identifier: deploy_dev
        type: Deployment
        spec:
          deploymentType: Kubernetes
          service:
            serviceRef: harness_demo_app
          environment:
            environmentRef: dev
            deployToAll: false
            infrastructureDefinitions:
              - identifier: dev_k8s
          execution:
            steps:
              - step:
                  name: K8s Rolling Deploy
                  identifier: k8s_rolling_deploy
                  type: K8sRollingDeploy
                  timeout: 10m
                  spec:
                    skipDryRun: false
        dependsOn:
          - build_image

    - stage:
        name: Integration Test
        identifier: integration_test
        type: Custom
        spec:
          execution:
            steps:
              - step:
                  name: Smoke Test
                  identifier: smoke_test
                  type: ShellScript
                  spec:
                    shell: Bash
                    source:
                      type: Inline
                      spec:
                        script: |
                          echo "run integration test"
        dependsOn:
          - deploy_dev

注意:

DAG Pipeline 需要功能开关 如果当前环境未启用,请使用传统 sequential + parallel 结构实现


二十六:传统多阶段 Pipeline YAML 示例

以下是更通用的传统多阶段结构,适合未启用 DAG 的账号。

pipeline:
  name: nodejs-multi-stage-delivery
  identifier: nodejs_multi_stage_delivery
  projectIdentifier: harness_demo
  orgIdentifier: devops_lab
  tags:
    tutorial: harness-11
    network: china
  variables:
    - name: app_name
      type: String
      value: harness-demo-app
    - name: image_tag
      type: String
      value: <+input>
    - name: deploy_dev
      type: String
      value: <+input>
    - name: deploy_test
      type: String
      value: <+input>
    - name: deploy_prod
      type: String
      value: <+input>
  stages:
    - stage:
        name: Prepare
        identifier: prepare
        type: Custom
        spec:
          execution:
            steps:
              - step:
                  name: Print Context And Check Network
                  identifier: print_context_and_check_network
                  type: ShellScript
                  timeout: 5m
                  spec:
                    shell: Bash
                    onDelegate: true
                    source:
                      type: Inline
                      spec:
                        script: |
                          set -e
                          echo "Pipeline: <+pipeline.name>"
                          echo "Execution ID: <+pipeline.executionId>"
                          echo "Branch: <+codebase.branch>"
                          echo "Commit: <+codebase.commitSha>"
                          chmod +x scripts/check-network-cn.sh
                          scripts/check-network-cn.sh

    - stage:
        name: Quality Checks
        identifier: quality_checks
        type: CI
        spec:
          cloneCodebase: true
          infrastructure:
            type: KubernetesDirect
            spec:
              connectorRef: k8s_dev_cluster
              namespace: harness-ci
              automountServiceAccountToken: true
              os: Linux
          execution:
            steps:
              - parallel:
                  - step:
                      name: Unit Test
                      identifier: unit_test
                      type: Run
                      spec:
                        connectorRef: harbor_company
                        image: harbor.company.com/library/node:20-alpine
                        shell: Sh
                        command: |
                          chmod +x scripts/unit-test.sh
                          scripts/unit-test.sh

                  - step:
                      name: SonarQube Scan
                      identifier: sonarqube_scan
                      type: Run
                      spec:
                        connectorRef: harbor_company
                        image: harbor.company.com/library/sonar-scanner-cli:latest
                        shell: Sh
                        envVariables:
                          SONAR_HOST_URL: https://sonarqube.company.com
                          SONAR_TOKEN: <+secrets.getValue("sonar_token")>
                        command: |
                          chmod +x scripts/sonar-scan.sh
                          SONAR_HOST_URL=${SONAR_HOST_URL} \
                          SONAR_TOKEN=${SONAR_TOKEN} \
                          scripts/sonar-scan.sh

                  - step:
                      name: Dependency Audit
                      identifier: dependency_audit
                      type: Run
                      spec:
                        connectorRef: harbor_company
                        image: harbor.company.com/library/node:20-alpine
                        shell: Sh
                        command: |
                          chmod +x scripts/dependency-audit.sh
                          scripts/dependency-audit.sh

    - stage:
        name: Build And Publish
        identifier: build_and_publish
        type: CI
        spec:
          cloneCodebase: true
          infrastructure:
            type: KubernetesDirect
            spec:
              connectorRef: k8s_dev_cluster
              namespace: harness-ci
              automountServiceAccountToken: true
              os: Linux
          execution:
            steps:
              - step:
                  name: Build And Push Image
                  identifier: build_and_push_image
                  type: BuildAndPushDockerRegistry
                  spec:
                    connectorRef: harbor_company
                    repo: harbor.company.com/devops/harness-demo-app
                    dockerfile: docker/Dockerfile
                    context: .
                    tags:
                      - <+pipeline.variables.image_tag>
                      - <+pipeline.sequenceId>

    - stage:
        name: Deploy Dev
        identifier: deploy_dev
        type: Deployment
        when:
          pipelineStatus: Success
          condition: <+pipeline.variables.deploy_dev> == "true"
        spec:
          deploymentType: Kubernetes
          service:
            serviceRef: harness_demo_app
          environment:
            environmentRef: dev
            deployToAll: false
            infrastructureDefinitions:
              - identifier: dev_k8s
          execution:
            steps:
              - step:
                  name: K8s Rolling Deploy
                  identifier: k8s_rolling_deploy
                  type: K8sRollingDeploy
                  timeout: 10m
                  spec:
                    skipDryRun: false

              - step:
                  name: Verify Dev
                  identifier: verify_dev
                  type: ShellScript
                  timeout: 5m
                  spec:
                    shell: Bash
                    source:
                      type: Inline
                      spec:
                        script: |
                          set -e
                          export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
                          kubectl rollout status deployment/harness-demo-app -n dev --timeout=180s

    - stage:
        name: Integration Test
        identifier: integration_test
        type: Custom
        when:
          pipelineStatus: Success
          condition: <+pipeline.variables.deploy_dev> == "true"
        spec:
          execution:
            steps:
              - step:
                  name: Smoke Test
                  identifier: smoke_test
                  type: ShellScript
                  timeout: 5m
                  spec:
                    shell: Bash
                    onDelegate: true
                    source:
                      type: Inline
                      spec:
                        script: |
                          set -e
                          export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
                          APP_NAME=harness-demo-app NAMESPACE=dev scripts/smoke-test.sh

    - stage:
        name: Deploy Test
        identifier: deploy_test
        type: Deployment
        when:
          pipelineStatus: Success
          condition: <+pipeline.variables.deploy_test> == "true"
        spec:
          deploymentType: Kubernetes
          service:
            serviceRef: harness_demo_app
          environment:
            environmentRef: test
            deployToAll: false
            infrastructureDefinitions:
              - identifier: test_k8s
          execution:
            steps:
              - step:
                  name: K8s Rolling Deploy Test
                  identifier: k8s_rolling_deploy_test
                  type: K8sRollingDeploy
                  timeout: 10m
                  spec:
                    skipDryRun: false

    - stage:
        name: Approval For Prod
        identifier: approval_for_prod
        type: Approval
        when:
          pipelineStatus: Success
          condition: <+pipeline.variables.deploy_prod> == "true"
        spec:
          execution:
            steps:
              - step:
                  name: Approve Prod
                  identifier: approve_prod
                  type: HarnessApproval
                  timeout: 1h
                  spec:
                    approvalMessage: |
                      请确认是否发布生产:

                      应用:harness-demo-app
                      镜像 Tag:<+pipeline.variables.image_tag>
                      分支:<+codebase.branch>
                      Commit:<+codebase.commitSha>
                      执行人:<+pipeline.triggeredBy.name>
                    includePipelineExecutionHistory: true
                    approvers:
                      userGroups:
                        - prod_release_approvers
                      minimumCount: 1
                      disallowPipelineExecutor: true

    - stage:
        name: Deploy Prod
        identifier: deploy_prod
        type: Deployment
        when:
          pipelineStatus: Success
          condition: <+pipeline.variables.deploy_prod> == "true"
        spec:
          deploymentType: Kubernetes
          service:
            serviceRef: harness_demo_app
          environment:
            environmentRef: prod
            deployToAll: false
            infrastructureDefinitions:
              - identifier: prod_k8s
          execution:
            steps:
              - step:
                  name: Check Prod Image Tag
                  identifier: check_prod_image_tag
                  type: ShellScript
                  timeout: 2m
                  spec:
                    shell: Bash
                    source:
                      type: Inline
                      spec:
                        script: |
                          set -e
                          IMAGE_TAG="<+pipeline.variables.image_tag>"

                          if [ "${IMAGE_TAG}" = "latest" ]; then
                            echo "生产环境禁止使用 latest"
                            exit 1
                          fi

                          echo "生产镜像 Tag 校验通过"

              - step:
                  name: K8s Rolling Deploy Prod
                  identifier: k8s_rolling_deploy_prod
                  type: K8sRollingDeploy
                  timeout: 10m
                  spec:
                    skipDryRun: false
        failureStrategies:
          - onFailure:
              errors:
                - AllErrors
              action:
                type: StageRollback

说明:

Quality Checks 中使用 parallel 并行步骤 Deploy Dev、Deploy Test、Deploy Prod 使用变量控制是否执行 Prod 前增加审批 Prod 阶段禁止 latest Prod 阶段配置失败后 StageRollback


二十七:Barrier 同步设计

当多个并行 Deploy Stage 都完成某个动作后,才允许继续执行后续步骤,可以使用 Barrier。

适合场景:

多个服务并行部署后统一开始验证 多个环境准备完成后统一执行测试 前端和后端都启动后再执行 E2E

示例流程:

Deploy Frontend ↓ Barrier: app-ready

Deploy Backend ↓ Barrier: app-ready

二者都到达 app-ready 后 ↓ E2E Test

注意:

Barrier 只在 Deploy 和 Custom Stage 支持 多个并行 Stage 或 Step Group 必须引用同一个 Barrier 名称 如果任一并行实体在到达 Barrier 前失败,其他等待实体也会收到失败信号 Barrier Timeout 要设置合理


二十八:触发策略设计

多阶段流水线通常不是所有触发都跑完整流程。

推荐策略:

feature/* push: Unit Test SonarQube Scan Dependency Audit Build Image 可选 不部署

dev push: Quality Checks Build Image Deploy Dev Integration Test

main push: Quality Checks Build Image Deploy Dev Integration Test Deploy Test

release/* push: Quality Checks Build Image Deploy Test Approval Pre Deploy Pre

tag v*: Quality Checks 可选 Approval Prod Deploy Prod

通过 Input Set 控制:

feature Input Set: deploy_dev=false deploy_test=false deploy_prod=false

dev Input Set: deploy_dev=true deploy_test=false deploy_prod=false

main Input Set: deploy_dev=true deploy_test=true deploy_prod=false

prod Input Set: deploy_dev=false deploy_test=false deploy_prod=true


二十九:Input Set 示例

1. feature Input Set

inputSet:
  name: feature-ci
  identifier: feature_ci
  orgIdentifier: devops_lab
  projectIdentifier: harness_demo
  pipeline:
    identifier: nodejs_multi_stage_delivery
    variables:
      - name: image_tag
        type: String
        value: <+codebase.commitSha>
      - name: deploy_dev
        type: String
        value: "false"
      - name: deploy_test
        type: String
        value: "false"
      - name: deploy_prod
        type: String
        value: "false"

2. main Input Set

inputSet:
  name: main-test
  identifier: main_test
  orgIdentifier: devops_lab
  projectIdentifier: harness_demo
  pipeline:
    identifier: nodejs_multi_stage_delivery
    variables:
      - name: image_tag
        type: String
        value: <+codebase.commitSha>
      - name: deploy_dev
        type: String
        value: "true"
      - name: deploy_test
        type: String
        value: "true"
      - name: deploy_prod
        type: String
        value: "false"

3. prod Input Set

inputSet:
  name: prod-release
  identifier: prod_release
  orgIdentifier: devops_lab
  projectIdentifier: harness_demo
  pipeline:
    identifier: nodejs_multi_stage_delivery
    variables:
      - name: image_tag
        type: String
        value: <+input>
      - name: deploy_dev
        type: String
        value: "false"
      - name: deploy_test
        type: String
        value: "false"
      - name: deploy_prod
        type: String
        value: "true"

生产运行时填写:

image_tag = v1.0.0


三十:国内环境并发资源控制

多阶段和并行会明显增加资源消耗。

1. Delegate 并发

需要关注:

Delegate Pod CPU / Memory Delegate 副本数 Delegate Selector 是否合理 是否所有 Pipeline 都挤到一个 Delegate

建议:

dev/test 使用一组 Delegate pre/prod 使用独立 Delegate 高并发构建使用多个 Delegate

2. Kubernetes CI Namespace

建议设置 ResourceQuota:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: harness-ci-quota
  namespace: harness-ci
spec:
  hard:
    pods: "20"
    requests.cpu: "8"
    requests.memory: 16Gi
    limits.cpu: "16"
    limits.memory: 32Gi

3. Harbor 并发

高并发构建会导致:

推送慢 存储增长快 镜像 GC 压力大 Robot Account 权限问题更难排查

建议:

开启镜像保留策略 按项目隔离 Harbor Project 限制生产镜像不可覆盖 构建镜像使用唯一 Tag

4. npm / Maven 私服

并行构建会同时拉依赖。

建议:

使用企业 npm 私服 / Nexus / Verdaccio 使用 Maven 私服 CI 镜像预置常用工具 依赖缓存策略谨慎设计


三十一:失败策略设计

多阶段流水线推荐失败策略:

Prepare: 网络检查失败可以失败,也可以只告警 Quality Checks: 任意失败直接终止 Build And Publish: 失败直接终止 Deploy Dev: 失败自动回滚 Integration Test: 失败终止,不进入 test/prod Deploy Test: 失败自动回滚 Approval Prod: 拒绝则终止 Deploy Prod: 失败 StageRollback 或 Manual Intervention Notify: 失败可 Ignore

YAML 示例:

failureStrategies:
  - onFailure:
      errors:
        - AllErrors
      action:
        type: StageRollback

通知 Step 可单独设置:

failureStrategies:
  - onFailure:
      errors:
        - AllErrors
      action:
        type: Ignore

三十二:常见问题排查

1. 并行 Step 没有并行执行

可能原因:

YAML 结构写错 没有使用 parallel 结构 UI 中添加的是串行 Step 资源不足导致实际启动慢

排查:

查看 Execution Graph 查看每个 Step 开始时间 查看 CI Pod 是否排队

2. Stage 条件不生效

可能原因:

变量值是 true,但条件写成布尔 true 表达式路径写错 Input Set 没覆盖变量 Stage broad condition 不匹配

建议:

echo "deploy_dev=<+pipeline.variables.deploy_dev>"
echo "triggerType=<+pipeline.triggerType>"
echo "branch=<+codebase.branch>"

条件统一写成字符串比较:

<+pipeline.variables.deploy_dev> == "true"

3. DAG dependsOn 不生效

可能原因:

账号未启用 DAG 功能 Pipeline 不是 DAG 模式 dependsOn identifier 写错 手动修改 YAML 但未启用 DAG 元数据

处理:

确认功能开关 从 UI 创建 DAG Pipeline 不要只在普通 Pipeline YAML 中添加 dependsOn 先在测试 Pipeline 验证

4. 并行部署导致资源不足

表现:

CI Pod Pending K8s 节点 CPU 不足 Harbor push 变慢 SonarQube 队列堆积

处理:

降低并行度 增加 CI 节点资源 设置 ResourceQuota 错峰定时任务 拆分大型 Pipeline

5. 生产部署被错误触发

可能原因:

prod Input Set 配错 deploy_prod 被设置为 true Trigger 分支过滤过宽 没有生产审批

处理:

prod 必须审批 prod 禁止 latest tag v* 才能触发 prod prod Input Set 权限收紧 增加 Check Prod Image Tag Step


三十三:企业最佳实践

1. Stage 要按职责拆分

推荐:

Build Test Scan Publish Deploy Verify Approval Notify

避免:

一个 Stage 做所有事情 一个 Shell Script 管全流程

2. 并行只用于无强依赖任务

适合:

单元测试 + 扫描 前端构建 + 后端构建 多服务构建 多个通知渠道

不适合:

部署和测试乱序 审批和生产部署并行 数据库变更和应用部署并行

3. 生产路径必须独立控制

生产至少要有:

审批 不可变 Tag 失败策略 回滚策略 健康检查 通知 审计

4. DAG 慎用但很有价值

DAG 适合:

多服务依赖复杂 前后端独立构建部署 多个环境有不同前置条件 测试依赖部分构建结果

注意:

DAG 需要功能开关 转换后不能简单回到传统模式 先 Clone 测试,再正式迁移

5. 并发要配资源治理

企业必须配套:

Delegate 扩容 K8s ResourceQuota Harbor 保留策略 npm/Maven 私服缓存 SonarQube 扫描队列规划


三十四:练习 1:把质量检查改成并行步骤

目标:

Unit Test、SonarQube Scan、Dependency Audit 同时执行。

步骤:

  1. 打开 Quality Checks Stage
  2. 添加 parallel 分支
  3. 分别放入 Unit Test、SonarQube Scan、Dependency Audit
  4. 运行 Pipeline
  5. 查看 Execution Graph

验收:

三个 Step 开始时间接近 任意一个失败,Quality Checks Stage 失败 Build And Publish 不执行


三十五:练习 2:为 Deploy Test 添加条件执行

目标:

只有 deploy_test=true 时才执行 Deploy Test。

配置:

<+pipeline.variables.deploy_test> == "true"

运行:

feature-ci Input Set: deploy_test=false

main-test Input Set: deploy_test=true

验收:

feature-ci 跳过 Deploy Test main-test 执行 Deploy Test


三十六:练习 3:设计生产审批和部署条件

目标:

deploy_prod=true 时才进入生产审批和生产部署。

配置:

Approval For Prod: <+pipeline.variables.deploy_prod> == "true"

Deploy Prod: <+pipeline.variables.deploy_prod> == "true"

验收:

prod Input Set 执行审批 审批通过后部署生产 非 prod Input Set 跳过生产阶段


三十七:练习 4:DAG 依赖验证

目标:

Unit Test 和 Sonar Scan 并行,Build Image 等二者都成功后执行。

前提:

Harness 账号已启用 DAG Pipeline 功能

配置:

Unit Test:
  dependsOn:
    - prepare

Sonar Scan:
  dependsOn:
    - prepare

Build Image:
  dependsOn:
    - unit_test
    - sonar_scan

验收:

Prepare 先执行 Unit Test 和 Sonar Scan 并行 Build Image 等二者成功后执行


三十八:练习 5:并发压力观察

目标:

观察并行步骤对国内 CI 资源的影响。

观察项:

Harness Execution Graph Kubernetes harness-ci namespace Pod 数量 Harbor 推送耗时 SonarQube 后台任务队列 npm install 耗时 Delegate CPU/Memory

命令:

kubectl get pods -n harness-ci
kubectl top pods -n harness-ci
kubectl top pods -n harness-delegate-ng

验收:

能判断并行是否带来资源瓶颈 能提出降低并行度或扩容 Delegate 的方案


三十九:验收标准

完成本文后,应达到:

已理解多阶段流水线拆分原则 已能设计 Build、Test、Scan、Publish、Deploy、Verify、Approval 阶段 已能在 Stage 内配置并行步骤 已能通过变量控制 Stage 是否执行 已能为 dev/test/prod 设计不同触发路径 已理解传统 parallel 和 DAG dependsOn 的区别 已知道 DAG 需要功能开关 已能使用 Barrier 进行并行阶段同步 已能设计多阶段失败策略 已能识别并行带来的资源压力 已能为国内网络环境设计 Delegate、Harbor、npm、K8s 资源治理方案


四十:本篇总结

本篇完成了 Harness 多阶段流水线设计的系统讲解。

你需要重点记住:

Stage 应按职责拆分,不要一个阶段塞满所有逻辑 无依赖任务可以并行,有依赖任务必须串行 Unit Test、SonarQube、Dependency Audit 适合并行 Docker Build 必须等质量检查通过 Deploy 必须等镜像发布成功 Integration Test 必须等 dev 部署成功 Prod 必须经过审批 条件执行可以按分支、触发方式、变量控制流程 DAG 适合复杂依赖,但需要功能开关 Barrier 可以同步并行 Stage 或 Step Group 并行越多,对 Delegate、K8s、Harbor、npm 私服压力越大 企业落地时,多阶段设计必须配合失败策略、审批、回滚和资源治理