一:教程定位
在前 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 同时执行。
步骤:
- 打开 Quality Checks Stage
- 添加 parallel 分支
- 分别放入 Unit Test、SonarQube Scan、Dependency Audit
- 运行 Pipeline
- 查看 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 私服压力越大 企业落地时,多阶段设计必须配合失败策略、审批、回滚和资源治理