一:教程定位
在前 9 篇教程中,我们已经完成了 Harness 从入门到基础工程化的主要内容:环境搭建、CI/CD 流水线、代码触发器、测试制品管理、Kubernetes 部署、多环境参数化、审批定时触发、日志排查、CLI 与 YAML 管理。
第 10 篇开始进入一个非常关键的主题:失败后怎么办?
很多团队做 CI/CD 时,只关注“自动部署成功”,但企业真实发布更关注:
部署失败能不能自动停止? 部署失败能不能自动回滚? 回滚后流量是否恢复到旧版本? 失败时是否需要人工介入? 失败条件应该怎么配置? 哪些失败可以重试? 哪些失败必须立刻终止? 哪些失败应该触发回滚?
本篇就围绕 Harness 的 Failure Strategy、Kubernetes 回滚、基础金丝雀发布失败回退展开。
二:适合人群
本文面向中级用户,也适合已经完成前 9 篇的初学者继续学习。
适合角色:
DevOps 工程师 SRE 平台工程师 Kubernetes 运维工程师 技术负责人 负责生产发布稳定性的研发团队
建议已经具备:
了解 Harness Pipeline / Stage / Step 了解 Kubernetes Deployment / Service 了解 Harbor 镜像仓库 了解 Harness Delegate 了解 Input Set 和多环境部署 能查看 Harness Execution History 和 Step Log
三:学习目标
完成本文后,你应该能够:
理解 Harness Failure Strategy 的作用 区分 Step 级失败策略和 Stage 级失败策略 理解 All Errors、Timeout Errors、Connectivity Errors、Verification Failures 等错误类型 配置失败后自动 Rollback Stage 配置失败后 Retry,再失败后 Rollback 配置失败后 Manual Intervention,由人工选择重试、终止或回滚 使用 kubectl rollout undo 回滚 Kubernetes Deployment 模拟错误镜像导致部署失败 模拟 readinessProbe 失败导致 rollout 超时 使用 Harness 内置 K8s Rolling Deploy 自动回滚 使用 Shell Script 实现 Kubernetes 原生 canary 失败回滚 验证金丝雀失败后所有流量恢复到稳定版本 制定 dev/test/pre/prod 不同环境的失败策略
四:国内网络环境下的回滚架构
本文仍采用国内企业常见架构:
GitLab / Gitee ↓ Harness Pipeline ↓ Harness Delegate ↓ Harbor / ACR / TCR / SWR ↓ Kubernetes 集群
回滚链路如下:
新版本发布 ↓ Kubernetes 创建新 ReplicaSet 或 Canary Deployment ↓ 健康检查 / 验证失败 ↓ Harness Failure Strategy 触发 ↓ 执行 StageRollback 或自定义 rollback 脚本 ↓ Kubernetes 恢复到旧版本 ↓ Service 流量重新指向稳定版本
国内环境中要额外关注:
Delegate 能否访问 Kubernetes API Kubernetes 节点能否访问 Harbor Harbor 镜像 Tag 是否存在 Kubernetes RBAC 是否允许 rollout undo / patch / delete 自签证书是否导致镜像拉取失败 企业代理是否影响 Delegate 访问内网 K8s
五:失败处理的核心原则
企业发布失败处理要遵守几个原则。
1. CI 失败不进入 CD
例如:
npm install 失败 npm test 失败 SonarQube Quality Gate 失败 Docker build 失败 Docker push 失败
这些失败不应该触发部署,也不应该触发 Kubernetes 回滚,因为还没有部署到集群。
处理策略:
直接失败 终止流水线 由开发或平台工程师修复 重新提交或重新运行
2. CD 部署失败必须回滚或人工介入
例如:
Kubernetes 新 Pod 拉镜像失败 新 Pod CrashLoopBackOff readinessProbe 不通过 rollout status 超时 健康检查失败 自动验证失败
这些失败已经影响到部署过程,应该:
dev/test:可以自动回滚 pre:自动回滚 + 通知 prod:优先人工介入或自动回滚,取决于企业发布策略
3. 生产环境不建议 Ignore Failure
生产部署中不要轻易使用:
Ignore Failure Mark as Success
除非你非常明确该失败不影响发布结果,例如非关键通知失败。
4. 失败策略要分层
推荐:
Stage 级: 统一兜底,AllErrors → StageRollback
Step 级: 对个别步骤特殊处理,例如通知失败可 Ignore,网络短暂失败可 Retry
Pipeline 级: 重要生产流程可启用 Pipeline Rollback 或更高级全流程回滚
六:Harness Failure Strategy 基础概念
Harness Failure Strategy 由两部分组成:
失败条件:什么错误触发策略 处理动作:触发后做什么
常见失败条件:
All Errors Authentication Errors Authorization Errors Connectivity Errors Delegate Provisioning Errors Timeout Errors Verification Failures Policy Evaluation Failures Approval Rejection Delegate Restart Unknown Errors
常见处理动作:
Abort Retry Manual Intervention Rollback Stage Ignore Failure Mark as Success Mark as Failure
常用组合:
部署阶段 AllErrors → StageRollback 部署步骤 TimeoutErrors → Retry 2 次,仍失败则 StageRollback 通知步骤 AllErrors → Ignore Failure 生产部署 VerificationFailures → Manual Intervention 审批拒绝 ApprovalRejection → Abort
七:Step 级与 Stage 级失败策略
1. Stage 级失败策略
Stage 级策略作用于整个 Stage 内没有单独设置失败策略的 Step。
适合:
部署阶段统一回滚 生产阶段统一人工介入 整个 Stage 超时后终止
示例:
failureStrategies:
- onFailure:
errors:
- AllErrors
action:
type: StageRollback
含义:
这个 Stage 内只要发生错误,就执行 Stage Rollback
2. Step 级失败策略
Step 级策略只作用于某个具体 Step,并且可以覆盖 Stage 级策略。
适合:
某个健康检查失败后人工介入 某个网络步骤失败后重试 某个通知步骤失败但不影响部署
示例:
failureStrategies:
- onFailure:
errors:
- TimeoutErrors
action:
type: Retry
spec:
retryCount: 2
retryIntervals:
- 1m
onRetryFailure:
action:
type: StageRollback
含义:
该 Step 超时时先重试 2 次,每次间隔 1 分钟;仍失败则回滚整个 Stage
八:推荐失败策略设计
1. dev 环境
CI 失败:直接失败 CD 失败:自动回滚 通知失败:忽略 健康检查失败:自动回滚 审批:通常不需要
推荐:
AllErrors → StageRollback 通知步骤 → Ignore Failure
2. test 环境
CI 失败:直接失败 部署失败:自动回滚 健康检查失败:自动回滚 测试环境通知失败:忽略或标记失败
推荐:
TimeoutErrors → Retry 1~2 次,再 StageRollback AllErrors → StageRollback
3. pre 环境
CI 失败:直接失败 部署失败:自动回滚 + 通知 验证失败:Manual Intervention 或 StageRollback
推荐:
VerificationFailures → Manual Intervention TimeoutErrors → Retry,再 StageRollback AllErrors → StageRollback
4. prod 环境
CI 失败:直接失败 部署失败:Manual Intervention 或 StageRollback 验证失败:Manual Intervention 网络抖动:有限重试 审批拒绝:Abort 通知失败:不应阻断核心发布,但必须补偿通知
推荐:
VerificationFailures → Manual Intervention TimeoutErrors → Retry 1 次,再 Manual Intervention AllErrors → StageRollback 或 Manual Intervention ApprovalRejection → Abort
生产是否自动回滚,要看企业策略。如果系统无状态、数据库兼容、回滚风险低,可以自动回滚;如果涉及数据库变更、消息积压、状态迁移,建议人工介入。
九:Kubernetes 回滚基础
Kubernetes Deployment 每次 Pod Template 变化时会创建新的 ReplicaSet。
常见触发变化:
镜像 Tag 改变 环境变量改变 探针配置改变 资源 requests/limits 改变 ConfigMap/Secret 引用变化
常用命令:
kubectl rollout status deployment/harness-demo-app -n dev
kubectl rollout history deployment/harness-demo-app -n dev
kubectl rollout undo deployment/harness-demo-app -n dev
kubectl rollout undo deployment/harness-demo-app -n dev --to-revision=2
查看当前镜像:
kubectl get deploy harness-demo-app -n dev \
-o jsonpath='{.spec.template.spec.containers[0].image}'
查看 ReplicaSet:
kubectl get rs -n dev -l app=harness-demo-app
查看事件:
kubectl get events -n dev --sort-by=.metadata.creationTimestamp | tail -n 50
十:示例项目结构
本文推荐仓库结构:
harness-demo-app/ ├── app/ │ ├── package.json │ ├── server.js │ └── server.test.js ├── docker/ │ └── Dockerfile ├── k8s/ │ ├── stable/ │ │ ├── deployment.yaml │ │ └── service.yaml │ ├── canary/ │ │ └── deployment-canary.yaml │ └── values.yaml ├── scripts/ │ ├── verify-deployment.sh │ ├── rollback-deployment.sh │ ├── deploy-canary.sh │ ├── verify-canary.sh │ └── rollback-canary.sh └── harness/ └── pipelines/ └── rollback-demo.yaml
十一:稳定版本 Deployment
k8s/stable/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: harness-demo-app
namespace: dev
labels:
app: harness-demo-app
track: stable
spec:
replicas: 3
revisionHistoryLimit: 5
progressDeadlineSeconds: 120
selector:
matchLabels:
app: harness-demo-app
track: stable
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: harness-demo-app
track: stable
spec:
imagePullSecrets:
- name: harbor-pull-secret
containers:
- name: harness-demo-app
image: harbor.company.com/devops/harness-demo-app:STABLE_IMAGE_TAG
ports:
- containerPort: 3000
env:
- name: APP_ENV
value: dev
- name: APP_VERSION
value: STABLE_IMAGE_TAG
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 6
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 20
periodSeconds: 20
failureThreshold: 3
说明:
stable Deployment 是当前稳定版本 replicas=3 progressDeadlineSeconds=120,超过 120 秒未完成 rollout 会认为失败 revisionHistoryLimit=5,保留最近 5 个版本用于回滚
十二:Service 配置
k8s/stable/service.yaml
apiVersion: v1
kind: Service
metadata:
name: harness-demo-app
namespace: dev
labels:
app: harness-demo-app
spec:
type: ClusterIP
selector:
app: harness-demo-app
ports:
- name: http
port: 80
targetPort: 3000
这里 Service 只选择:
app: harness-demo-app
这意味着只要 Pod 有 app=harness-demo-app,就会被 Service 转发流量。
后续金丝雀 Deployment 也带有:
app: harness-demo-app track: canary
所以 Service 会同时转发到 stable 和 canary Pod。
如果 stable 有 3 个副本,canary 有 1 个副本,那么在没有服务网格的普通 Kubernetes Service 场景中,流量大致会按 Pod 数量分摊。
注意:
这不是精准 10% / 20% 权重流量控制。 普通 Kubernetes Service 只能基于 selector 选择 Pod,不能原生做精确流量权重。 如果需要精确流量比例,应使用 Ingress Controller、Service Mesh、Gateway API、Argo Rollouts 或 Harness 支持的高级流量管理能力。
本文为了保证企业或个人都能落地,使用 Kubernetes 原生 Pod 比例方式演示基础金丝雀和回滚。
十三:Canary Deployment
k8s/canary/deployment-canary.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: harness-demo-app-canary
namespace: dev
labels:
app: harness-demo-app
track: canary
spec:
replicas: 1
revisionHistoryLimit: 2
progressDeadlineSeconds: 120
selector:
matchLabels:
app: harness-demo-app
track: canary
template:
metadata:
labels:
app: harness-demo-app
track: canary
spec:
imagePullSecrets:
- name: harbor-pull-secret
containers:
- name: harness-demo-app
image: harbor.company.com/devops/harness-demo-app:CANARY_IMAGE_TAG
ports:
- containerPort: 3000
env:
- name: APP_ENV
value: dev
- name: APP_VERSION
value: CANARY_IMAGE_TAG
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 20
periodSeconds: 20
failureThreshold: 3
说明:
canary Deployment 名称是 harness-demo-app-canary 副本数为 1 和 stable 共用 app=harness-demo-app 标签 Service 会同时转发 stable 和 canary 失败时删除 canary Deployment,即可让流量回到 stable
十四:稳定版本部署脚本
scripts/deploy-stable.sh
#!/usr/bin/env bash
set -e
APP_NAME="${APP_NAME:-harness-demo-app}"
NAMESPACE="${NAMESPACE:-dev}"
STABLE_IMAGE_TAG="${STABLE_IMAGE_TAG:?STABLE_IMAGE_TAG is required}"
WORKDIR="/tmp/${APP_NAME}-stable"
rm -rf "${WORKDIR}"
mkdir -p "${WORKDIR}"
cp k8s/stable/deployment.yaml "${WORKDIR}/deployment.yaml"
cp k8s/stable/service.yaml "${WORKDIR}/service.yaml"
sed -i "s|STABLE_IMAGE_TAG|${STABLE_IMAGE_TAG}|g" "${WORKDIR}/deployment.yaml"
echo "部署 stable 版本:${STABLE_IMAGE_TAG}"
kubectl apply -f "${WORKDIR}/service.yaml"
kubectl apply -f "${WORKDIR}/deployment.yaml"
kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=180s
kubectl get deploy ${APP_NAME} -n ${NAMESPACE} -o wide
kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} -o wide
echo "stable 部署完成"
十五:稳定版本验证脚本
scripts/verify-deployment.sh
#!/usr/bin/env bash
set -e
APP_NAME="${APP_NAME:-harness-demo-app}"
NAMESPACE="${NAMESPACE:-dev}"
HEALTH_PATH="${HEALTH_PATH:-/health}"
echo "验证 Deployment rollout 状态"
kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=180s
echo "查看 Pod"
kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} -o wide
echo "查看 Service 和 Endpoints"
kubectl get svc ${APP_NAME} -n ${NAMESPACE}
kubectl get endpoints ${APP_NAME} -n ${NAMESPACE}
echo "端口转发验证健康检查"
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_PATH}
kill ${PF_PID}
echo "验证通过"
十六:普通 Deployment 回滚脚本
scripts/rollback-deployment.sh
#!/usr/bin/env bash
set -e
APP_NAME="${APP_NAME:-harness-demo-app}"
NAMESPACE="${NAMESPACE:-dev}"
TO_REVISION="${TO_REVISION:-}"
echo "查看 rollout history"
kubectl rollout history deployment/${APP_NAME} -n ${NAMESPACE} || true
if [ -n "${TO_REVISION}" ]; then
echo "回滚到指定 revision:${TO_REVISION}"
kubectl rollout undo deployment/${APP_NAME} -n ${NAMESPACE} --to-revision=${TO_REVISION}
else
echo "回滚到上一版本"
kubectl rollout undo deployment/${APP_NAME} -n ${NAMESPACE}
fi
echo "等待回滚完成"
kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=180s
echo "回滚后镜像:"
kubectl get deploy ${APP_NAME} -n ${NAMESPACE} \
-o jsonpath='{.spec.template.spec.containers[0].image}'
echo
kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} -o wide
echo "回滚完成"
十七:Canary 部署脚本
scripts/deploy-canary.sh
#!/usr/bin/env bash
set -e
APP_NAME="${APP_NAME:-harness-demo-app}"
NAMESPACE="${NAMESPACE:-dev}"
CANARY_IMAGE_TAG="${CANARY_IMAGE_TAG:?CANARY_IMAGE_TAG is required}"
WORKDIR="/tmp/${APP_NAME}-canary"
rm -rf "${WORKDIR}"
mkdir -p "${WORKDIR}"
cp k8s/canary/deployment-canary.yaml "${WORKDIR}/deployment-canary.yaml"
sed -i "s|CANARY_IMAGE_TAG|${CANARY_IMAGE_TAG}|g" "${WORKDIR}/deployment-canary.yaml"
echo "部署 canary 版本:${CANARY_IMAGE_TAG}"
kubectl apply -f "${WORKDIR}/deployment-canary.yaml"
kubectl rollout status deployment/${APP_NAME}-canary -n ${NAMESPACE} --timeout=180s
kubectl get deploy -n ${NAMESPACE} -l app=${APP_NAME}
kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} -o wide
kubectl get endpoints ${APP_NAME} -n ${NAMESPACE}
echo "canary 部署完成"
十八:Canary 验证脚本
scripts/verify-canary.sh
#!/usr/bin/env bash
set -e
APP_NAME="${APP_NAME:-harness-demo-app}"
NAMESPACE="${NAMESPACE:-dev}"
HEALTH_PATH="${HEALTH_PATH:-/health}"
EXPECTED_CANARY_TAG="${EXPECTED_CANARY_TAG:-}"
echo "查看 canary Pod"
kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME},track=canary -o wide
echo "查看 Service Endpoints"
kubectl get endpoints ${APP_NAME} -n ${NAMESPACE}
echo "验证 canary rollout"
kubectl rollout status deployment/${APP_NAME}-canary -n ${NAMESPACE} --timeout=180s
echo "端口转发验证健康检查"
kubectl port-forward svc/${APP_NAME} 18081:80 -n ${NAMESPACE} >/tmp/port-forward-canary.log 2>&1 &
PF_PID=$!
sleep 5
for i in $(seq 1 10); do
echo "请求 ${i}"
curl -f http://127.0.0.1:18081${HEALTH_PATH}
echo
done
kill ${PF_PID}
echo "canary 健康检查通过"
注意:
这个验证脚本只是检查 Service 整体健康。 如果要精确确认请求命中了 canary,需要应用返回 APP_VERSION,或使用日志/指标区分 stable 和 canary。
十九:Canary 回滚脚本
scripts/rollback-canary.sh
#!/usr/bin/env bash
set -e
APP_NAME="${APP_NAME:-harness-demo-app}"
NAMESPACE="${NAMESPACE:-dev}"
echo "开始回滚 canary:删除 canary Deployment"
kubectl delete deployment ${APP_NAME}-canary -n ${NAMESPACE} --ignore-not-found=true
echo "等待 canary Pod 删除"
for i in $(seq 1 30); do
COUNT=$(kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME},track=canary --no-headers 2>/dev/null | wc -l | tr -d ' ')
echo "剩余 canary Pod 数:${COUNT}"
if [ "${COUNT}" = "0" ]; then
break
fi
sleep 3
done
echo "确认 stable Pod"
kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME},track=stable -o wide
echo "确认 Service Endpoints"
kubectl get endpoints ${APP_NAME} -n ${NAMESPACE}
echo "canary 回滚完成,流量已回到 stable Pod"
这就是“金丝雀失败后回滚所有流量”的基础实现:删除 canary Deployment 后,Service 只剩 stable Pod 可接收流量。
二十:Harness Pipeline 方案一:K8s Rolling Deploy 自动回滚
这是最基础、最推荐初学者先掌握的方式。
Pipeline 结构:
Stage: Deploy Dev Step 1: K8s Rolling Deploy Step 2: Verify Deployment Failure Strategy: AllErrors → StageRollback
Stage 级 failure strategy:
failureStrategies:
- onFailure:
errors:
- AllErrors
action:
type: StageRollback
如果 K8s Rolling Deploy 或 Verify Deployment 失败,Harness 会执行 Stage Rollback。
适用场景:
普通 Kubernetes Deployment 滚动升级 dev/test/pre 环境 无状态服务 没有复杂流量灰度要求
二十一:Rolling Deploy Pipeline YAML 示例
pipeline:
name: rollback-basic-demo
identifier: rollback_basic_demo
projectIdentifier: harness_demo
orgIdentifier: devops_lab
tags:
tutorial: harness-10
type: rollback
variables:
- name: app_name
type: String
value: harness-demo-app
- name: namespace
type: String
value: dev
- name: image_tag
type: String
value: <+input>
stages:
- 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
- step:
name: Verify Deployment
identifier: verify_deployment
type: ShellScript
timeout: 5m
spec:
shell: Bash
source:
type: Inline
spec:
script: |
set -e
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
APP_NAME="<+pipeline.variables.app_name>"
NAMESPACE="<+pipeline.variables.namespace>"
kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=180s
kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} -o wide
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
kill ${PF_PID}
echo "部署验证通过"
failureStrategies:
- onFailure:
errors:
- AllErrors
action:
type: StageRollback
二十二:模拟 Rolling Deploy 失败
1. 先部署稳定版本
运行 Pipeline:
image_tag = v1.0.0
验证:
kubectl get deploy harness-demo-app -n dev
kubectl get pods -n dev -l app=harness-demo-app
kubectl get deploy harness-demo-app -n dev \
-o jsonpath='{.spec.template.spec.containers[0].image}'
2. 部署错误版本
运行 Pipeline:
image_tag = not-exist-tag
预期:
K8s Rolling Deploy 失败 Pod 出现 ImagePullBackOff Harness 触发 StageRollback Deployment 回到上一版本
3. 验证回滚
kubectl get deploy harness-demo-app -n dev \
-o jsonpath='{.spec.template.spec.containers[0].image}'
kubectl rollout history deployment/harness-demo-app -n dev
kubectl get pods -n dev -l app=harness-demo-app -o wide
预期镜像回到:
harbor.company.com/devops/harness-demo-app:v1.0.0
二十三:Harness Pipeline 方案二:Retry 后回滚
对于网络抖动类问题,直接回滚可能太激进。
可以设置:
TimeoutErrors → Retry 2 次 仍失败 → StageRollback
Step 级 failure strategy:
failureStrategies:
- onFailure:
errors:
- TimeoutErrors
action:
type: Retry
spec:
retryCount: 2
retryIntervals:
- 1m
onRetryFailure:
action:
type: StageRollback
适合:
临时网络超时 临时 API Server 响应慢 临时 Harbor 拉取慢 健康检查偶发超时
不适合:
镜像 Tag 不存在 Secret 错误 RBAC 权限不足 代码本身有问题 readinessProbe 配置错误
这些错误重试也不会成功,应该直接失败或回滚。
二十四:Harness Pipeline 方案三:Manual Intervention
生产环境中,某些失败不应立即自动回滚,而是等待人工判断。
例如:
新版本部分 Pod Ready 监控指标不稳定 业务检查异常但不确定是否新版本导致 数据库变更不可逆 回滚可能带来更大风险
可以配置:
VerificationFailures → Manual Intervention TimeoutErrors → Manual Intervention
YAML 示例:
failureStrategies:
- onFailure:
errors:
- VerificationFailures
- TimeoutErrors
action:
type: ManualIntervention
spec:
timeout: 30m
onTimeout:
action:
type: StageRollback
含义:
验证失败或超时时,暂停 30 分钟等待人工决策; 如果无人处理,自动执行 StageRollback。
适合生产:
业务负责人需要确认 运维需要查看监控 需要判断是否回滚 需要等待外部系统恢复
二十五:Harness Pipeline 方案四:Shell Script 实现金丝雀回滚
如果你的 Harness 账号或当前 CD 模块已启用内置 Kubernetes Canary 策略,可以优先使用内置策略。
但为了保证个人和企业都能落地,本文提供一个不依赖特殊流量网关的 Kubernetes 原生 Canary 方案:
stable Deployment:3 个副本,承载主要流量 canary Deployment:1 个副本,承载小部分流量 Service:selector 选择 app=harness-demo-app 失败时:删除 canary Deployment 结果:Service 只剩 stable Pod,所有流量回到 stable
Pipeline 结构:
Stage: Canary Deploy Step 1: Deploy Canary Step 2: Verify Canary Step 3: Promote Canary Failure Strategy: AllErrors → 执行 rollback-canary.sh
这里我们使用 Shell Script Step 显式执行回滚脚本。这样逻辑透明,适合教学和企业初期落地。
二十六:Canary Pipeline YAML 示例
pipeline:
name: canary-rollback-demo
identifier: canary_rollback_demo
projectIdentifier: harness_demo
orgIdentifier: devops_lab
tags:
tutorial: harness-10
type: canary
variables:
- name: app_name
type: String
value: harness-demo-app
- name: namespace
type: String
value: dev
- name: stable_image_tag
type: String
value: <+input>
- name: canary_image_tag
type: String
value: <+input>
stages:
- stage:
name: Canary Deploy
identifier: canary_deploy
type: Custom
spec:
execution:
steps:
- step:
name: Deploy Stable
identifier: deploy_stable
type: ShellScript
timeout: 10m
spec:
shell: Bash
onDelegate: true
source:
type: Inline
spec:
script: |
set -e
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
chmod +x scripts/deploy-stable.sh
APP_NAME="<+pipeline.variables.app_name>" \
NAMESPACE="<+pipeline.variables.namespace>" \
STABLE_IMAGE_TAG="<+pipeline.variables.stable_image_tag>" \
scripts/deploy-stable.sh
- step:
name: Deploy Canary
identifier: deploy_canary
type: ShellScript
timeout: 10m
spec:
shell: Bash
onDelegate: true
source:
type: Inline
spec:
script: |
set -e
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
chmod +x scripts/deploy-canary.sh
APP_NAME="<+pipeline.variables.app_name>" \
NAMESPACE="<+pipeline.variables.namespace>" \
CANARY_IMAGE_TAG="<+pipeline.variables.canary_image_tag>" \
scripts/deploy-canary.sh
- step:
name: Verify Canary
identifier: verify_canary
type: ShellScript
timeout: 5m
spec:
shell: Bash
onDelegate: true
source:
type: Inline
spec:
script: |
set -e
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
chmod +x scripts/verify-canary.sh
APP_NAME="<+pipeline.variables.app_name>" \
NAMESPACE="<+pipeline.variables.namespace>" \
HEALTH_PATH="/health" \
scripts/verify-canary.sh
- step:
name: Promote Canary
identifier: promote_canary
type: ShellScript
timeout: 10m
spec:
shell: Bash
onDelegate: true
source:
type: Inline
spec:
script: |
set -e
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
APP_NAME="<+pipeline.variables.app_name>"
NAMESPACE="<+pipeline.variables.namespace>"
CANARY_TAG="<+pipeline.variables.canary_image_tag>"
echo "将 stable Deployment 更新为 canary 镜像:${CANARY_TAG}"
kubectl set image deployment/${APP_NAME} \
${APP_NAME}=harbor.company.com/devops/${APP_NAME}:${CANARY_TAG} \
-n ${NAMESPACE}
kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=180s
echo "删除 canary Deployment"
kubectl delete deployment ${APP_NAME}-canary -n ${NAMESPACE} --ignore-not-found=true
kubectl get deploy -n ${NAMESPACE} -l app=${APP_NAME}
kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} -o wide
failureStrategies:
- onFailure:
errors:
- AllErrors
action:
type: ManualIntervention
spec:
timeout: 10m
onTimeout:
action:
type: Abort
说明:
这里使用 Custom Stage 是为了清楚演示 Kubernetes 原生 canary 的全过程。 生产环境可以用 Harness 内置 Kubernetes Canary 策略、服务网格或网关控制流量。
二十七:Canary 失败后自动回滚脚本 Step
如果希望失败后自动删除 canary,需要在 Rollback 逻辑中执行:
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
chmod +x scripts/rollback-canary.sh
APP_NAME="<+pipeline.variables.app_name>" \
NAMESPACE="<+pipeline.variables.namespace>" \
scripts/rollback-canary.sh
在 Harness 中,推荐两种实现方式。
方式一:在失败策略中使用 StageRollback,并把 rollback-canary.sh 放到 Rollback 步骤
适合使用 Harness Deployment Stage 时。
方式二:在 Custom Stage 中增加失败后人工介入,由人工选择运行 rollback-canary.sh
适合教学和初期落地。
方式三:用独立 Rollback Pipeline
创建独立 Pipeline:
Pipeline: rollback-canary Input: app_name namespace Action: delete canary Deployment verify stable endpoints
当 Canary Pipeline 失败时,人工或通知触发 rollback-canary Pipeline。
企业生产环境推荐独立 Rollback Pipeline,因为:
权限更清晰 审计更清晰 回滚可单独执行 不会和发布流程耦合太深
二十八:模拟 Canary 失败
1. 准备稳定版本
先构建并推送:
harbor.company.com/devops/harness-demo-app:v1.0.0
运行 canary Pipeline:
stable_image_tag = v1.0.0 canary_image_tag = v1.0.1
预期:
stable 部署成功 canary 部署成功 verify canary 成功 promote canary 成功 canary 删除 stable 更新到 v1.0.1
2. 模拟错误镜像
运行:
stable_image_tag = v1.0.0 canary_image_tag = not-exist-tag
预期:
stable 正常 canary 部署失败 canary Pod ImagePullBackOff Pipeline 进入失败策略 执行 rollback-canary 或等待人工介入 canary Deployment 被删除 Service 只剩 stable Pod 所有流量回到 v1.0.0
3. 验证 canary 已删除
kubectl get deploy -n dev -l app=harness-demo-app
kubectl get pods -n dev -l app=harness-demo-app -o wide
kubectl get endpoints harness-demo-app -n dev
预期:
只剩 harness-demo-app stable Deployment 不存在 harness-demo-app-canary Endpoints 只包含 stable Pod IP
二十九:模拟 readinessProbe 失败
除了错误镜像,还可以模拟健康检查失败。
修改 canary Deployment:
readinessProbe:
httpGet:
path: /wrong-health
port: 3000
运行:
stable_image_tag = v1.0.0 canary_image_tag = v1.0.1
预期:
canary Pod Running 但不 Ready kubectl rollout status 超时 Verify Canary 失败 触发回滚 删除 canary Deployment 流量回到 stable
排查:
kubectl describe pod <canary-pod> -n dev
常见事件:
Readiness probe failed: HTTP probe failed with statuscode: 404
三十:失败条件调整练习
练习 1:AllErrors 直接回滚
配置:
failureStrategies:
- onFailure:
errors:
- AllErrors
action:
type: StageRollback
验证:
部署错误镜像 Stage 自动回滚 无需人工介入
适合:
dev/test 无状态服务 回滚风险低
练习 2:TimeoutErrors 先重试再回滚
配置:
failureStrategies:
- onFailure:
errors:
- TimeoutErrors
action:
type: Retry
spec:
retryCount: 2
retryIntervals:
- 1m
onRetryFailure:
action:
type: StageRollback
验证:
把 progressDeadlineSeconds 设置较短 或把 readinessProbe 改错 观察重试 2 次后回滚
适合:
网络偶发超时 K8s API Server 临时响应慢 健康检查偶发失败
练习 3:VerificationFailures 进入人工介入
配置:
failureStrategies:
- onFailure:
errors:
- VerificationFailures
action:
type: ManualIntervention
spec:
timeout: 30m
onTimeout:
action:
type: StageRollback
验证:
让健康检查失败 Pipeline 暂停等待人工处理 超时后自动回滚
适合:
pre/prod 需要人工确认业务指标 回滚风险需要判断
练习 4:通知失败不阻断发布
对于 Slack/企业微信通知 Step:
failureStrategies:
- onFailure:
errors:
- AllErrors
action:
type: Ignore
说明:
通知失败不影响应用发布 但建议在后续补偿通知或记录告警 生产环境的核心审批通知不建议完全忽略
三十一:回滚验证清单
自动回滚后不能只看 Pipeline 变绿,还要验证以下内容:
Deployment 是否恢复到旧镜像 Pod 是否全部 Ready Service endpoints 是否只包含稳定版本 Pod Canary Deployment 是否删除 应用 /health 是否正常 业务接口是否正常 Harbor 中旧镜像是否仍存在 Kubernetes rollout history 是否可查看 Harness Execution History 是否记录回滚
命令:
kubectl get deploy -n dev -l app=harness-demo-app
kubectl get pods -n dev -l app=harness-demo-app -o wide
kubectl get endpoints harness-demo-app -n dev
kubectl rollout history deployment/harness-demo-app -n dev
kubectl get deploy harness-demo-app -n dev \
-o jsonpath='{.spec.template.spec.containers[0].image}'
三十二:常见问题排查
1. StageRollback 没有执行
常见原因:
Failure Strategy 没配置在正确 Stage Step 级策略覆盖了 Stage 级策略 错误类型没有命中 失败步骤被 Ignore Failure 条件执行和失败策略冲突
排查:
查看 Harness Execution History 查看失败 Step 的 Failure Strategy 查看 Stage Advanced 配置 确认错误类型是否是 AllErrors 或 TimeoutErrors
2. 回滚后镜像没有变化
常见原因:
部署前后使用了同一个 latest Tag Kubernetes Pod Template 没有变化 revisionHistoryLimit 太小 Deployment 不存在上一 revision
解决:
不要在生产使用 latest 每次发布使用唯一镜像 Tag 设置 revisionHistoryLimit >= 3 发布前确认 rollout history
3. rollback-canary.sh 删除不了 canary
常见原因:
Kubernetes RBAC 无 delete deployment 权限 namespace 写错 canary Deployment 名称不一致 Delegate kubeconfig 不正确
排查:
kubectl auth can-i delete deployment -n dev
kubectl get deploy -n dev
4. Canary 删除后流量仍异常
常见原因:
Service selector 不正确 Endpoints 仍包含异常 Pod stable Pod 本身也不健康 Ingress 或网关缓存 应用本身依赖外部服务异常
排查:
kubectl get endpoints harness-demo-app -n dev
kubectl get pods -n dev -l app=harness-demo-app --show-labels
kubectl describe svc harness-demo-app -n dev
5. Retry 重试了也没用
常见原因:
错误是配置错误而不是临时错误 镜像 Tag 不存在 Secret 错误 RBAC 权限不足 readinessProbe 路径写错
建议:
只有临时性错误才适合 Retry 配置错误应直接失败或回滚
6. Manual Intervention 没人处理
常见原因:
审批人不在用户组 没有通知 Timeout 太长 责任人不明确
建议:
生产失败 Manual Intervention 必须配合通知 设置合理 timeout Post timeout action 应选择 Rollback 或 Abort
三十三:企业最佳实践
1. 生产环境不要只依赖自动回滚
自动回滚很重要,但生产发布还需要:
审批 变更单 监控验证 业务验证 数据库变更兼容 回滚预案 负责人在线
2. 区分无状态和有状态服务
无状态服务:
适合自动回滚 适合 Rolling / Canary 适合快速恢复
有状态服务:
谨慎自动回滚 特别关注数据库 schema 特别关注消息消费和数据一致性 必要时人工介入
3. 镜像 Tag 必须不可变
禁止生产使用:
latest
推荐:
v1.0.0 release-20260618-001 commit sha
4. 保留回滚版本
Kubernetes:
revisionHistoryLimit: 5
Harbor:
保留最近 N 个 release 镜像 禁止生产镜像被覆盖 开启镜像保留策略
5. 回滚也要审计
记录:
谁触发了回滚 什么时候回滚 从哪个版本回到哪个版本 原因是什么 影响范围是什么 是否需要复盘
6. 回滚后要验证
回滚后必须执行:
rollout status Pod Ready Service endpoints 健康检查 业务接口冒烟 监控指标恢复
三十四:练习 1:配置 AllErrors 自动回滚
目标:
部署错误镜像后自动回滚。
步骤:
- Stage Failure Strategy 设置 AllErrors → StageRollback
- 先部署 v1.0.0
- 再部署 not-exist-tag
- 查看 Harness 是否触发 rollback
- 查看 Kubernetes 是否回到 v1.0.0
验收:
Pipeline 失败后自动执行 StageRollback Deployment 镜像回到旧版本 Pod 全部 Ready
三十五:练习 2:配置 Retry 后回滚
目标:
超时错误先重试,再回滚。
步骤:
- Step Failure Strategy 设置 TimeoutErrors → Retry 2 次
- Post Retry Failure Action 设置 StageRollback
- 制造 readinessProbe 失败
- 观察重试和回滚
验收:
Harness 重试指定次数 重试失败后执行回滚
三十六:练习 3:配置 Manual Intervention
目标:
生产验证失败时暂停等待人工决策。
步骤:
- VerificationFailures 设置 ManualIntervention
- Timeout 设置 30m
- Post Timeout Action 设置 StageRollback
- 制造健康检查失败
- 人工选择 Retry / Abort / Rollback
验收:
Pipeline 进入 Paused 人工操作后继续处理 超时无人处理则自动按配置执行
三十七:练习 4:模拟 Canary 失败并回滚所有流量
目标:
canary 新版本失败后删除 canary,所有 Service 流量回到 stable。
步骤:
- 部署 stable v1.0.0,replicas=3
- 部署 canary not-exist-tag,replicas=1
- 观察 canary ImagePullBackOff
- 执行 rollback-canary.sh
- 查看 endpoints 只剩 stable Pod
验收:
harness-demo-app-canary 被删除 Service endpoints 不再包含 canary Pod stable Pod 仍然 Ready 业务健康检查正常
三十八:验收标准
完成本文后,应达到:
已理解 Harness Failure Strategy 已能配置 Stage 级失败策略 已能配置 Step 级失败策略 已能区分 AllErrors、TimeoutErrors、VerificationFailures 等错误类型 已能配置 AllErrors → StageRollback 已能配置 Retry 后 StageRollback 已能配置 Manual Intervention 已能使用 kubectl rollout undo 回滚 Deployment 已能模拟错误镜像导致部署失败 已能模拟 readinessProbe 失败导致 rollout 超时 已能使用 Kubernetes 原生方式模拟 canary 发布 已能在 canary 失败后删除 canary 并恢复 stable 流量 已能验证回滚结果 已能制定 dev/test/pre/prod 不同失败策略