Harness 教程 16:Kubernetes + Helm 部署示例:Helm Chart、values 管理、HelmDeploy 与回滚落地
一:教程定位
在前面的教程中,我们已经完成了 Kubernetes Manifest 部署、变量参数化、Input Set、多阶段流水线、模板复用、质量扫描和高级部署策略。
第 16 篇开始进入企业中非常常见的 Kubernetes 应用交付方式:Helm 部署。
很多团队在项目初期会直接使用 Kubernetes YAML:
deployment.yaml service.yaml configmap.yaml ingress.yaml
随着环境变多、服务变多、配置变复杂,就会遇到这些问题:
dev/test/prod 三套 YAML 大量重复 镜像地址、端口、副本数、资源限制到处修改 ConfigMap、Secret、Service、Ingress 难以统一管理 发布版本和配置变更缺少清晰记录 回滚时不知道应该回滚哪个配置组合 多个微服务无法统一模板规范
Helm 的核心价值是:把 Kubernetes 资源打包成一个可配置、可版本化、可回滚的应用包。
本篇会用一个简单的 Node.js Web 服务作为示例,完整演示:
编写 Helm Chart 拆分 values.yaml、values-dev.yaml、values-test.yaml、values-prod.yaml 在本地 lint/template 验证 Chart 在 Harness 中配置 Helm Service 使用 HelmDeploy 步骤发布应用 部署后做健康检查 修改 Chart 值并发布新版本 通过 Helm 回滚旧版本
二:适合人群
本文面向中级用户,适合:
DevOps 工程师 平台工程师 SRE Kubernetes 运维人员 微服务开发负责人 正在把 YAML 部署迁移到 Helm 的团队 准备在 Harness 中标准化 Helm 发布的团队
建议已经具备:
了解 Kubernetes Deployment / Service / Ingress 了解 Docker 镜像构建与推送 了解 Harbor 私有镜像仓库 了解 Harness Pipeline / Stage / Step 了解 Harness Connector / Service / Environment / Infrastructure 了解 Input Set 基础用法 能看懂 YAML 和 Bash
三:学习目标
完成本文后,你应该能够:
理解 Helm Chart 的标准目录结构 理解 Chart.yaml、values.yaml、templates 的作用 编写一个可部署 Web 服务的 Helm Chart 为 dev/test/prod 编写不同 values 文件 使用 helm lint 和 helm template 做发布前检查 理解 Harness-managed Helm 与 Native Helm 的区别 在 Harness 中配置 Native Helm Service 使用 HelmDeploy 步骤部署 Helm Chart 在流水线中传入 chart version、image tag、namespace、release name 部署后检查 helm release、Pod、Service、健康接口 修改 values 后重新发布 使用 Helm Rollback 或 helm rollback 回滚到旧版本 掌握国内网络环境下 Helm Chart 仓库、Harbor OCI、Delegate 访问问题
四:Helm 部署在 Harness 中的两种方式
Harness 中与 Helm 有关的部署大体可以分为两类。
1. Harness-managed Helm
这种方式的核心流程是:
Harness 拉取 Helm Chart ↓ 执行 helm template 渲染 Kubernetes YAML ↓ Harness 使用 kubectl apply 部署 ↓ Harness 管理 Kubernetes 资源版本和回滚
适合:
希望 Harness 编排 Kubernetes Rolling / Canary / Blue-Green 希望 Harness 管理 ConfigMap / Secret 版本 不依赖 Helm hooks 不强依赖 Helm release history 主要把 Helm 当作 YAML 模板引擎
注意:
Harness-managed Helm 不等同于原生 helm install/upgrade。 它更像是用 Helm 渲染模板,再由 Harness/Kubernetes 部署资源。
2. Native Helm
这种方式更接近原生 Helm:
Harness 拉取 Helm Chart ↓ Harness 执行 helm install / helm upgrade ↓ Helm 管理 release history ↓ 失败或手动回滚时使用 helm rollback
适合:
已有成熟 Helm Chart 使用 Helm hooks 使用 subcharts / dependencies 希望保留 Helm 原生 release history 希望用 helm rollback 回滚 从现有 Helm 发布流程迁移到 Harness
本文示例以 Native Helm + HelmDeploy 为主,因为它最符合“在流水线中使用 HelmDeploy 步骤发布,并练习修改 Chart 值和回滚旧版本”的目标。
五:本文最终流水线结构
Pipeline: helm-deploy-demo
Stage 1: CI Build Step 1: npm install Step 2: npm test Step 3: docker build Step 4: docker push Harbor
Stage 2: Helm Chart Check Step 1: helm lint Step 2: helm template Step 3: kubeconform / kubectl dry-run 可选
Stage 3: Helm Deploy Step 1: HelmDeploy Step 2: helm status Step 3: kubectl get pods Step 4: curl /health
Stage 4: Manual Rollback,可选 Step 1: Helm Rollback Step 2: 验证回滚后版本
推荐参数:
app_name = harness-helm-demo release_name = harness-helm-demo namespace = dev/test/prod chart_version = 0.1.0 image_repository = harbor.company.com/devops/harness-helm-demo image_tag = dev-main-a1b2c3d-100 values_file = values-dev.yaml / values-test.yaml / values-prod.yaml
六:示例仓库结构
harness-helm-demo/ ├── app/ │ ├── package.json │ ├── server.js │ └── server.test.js ├── docker/ │ └── Dockerfile ├── charts/ │ └── harness-helm-demo/ │ ├── Chart.yaml │ ├── values.yaml │ ├── values-dev.yaml │ ├── values-test.yaml │ ├── values-prod.yaml │ ├── values.schema.json │ └── templates/ │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── service.yaml │ ├── ingress.yaml │ ├── configmap.yaml │ ├── hpa.yaml │ ├── serviceaccount.yaml │ └── NOTES.txt ├── scripts/ │ ├── helm-lint.sh │ ├── helm-template.sh │ ├── helm-package.sh │ ├── helm-push-oci.sh │ ├── helm-release-status.sh │ ├── helm-rollback.sh │ └── k8s-health-check.sh └── harness/ ├── pipeline-helm-deploy.yaml └── inputsets/ ├── dev.yaml ├── test.yaml └── prod.yaml
七:准备示例应用
app/package.json
{
"name": "harness-helm-demo",
"version": "1.0.0",
"description": "Harness Helm deployment demo",
"main": "server.js",
"scripts": {
"start": "node server.js",
"test": "jest --runInBand"
},
"dependencies": {
"express": "^4.18.3"
},
"devDependencies": {
"jest": "^29.7.0",
"supertest": "^6.3.4"
}
}
app/server.js
const express = require("express");
function createApp() {
const app = express();
app.get("/", (req, res) => {
res.json({
app: process.env.APP_NAME || "harness-helm-demo",
env: process.env.APP_ENV || "dev",
version: process.env.APP_VERSION || "local",
status: "ok"
});
});
app.get("/health", (req, res) => {
res.status(200).json({
status: "UP"
});
});
return app;
}
if (require.main === module) {
const port = process.env.PORT || 3000;
const app = createApp();
app.listen(port, () => {
console.log(`server started on port ${port}`);
});
}
module.exports = createApp;
app/server.test.js
const request = require("supertest");
const createApp = require("./server");
describe("harness-helm-demo", () => {
test("GET /health should return UP", async () => {
const app = createApp();
const response = await request(app).get("/health");
expect(response.status).toBe(200);
expect(response.body.status).toBe("UP");
});
test("GET / should return app info", async () => {
const app = createApp();
const response = await request(app).get("/");
expect(response.status).toBe(200);
expect(response.body.status).toBe("ok");
});
});
八:Dockerfile
docker/Dockerfile
FROM harbor.company.com/library/node:20-alpine
WORKDIR /app
COPY app/package*.json ./
RUN npm config set registry https://registry.npmmirror.com \
&& npm install --omit=dev
COPY app/ ./
ENV PORT=3000
EXPOSE 3000
CMD ["node", "server.js"]
国内环境建议:
不要直接使用 docker.io/library/node 先把 node:20-alpine 同步到 Harbor npm 使用 npmmirror 或企业 npm 私服 生产镜像不要安装 devDependencies
九:创建 Helm Chart
可以先用命令生成骨架:
mkdir -p charts
helm create charts/harness-helm-demo
然后按本文内容精简和改造。
十:Chart.yaml
charts/harness-helm-demo/Chart.yaml
apiVersion: v2
name: harness-helm-demo
description: A Helm chart for Harness Kubernetes Helm deployment demo
type: application
version: 0.1.0
appVersion: "1.0.0"
kubeVersion: ">=1.23.0"
maintainers:
- name: platform-team
email: platform@example.com
keywords:
- harness
- helm
- kubernetes
- demo
说明:
version 是 Chart 包版本。 appVersion 是应用版本,只是描述信息,不直接决定镜像 tag。 生产中建议 Chart version 和应用 image tag 分开管理。
推荐规则:
Chart version: 0.1.0、0.2.0、1.0.0
App image tag: dev-main-a1b2c3d-100 test-main-a1b2c3d-101 v1.2.0
十一:values.yaml 默认值
charts/harness-helm-demo/values.yaml
replicaCount: 1
image:
repository: harbor.company.com/devops/harness-helm-demo
tag: "latest"
pullPolicy: IfNotPresent
imagePullSecrets:
- name: harbor-pull-secret
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
name: ""
podAnnotations: {}
podLabels: {}
service:
type: ClusterIP
port: 80
targetPort: 3000
container:
port: 3000
env:
APP_NAME: harness-helm-demo
APP_ENV: dev
APP_VERSION: latest
LOG_LEVEL: info
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
readinessProbe:
enabled: true
path: /health
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 6
livenessProbe:
enabled: true
path: /health
initialDelaySeconds: 20
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 3
ingress:
enabled: false
className: nginx
host: harness-helm-demo.local
path: /
tls:
enabled: false
secretName: ""
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 5
targetCPUUtilizationPercentage: 70
nodeSelector: {}
tolerations: []
affinity: {}
注意:
values.yaml 里可以有默认值,但生产不要真的使用 latest。 后续环境 values 文件会覆盖 image.tag、replicaCount、APP_ENV 等字段。
十二:values-dev.yaml
charts/harness-helm-demo/values-dev.yaml
replicaCount: 1
image:
repository: harbor.company.com/devops/harness-helm-demo
tag: "dev"
pullPolicy: IfNotPresent
env:
APP_ENV: dev
APP_VERSION: dev
LOG_LEVEL: debug
resources:
requests:
cpu: 50m
memory: 96Mi
limits:
cpu: 300m
memory: 256Mi
ingress:
enabled: false
autoscaling:
enabled: false
十三:values-test.yaml
charts/harness-helm-demo/values-test.yaml
replicaCount: 2
image:
repository: harbor.company.com/devops/harness-helm-demo
tag: "test"
pullPolicy: IfNotPresent
env:
APP_ENV: test
APP_VERSION: test
LOG_LEVEL: info
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
ingress:
enabled: true
className: nginx
host: harness-helm-demo-test.company.com
path: /
tls:
enabled: false
autoscaling:
enabled: false
十四:values-prod.yaml
charts/harness-helm-demo/values-prod.yaml
replicaCount: 3
image:
repository: harbor.company.com/devops/harness-helm-demo
tag: "v1.0.0"
pullPolicy: IfNotPresent
env:
APP_ENV: prod
APP_VERSION: v1.0.0
LOG_LEVEL: warn
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
ingress:
enabled: true
className: nginx
host: harness-helm-demo.company.com
path: /
tls:
enabled: true
secretName: harness-helm-demo-tls
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
生产建议:
replicaCount 至少 2 readinessProbe 必须启用 livenessProbe 必须启用 resources 必须设置 image.tag 必须是不可变版本 不要使用 latest
十五:values.schema.json
charts/harness-helm-demo/values.schema.json
{
"$schema": "https://json-schema.org/schema#",
"type": "object",
"required": ["replicaCount", "image", "service", "container"],
"properties": {
"replicaCount": {
"type": "integer",
"minimum": 1
},
"image": {
"type": "object",
"required": ["repository", "tag", "pullPolicy"],
"properties": {
"repository": {
"type": "string",
"minLength": 1
},
"tag": {
"type": "string",
"minLength": 1
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"]
}
}
},
"service": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"port": {
"type": "integer"
},
"targetPort": {
"type": "integer"
}
}
}
}
}
作用:
提前校验 values 格式 防止 replicaCount 写成字符串 防止 image.tag 为空 防止 pullPolicy 写错
十六:_helpers.tpl
charts/harness-helm-demo/templates/_helpers.tpl
{{/*
Expand the name of the chart.
*/}}
{{- define "harness-helm-demo.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "harness-helm-demo.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Common labels.
*/}}
{{- define "harness-helm-demo.labels" -}}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
app.kubernetes.io/name: {{ include "harness-helm-demo.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{/*
Selector labels.
*/}}
{{- define "harness-helm-demo.selectorLabels" -}}
app.kubernetes.io/name: {{ include "harness-helm-demo.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{/*
Service account name.
*/}}
{{- define "harness-helm-demo.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{- default (include "harness-helm-demo.fullname" .) .Values.serviceAccount.name -}}
{{- else -}}
{{- default "default" .Values.serviceAccount.name -}}
{{- end -}}
{{- end -}}
十七:Deployment 模板
charts/harness-helm-demo/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "harness-helm-demo.fullname" . }}
labels:
{{- include "harness-helm-demo.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
revisionHistoryLimit: 5
selector:
matchLabels:
{{- include "harness-helm-demo.selectorLabels" . | nindent 6 }}
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
{{- include "harness-helm-demo.selectorLabels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
serviceAccountName: {{ include "harness-helm-demo.serviceAccountName" . }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ include "harness-helm-demo.name" . }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.container.port }}
protocol: TCP
env:
- name: APP_NAME
value: {{ .Values.env.APP_NAME | quote }}
- name: APP_ENV
value: {{ .Values.env.APP_ENV | quote }}
- name: APP_VERSION
value: {{ .Values.env.APP_VERSION | quote }}
- name: LOG_LEVEL
value: {{ .Values.env.LOG_LEVEL | quote }}
{{- if .Values.readinessProbe.enabled }}
readinessProbe:
httpGet:
path: {{ .Values.readinessProbe.path }}
port: http
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
{{- end }}
{{- if .Values.livenessProbe.enabled }}
livenessProbe:
httpGet:
path: {{ .Values.livenessProbe.path }}
port: http
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
说明:
checksum/config 用于 ConfigMap 变化后触发 Pod 滚动更新。 readinessProbe 控制服务是否接流量。 livenessProbe 控制容器异常时自动重启。 maxUnavailable=0 可以降低滚动升级中断风险。
十八:Service 模板
charts/harness-helm-demo/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "harness-helm-demo.fullname" . }}
labels:
{{- include "harness-helm-demo.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
selector:
{{- include "harness-helm-demo.selectorLabels" . | nindent 4 }}
ports:
- name: http
port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
十九:ConfigMap 模板
charts/harness-helm-demo/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "harness-helm-demo.fullname" . }}-config
labels:
{{- include "harness-helm-demo.labels" . | nindent 4 }}
data:
APP_NAME: {{ .Values.env.APP_NAME | quote }}
APP_ENV: {{ .Values.env.APP_ENV | quote }}
APP_VERSION: {{ .Values.env.APP_VERSION | quote }}
LOG_LEVEL: {{ .Values.env.LOG_LEVEL | quote }}
二十:Ingress 模板
charts/harness-helm-demo/templates/ingress.yaml
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "harness-helm-demo.fullname" . }}
labels:
{{- include "harness-helm-demo.labels" . | nindent 4 }}
spec:
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.ingress.tls.enabled }}
tls:
- hosts:
- {{ .Values.ingress.host }}
secretName: {{ .Values.ingress.tls.secretName }}
{{- end }}
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
- path: {{ .Values.ingress.path }}
pathType: Prefix
backend:
service:
name: {{ include "harness-helm-demo.fullname" . }}
port:
number: {{ .Values.service.port }}
{{- end }}
二十一:HPA 模板
charts/harness-helm-demo/templates/hpa.yaml
{{- if .Values.autoscaling.enabled -}}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "harness-helm-demo.fullname" . }}
labels:
{{- include "harness-helm-demo.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "harness-helm-demo.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
二十二:ServiceAccount 模板
charts/harness-helm-demo/templates/serviceaccount.yaml
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "harness-helm-demo.serviceAccountName" . }}
labels:
{{- include "harness-helm-demo.labels" . | nindent 4 }}
{{- end }}
二十三:NOTES.txt
charts/harness-helm-demo/templates/NOTES.txt
Release: {{ .Release.Name }} Namespace: {{ .Release.Namespace }} Chart: {{ .Chart.Name }}-{{ .Chart.Version }} Image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
To check pods: kubectl get pods -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }}
To port-forward: kubectl port-forward svc/{{ include "harness-helm-demo.fullname" . }} 8080:{{ .Values.service.port }} -n {{ .Release.Namespace }}
Health check: curl http://127.0.0.1:8080{{ .Values.readinessProbe.path }}
二十四:本地验证 Helm Chart
1. helm lint
scripts/helm-lint.sh
#!/usr/bin/env bash
set -euo pipefail
CHART_DIR="${CHART_DIR:-charts/harness-helm-demo}"
VALUES_FILE="${VALUES_FILE:-values-dev.yaml}"
echo "CHART_DIR=${CHART_DIR}"
echo "VALUES_FILE=${VALUES_FILE}"
helm lint "${CHART_DIR}" -f "${CHART_DIR}/${VALUES_FILE}"
echo "helm lint 通过"
运行:
chmod +x scripts/helm-lint.sh
CHART_DIR=charts/harness-helm-demo \
VALUES_FILE=values-dev.yaml \
scripts/helm-lint.sh
2. helm template
scripts/helm-template.sh
#!/usr/bin/env bash
set -euo pipefail
CHART_DIR="${CHART_DIR:-charts/harness-helm-demo}"
VALUES_FILE="${VALUES_FILE:-values-dev.yaml}"
RELEASE_NAME="${RELEASE_NAME:-harness-helm-demo}"
NAMESPACE="${NAMESPACE:-dev}"
IMAGE_TAG="${IMAGE_TAG:-dev}"
echo "渲染 Helm 模板"
echo "CHART_DIR=${CHART_DIR}"
echo "VALUES_FILE=${VALUES_FILE}"
echo "RELEASE_NAME=${RELEASE_NAME}"
echo "NAMESPACE=${NAMESPACE}"
echo "IMAGE_TAG=${IMAGE_TAG}"
helm template "${RELEASE_NAME}" "${CHART_DIR}" \
--namespace "${NAMESPACE}" \
-f "${CHART_DIR}/${VALUES_FILE}" \
--set image.tag="${IMAGE_TAG}" \
--set env.APP_VERSION="${IMAGE_TAG}" \
> /tmp/${RELEASE_NAME}-manifest.yaml
echo "渲染结果:/tmp/${RELEASE_NAME}-manifest.yaml"
kubectl apply --dry-run=client -f /tmp/${RELEASE_NAME}-manifest.yaml
echo "helm template 和 kubectl dry-run 通过"
运行:
chmod +x scripts/helm-template.sh
NAMESPACE=dev \
VALUES_FILE=values-dev.yaml \
IMAGE_TAG=dev-main-a1b2c3d-100 \
scripts/helm-template.sh
二十五:打包 Helm Chart
scripts/helm-package.sh
#!/usr/bin/env bash
set -euo pipefail
CHART_DIR="${CHART_DIR:-charts/harness-helm-demo}"
PACKAGE_DIR="${PACKAGE_DIR:-dist/charts}"
mkdir -p "${PACKAGE_DIR}"
helm dependency update "${CHART_DIR}" || true
helm lint "${CHART_DIR}"
helm package "${CHART_DIR}" --destination "${PACKAGE_DIR}"
ls -lh "${PACKAGE_DIR}"
echo "Chart 打包完成"
运行:
chmod +x scripts/helm-package.sh
scripts/helm-package.sh
输出示例:
dist/charts/harness-helm-demo-0.1.0.tgz
二十六:Chart 仓库选择
Helm Chart 可以存在多种地方:
Git 仓库 HTTP Helm Repo Harbor OCI Registry Nexus Helm Repository Artifactory 云厂商 Artifact Registry
国内企业推荐:
开发初期: Chart 放在 Git 仓库 charts/ 目录
团队标准化: Chart 打包后推送到 Harbor OCI
企业平台化: Harbor OCI / Nexus / Artifactory 统一管理 Chart
本文建议:
源码仓库中保留 Chart 源文件 发布时打包成 tgz 推送到 Harbor OCI Harness 从 Git 或 OCI 拉取 Chart
二十七:推送 Helm Chart 到 Harbor OCI
Harbor 2.x 支持 OCI Artifact,可以存储 Helm Chart。
登录:
helm registry login harbor.company.com \
--username robot$helm \
--password "${HARBOR_ROBOT_TOKEN}"
推送:
scripts/helm-push-oci.sh
#!/usr/bin/env bash
set -euo pipefail
CHART_PACKAGE="${CHART_PACKAGE:-dist/charts/harness-helm-demo-0.1.0.tgz}"
OCI_REPO="${OCI_REPO:-oci://harbor.company.com/helm-charts}"
echo "CHART_PACKAGE=${CHART_PACKAGE}"
echo "OCI_REPO=${OCI_REPO}"
helm push "${CHART_PACKAGE}" "${OCI_REPO}"
echo "Chart 已推送到 ${OCI_REPO}"
运行:
HARBOR_ROBOT_TOKEN=xxxx \
helm registry login harbor.company.com \
--username 'robot$helm' \
--password-stdin
CHART_PACKAGE=dist/charts/harness-helm-demo-0.1.0.tgz \
OCI_REPO=oci://harbor.company.com/helm-charts \
scripts/helm-push-oci.sh
注意:
Harbor 项目需要允许 OCI artifact。 Robot Account 需要 push/pull 权限。 Harness Delegate 需要能访问 Harbor。 如果 Harbor 使用自签证书,Delegate 容器或节点要信任企业 CA。
二十八:Kubernetes RBAC 准备
创建 namespace:
kubectl create namespace dev
kubectl create namespace test
kubectl create namespace prod
创建 Harbor 镜像拉取 Secret:
kubectl create secret docker-registry harbor-pull-secret \
--docker-server=harbor.company.com \
--docker-username='robot$app' \
--docker-password='YOUR_TOKEN' \
-n dev
kubectl create secret docker-registry harbor-pull-secret \
--docker-server=harbor.company.com \
--docker-username='robot$app' \
--docker-password='YOUR_TOKEN' \
-n test
kubectl create secret docker-registry harbor-pull-secret \
--docker-server=harbor.company.com \
--docker-username='robot$app' \
--docker-password='YOUR_TOKEN' \
-n prod
如果希望最小权限,可以为 Harness Delegate 使用 namespace 级 ServiceAccount。
k8s/harness-deployer-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: harness-deployer
namespace: dev
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: harness-deployer
namespace: dev
rules:
- apiGroups: ["", "apps", "networking.k8s.io", "autoscaling", "batch"]
resources:
- pods
- pods/log
- services
- endpoints
- configmaps
- secrets
- deployments
- replicasets
- ingresses
- horizontalpodautoscalers
- jobs
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: harness-deployer
namespace: dev
subjects:
- kind: ServiceAccount
name: harness-deployer
namespace: dev
roleRef:
kind: Role
name: harness-deployer
apiGroup: rbac.authorization.k8s.io
test/prod 可以复制后修改 namespace。
二十九:Harness 中需要准备的资源
1. Git Connector
用于拉取:
应用代码 Dockerfile Helm Chart 源文件 values 文件
国内建议:
GitLab Gitee 企业自建 Git
2. Docker Registry Connector
用于推送镜像:
harbor.company.com/devops/harness-helm-demo
3. OCI Helm Registry Connector 或 HTTP Helm Connector
用于拉取 Chart:
oci://harbor.company.com/helm-charts
或者:
4. Kubernetes Connector
用于部署到目标集群:
dev cluster test cluster prod cluster
5. Secret
harbor_robot_token helm_robot_token wecom_webhook_url
三十:Harness Native Helm Service 配置思路
在 Harness 中创建 Service:
Service Name: harness-helm-demo
Deployment Type: Native Helm
Manifest 配置:
Manifest Type: Helm Chart
Chart Store: Git / OCI Helm / HTTP Helm
Chart Name: harness-helm-demo
Chart Version: <+input> 或 0.1.0
Helm Version: V3
Values YAML: values.yaml values-<+env.name>.yaml
推荐 values 文件路径:
values.yaml values-<+env.name>.yaml
这样:
env.name = dev → values-dev.yaml env.name = test → values-test.yaml env.name = prod → values-prod.yaml
也可以使用 Input Set:
values_file = values-dev.yaml values_file = values-test.yaml values_file = values-prod.yaml
三十一:Harness Service YAML 参考
下面是参考结构,实际字段以 Harness UI 导出的 YAML 为准。
service:
name: harness-helm-demo
identifier: harness_helm_demo
orgIdentifier: devops_lab
projectIdentifier: harness_demo
serviceDefinition:
type: NativeHelm
spec:
manifests:
- manifest:
identifier: helm_chart
type: HelmChart
spec:
store:
type: Github
spec:
connectorRef: gitlab_company
gitFetchType: Branch
branch: main
folderPath: charts/harness-helm-demo
chartName: harness-helm-demo
chartVersion: <+input>
helmVersion: V3
valuesPaths:
- values.yaml
- values-<+env.name>.yaml
fetchHelmChartMetadata: true
如果使用 Harbor OCI:
service:
name: harness-helm-demo
identifier: harness_helm_demo
orgIdentifier: devops_lab
projectIdentifier: harness_demo
serviceDefinition:
type: NativeHelm
spec:
manifests:
- manifest:
identifier: helm_chart
type: HelmChart
spec:
store:
type: OciHelmChart
spec:
connectorRef: harbor_oci_helm
basePath: helm-charts
chartName: harness-helm-demo
chartVersion: <+input>
helmVersion: V3
valuesPaths:
- values.yaml
- values-<+env.name>.yaml
fetchHelmChartMetadata: true
注意:
不同 Harness 版本对 OCI Helm 字段名可能略有差异。 建议先在 UI 中配置成功,再切换 YAML 查看真实字段。
三十二:Pipeline 变量设计
variables:
- name: app_name
type: String
value: harness-helm-demo
- name: deploy_env
type: String
value: <+input>
- name: namespace
type: String
value: <+input>
- name: release_name
type: String
value: <+input>
- name: chart_version
type: String
value: <+input>
- name: image_repository
type: String
value: harbor.company.com/devops/harness-helm-demo
- name: image_tag
type: String
value: <+input>
- name: values_file
type: String
value: <+input>
- name: rollback_revision
type: String
value: <+input>
三十三:dev Input Set
harness/inputsets/dev.yaml
inputSet:
name: helm-dev
identifier: helm_dev
orgIdentifier: devops_lab
projectIdentifier: harness_demo
pipeline:
identifier: helm_deploy_demo
variables:
- name: deploy_env
type: String
value: dev
- name: namespace
type: String
value: dev
- name: release_name
type: String
value: harness-helm-demo-dev
- name: chart_version
type: String
value: 0.1.0
- name: image_tag
type: String
value: <+input>
- name: values_file
type: String
value: values-dev.yaml
- name: rollback_revision
type: String
value: "0"
三十四:test Input Set
inputSet:
name: helm-test
identifier: helm_test
orgIdentifier: devops_lab
projectIdentifier: harness_demo
pipeline:
identifier: helm_deploy_demo
variables:
- name: deploy_env
type: String
value: test
- name: namespace
type: String
value: test
- name: release_name
type: String
value: harness-helm-demo-test
- name: chart_version
type: String
value: 0.1.0
- name: image_tag
type: String
value: <+input>
- name: values_file
type: String
value: values-test.yaml
- name: rollback_revision
type: String
value: "0"
三十五:prod Input Set
inputSet:
name: helm-prod
identifier: helm_prod
orgIdentifier: devops_lab
projectIdentifier: harness_demo
pipeline:
identifier: helm_deploy_demo
variables:
- name: deploy_env
type: String
value: prod
- name: namespace
type: String
value: prod
- name: release_name
type: String
value: harness-helm-demo-prod
- name: chart_version
type: String
value: <+input>
- name: image_tag
type: String
value: <+input>
- name: values_file
type: String
value: values-prod.yaml
- name: rollback_revision
type: String
value: "0"
生产规则:
chart_version 必须明确输入 image_tag 必须明确输入 禁止 latest 必须审批 必须保留 helm history
三十六:HelmDeploy Pipeline YAML 参考
下面是参考结构。Harness YAML 字段会随版本和部署类型略有差异,建议先使用 Visual Editor 创建成功,再切换 YAML 保存为标准模板。
pipeline:
name: helm-deploy-demo
identifier: helm_deploy_demo
orgIdentifier: devops_lab
projectIdentifier: harness_demo
tags:
tutorial: harness-16
type: helm
variables:
- name: app_name
type: String
value: harness-helm-demo
- name: deploy_env
type: String
value: <+input>
- name: namespace
type: String
value: <+input>
- name: release_name
type: String
value: <+input>
- name: chart_version
type: String
value: <+input>
- name: image_repository
type: String
value: harbor.company.com/devops/harness-helm-demo
- name: image_tag
type: String
value: <+input>
- name: values_file
type: String
value: <+input>
- name: rollback_revision
type: String
value: <+input>
stages:
- stage:
name: CI Build
identifier: ci_build
type: CI
spec:
cloneCodebase: true
infrastructure:
type: KubernetesDirect
spec:
connectorRef: k8s_dev_cluster
namespace: harness-ci
automountServiceAccountToken: true
os: Linux
execution:
steps:
- step:
name: Unit Test
identifier: unit_test
type: Run
spec:
connectorRef: harbor_company
image: harbor.company.com/library/node:20-alpine
shell: Sh
command: |
set -e
cd app
npm config set registry https://registry.npmmirror.com
npm install
npm test
- step:
name: Build And Push Image
identifier: build_and_push_image
type: BuildAndPushDockerRegistry
spec:
connectorRef: harbor_company
repo: harbor.company.com/devops/harness-helm-demo
dockerfile: docker/Dockerfile
context: .
tags:
- <+pipeline.variables.image_tag>
- stage:
name: Helm Chart Check
identifier: helm_chart_check
type: CI
spec:
cloneCodebase: true
infrastructure:
type: KubernetesDirect
spec:
connectorRef: k8s_dev_cluster
namespace: harness-ci
automountServiceAccountToken: true
os: Linux
execution:
steps:
- step:
name: Helm Lint
identifier: helm_lint
type: Run
spec:
connectorRef: harbor_company
image: harbor.company.com/library/helm-kubectl:3.15
shell: Sh
envVariables:
CHART_DIR: charts/harness-helm-demo
VALUES_FILE: <+pipeline.variables.values_file>
command: |
set -e
chmod +x scripts/helm-lint.sh
scripts/helm-lint.sh
- step:
name: Helm Template Dry Run
identifier: helm_template_dry_run
type: Run
spec:
connectorRef: harbor_company
image: harbor.company.com/library/helm-kubectl:3.15
shell: Sh
envVariables:
CHART_DIR: charts/harness-helm-demo
VALUES_FILE: <+pipeline.variables.values_file>
RELEASE_NAME: <+pipeline.variables.release_name>
NAMESPACE: <+pipeline.variables.namespace>
IMAGE_TAG: <+pipeline.variables.image_tag>
command: |
set -e
chmod +x scripts/helm-template.sh
scripts/helm-template.sh
- stage:
name: Helm Deploy
identifier: helm_deploy
type: Deployment
spec:
deploymentType: NativeHelm
service:
serviceRef: harness_helm_demo
serviceInputs:
serviceDefinition:
type: NativeHelm
spec:
manifests:
- manifest:
identifier: helm_chart
type: HelmChart
spec:
chartVersion: <+pipeline.variables.chart_version>
environment:
environmentRef: <+pipeline.variables.deploy_env>
deployToAll: false
infrastructureDefinitions:
- identifier: <+pipeline.variables.deploy_env>_helm
execution:
steps:
- step:
name: Helm Deploy
identifier: helm_deploy_step
type: HelmDeploy
timeout: 10m
spec:
skipDryRun: false
commandFlags:
- commandType: Upgrade
flag: "--atomic --wait --timeout 5m --set image.tag=<+pipeline.variables.image_tag> --set env.APP_VERSION=<+pipeline.variables.image_tag>"
- step:
name: Helm Release Status
identifier: helm_release_status
type: ShellScript
timeout: 3m
spec:
shell: Bash
onDelegate: true
source:
type: Inline
spec:
script: |
set -e
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
helm status <+pipeline.variables.release_name> -n <+pipeline.variables.namespace>
helm history <+pipeline.variables.release_name> -n <+pipeline.variables.namespace>
kubectl get pods -n <+pipeline.variables.namespace> -l app.kubernetes.io/instance=<+pipeline.variables.release_name> -o wide
- step:
name: Health Check
identifier: health_check
type: ShellScript
timeout: 5m
spec:
shell: Bash
onDelegate: true
source:
type: Inline
spec:
script: |
set -e
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
RELEASE_NAME="<+pipeline.variables.release_name>"
NAMESPACE="<+pipeline.variables.namespace>"
SERVICE_NAME=$(kubectl get svc -n ${NAMESPACE} -l app.kubernetes.io/instance=${RELEASE_NAME} -o jsonpath='{.items[0].metadata.name}')
kubectl rollout status deployment -n ${NAMESPACE} -l app.kubernetes.io/instance=${RELEASE_NAME} --timeout=180s
kubectl port-forward svc/${SERVICE_NAME} 18080:80 -n ${NAMESPACE} >/tmp/${SERVICE_NAME}-pf.log 2>&1 &
PF_PID=$!
sleep 5
curl -f http://127.0.0.1:18080/health
kill ${PF_PID}
echo "Helm 部署健康检查通过"
rollbackSteps:
- step:
name: Helm Rollback
identifier: helm_rollback
type: HelmRollback
timeout: 10m
spec: {}
failureStrategies:
- onFailure:
errors:
- AllErrors
action:
type: StageRollback
注意:
HelmDeploy、HelmRollback 的 YAML 字段以当前 Harness UI 导出为准。 如果你的 Harness 版本不支持 commandFlags 写法,可以在 Helm Service Manifest 的高级配置中添加 Helm Command Flags。 Native Helm 的回滚依赖 Helm release history。
三十七:为什么建议加 --atomic --wait --timeout
--wait:
等待 Pod、PVC、Service、Ingress 等资源达到 Ready 状态。
--timeout 5m:
限制等待时间,避免流水线无限卡住。
--atomic:
升级失败时自动回滚到上一个成功版本。
建议:
dev/test 可以使用 --wait --timeout pre/prod 建议使用 --atomic --wait --timeout
三十八:部署后检查脚本
scripts/helm-release-status.sh
#!/usr/bin/env bash
set -euo pipefail
RELEASE_NAME="${RELEASE_NAME:?RELEASE_NAME is required}"
NAMESPACE="${NAMESPACE:?NAMESPACE is required}"
echo "===== helm status ====="
helm status "${RELEASE_NAME}" -n "${NAMESPACE}"
echo "===== helm history ====="
helm history "${RELEASE_NAME}" -n "${NAMESPACE}"
echo "===== helm get values ====="
helm get values "${RELEASE_NAME}" -n "${NAMESPACE}"
echo "===== kubectl resources ====="
kubectl get all -n "${NAMESPACE}" -l app.kubernetes.io/instance="${RELEASE_NAME}" -o wide
三十九:健康检查脚本
scripts/k8s-health-check.sh
#!/usr/bin/env bash
set -euo pipefail
RELEASE_NAME="${RELEASE_NAME:?RELEASE_NAME is required}"
NAMESPACE="${NAMESPACE:?NAMESPACE is required}"
HEALTH_PATH="${HEALTH_PATH:-/health}"
LOCAL_PORT="${LOCAL_PORT:-18080}"
echo "RELEASE_NAME=${RELEASE_NAME}"
echo "NAMESPACE=${NAMESPACE}"
echo "HEALTH_PATH=${HEALTH_PATH}"
kubectl rollout status deployment \
-n "${NAMESPACE}" \
-l app.kubernetes.io/instance="${RELEASE_NAME}" \
--timeout=180s
SERVICE_NAME=$(kubectl get svc -n "${NAMESPACE}" \
-l app.kubernetes.io/instance="${RELEASE_NAME}" \
-o jsonpath='{.items[0].metadata.name}')
echo "SERVICE_NAME=${SERVICE_NAME}"
kubectl get endpoints "${SERVICE_NAME}" -n "${NAMESPACE}"
kubectl port-forward svc/${SERVICE_NAME} ${LOCAL_PORT}:80 -n "${NAMESPACE}" >/tmp/${SERVICE_NAME}-pf.log 2>&1 &
PF_PID=$!
sleep 5
curl -f "http://127.0.0.1:${LOCAL_PORT}${HEALTH_PATH}"
kill ${PF_PID}
echo "健康检查通过"
四十:手动回滚脚本
scripts/helm-rollback.sh
#!/usr/bin/env bash
set -euo pipefail
RELEASE_NAME="${RELEASE_NAME:?RELEASE_NAME is required}"
NAMESPACE="${NAMESPACE:?NAMESPACE is required}"
REVISION="${REVISION:-0}"
TIMEOUT="${TIMEOUT:-5m}"
echo "RELEASE_NAME=${RELEASE_NAME}"
echo "NAMESPACE=${NAMESPACE}"
echo "REVISION=${REVISION}"
echo "===== 当前历史 ====="
helm history "${RELEASE_NAME}" -n "${NAMESPACE}"
if [ "${REVISION}" = "0" ]; then
echo "回滚到上一个 revision"
helm rollback "${RELEASE_NAME}" -n "${NAMESPACE}" --wait --timeout "${TIMEOUT}"
else
echo "回滚到指定 revision:${REVISION}"
helm rollback "${RELEASE_NAME}" "${REVISION}" -n "${NAMESPACE}" --wait --timeout "${TIMEOUT}"
fi
echo "===== 回滚后状态 ====="
helm status "${RELEASE_NAME}" -n "${NAMESPACE}"
helm history "${RELEASE_NAME}" -n "${NAMESPACE}"
kubectl get pods -n "${NAMESPACE}" -l app.kubernetes.io/instance="${RELEASE_NAME}" -o wide
echo "Helm 回滚完成"
运行示例:
RELEASE_NAME=harness-helm-demo-dev \
NAMESPACE=dev \
REVISION=1 \
scripts/helm-rollback.sh
四十一:修改 values 并重新发布
比如把 dev 副本数从 1 改成 2:
values-dev.yaml
replicaCount: 2
然后重新运行 Pipeline。
验证:
helm history harness-helm-demo-dev -n dev
helm get values harness-helm-demo-dev -n dev
kubectl get deploy -n dev -l app.kubernetes.io/instance=harness-helm-demo-dev
kubectl get pods -n dev -l app.kubernetes.io/instance=harness-helm-demo-dev
你应该看到:
Helm revision 增加 Deployment replicas 变为 2 Pod 数量变为 2
四十二:回滚旧版本验证
查看历史:
helm history harness-helm-demo-dev -n dev
示例输出:
REVISION UPDATED STATUS CHART APP VERSION 1 2026-07-08 10:00:00 superseded harness-helm-demo-0.1.0 1.0.0 2 2026-07-08 10:20:00 deployed harness-helm-demo-0.1.0 1.0.0
回滚到 revision 1:
helm rollback harness-helm-demo-dev 1 -n dev --wait --timeout 5m
验证:
helm history harness-helm-demo-dev -n dev
helm get values harness-helm-demo-dev -n dev
kubectl get deploy -n dev -l app.kubernetes.io/instance=harness-helm-demo-dev
预期:
replicaCount 回到旧版本 Helm revision 继续增加 当前 deployed revision 是回滚后生成的新 revision
四十三:生产 Helm 发布规范
生产环境建议:
Chart version 必须固定 image tag 必须固定 禁止 latest 必须开启 --atomic 必须开启 --wait 必须设置 --timeout 必须保留 helm history 必须审批 必须部署后健康检查 必须有回滚方案
生产发布输入:
chart_version = 0.1.5 image_tag = v1.2.0 release_name = harness-helm-demo-prod namespace = prod values_file = values-prod.yaml
不要这样:
chart_version = latest image_tag = latest namespace = default release_name = demo
四十四:Helm 与 Secret 管理
不要把敏感信息直接写进 values.yaml:
database:
password: "123456"
推荐:
数据库密码放 Kubernetes Secret Secret 由外部系统创建 Helm Chart 只引用 Secret name
values:
database:
existingSecret: harness-helm-demo-db
usernameKey: username
passwordKey: password
模板:
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: {{ .Values.database.existingSecret }}
key: {{ .Values.database.usernameKey }}
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.database.existingSecret }}
key: {{ .Values.database.passwordKey }}
企业建议:
Secret 由 External Secrets / Sealed Secrets / Vault / 云 KMS 管理 Helm Chart 不保存明文密钥 Harness Secret 只保存访问仓库、镜像仓库、通知机器人的凭证
四十五:国内网络环境注意事项
1. Chart 仓库访问
常见问题:
Delegate 无法访问 GitLab / Harbor / Nexus 自签证书不被信任 HTTP Helm Repo 需要认证 OCI Helm 登录失败
解决:
Delegate 所在网络必须能访问 Chart 仓库 给 Delegate 配置企业 CA Harbor Robot Account 使用最小权限 不要让 Harness SaaS 直接访问内网仓库,应通过 Delegate
2. Helm 依赖更新
如果 Chart 有 dependencies:
dependencies:
- name: redis
version: 18.0.0
repository: https://charts.bitnami.com/bitnami
国内生产不建议直接访问公网:
把依赖 Chart 同步到企业内部 Helm 仓库 或把 tgz 放入 charts/ 目录 或使用 Harbor/Nexus 做统一代理缓存
3. 基础镜像
不要写:
docker.io/library/node:20-alpine
推荐:
harbor.company.com/library/node:20-alpine
4. values 文件权限
生产 values 文件应严格控制:
只能平台团队 / SRE / 技术负责人修改 必须走 Merge Request 修改 replicas、resources、ingress、autoscaling 必须审核
四十六:常见问题排查
1. HelmDeploy 失败:找不到 Chart
可能原因:
chartName 写错 chartVersion 不存在 OCI basePath 错误 Git folderPath 错误 Delegate 无法访问 Chart 仓库 Harbor 权限不足
排查:
helm pull oci://harbor.company.com/helm-charts/harness-helm-demo --version 0.1.0
helm show chart oci://harbor.company.com/helm-charts/harness-helm-demo --version 0.1.0
2. values 文件没有生效
可能原因:
values 文件路径写错 values 文件不在 Chart 内 多个 values 文件覆盖顺序不清楚 Harness 表达式没有解析出正确文件名
排查:
helm template harness-helm-demo charts/harness-helm-demo \
-f charts/harness-helm-demo/values.yaml \
-f charts/harness-helm-demo/values-dev.yaml
注意:
多个 values 文件后面的优先级更高。
3. 镜像拉取失败
可能原因:
image.repository 或 image.tag 错误 Harbor Secret 不存在 imagePullSecrets namespace 不一致 节点无法访问 Harbor Harbor 自签证书不被节点信任
排查:
kubectl describe pod POD_NAME -n dev
kubectl get secret harbor-pull-secret -n dev
kubectl get events -n dev --sort-by=.metadata.creationTimestamp
4. Helm release 状态 FAILED
排查:
helm status RELEASE_NAME -n NAMESPACE
helm history RELEASE_NAME -n NAMESPACE
helm get manifest RELEASE_NAME -n NAMESPACE
kubectl get events -n NAMESPACE --sort-by=.metadata.creationTimestamp
5. 回滚失败
可能原因:
release history 不存在 目标 revision 不存在 资源被手工修改导致冲突 Hook Job 卡住 RBAC 权限不足
排查:
helm history RELEASE_NAME -n NAMESPACE
helm rollback RELEASE_NAME REVISION -n NAMESPACE --debug --wait --timeout 10m
四十七:企业最佳实践
1. Chart 与镜像分开版本
Chart version 管 Kubernetes 模板变化 Image tag 管应用代码变化
例如:
Chart version = 0.3.0 Image tag = v1.8.2
2. dev/test/prod 使用不同 values 文件
推荐:
values.yaml values-dev.yaml values-test.yaml values-prod.yaml
3. 生产必须固定版本
生产不要使用:
latest main dev
生产使用:
v1.2.0 release-20260708-001
4. 发布前必须 helm lint/template
helm lint helm template kubectl dry-run kubeconform 可选
5. Helm 回滚前要看 history
helm history RELEASE_NAME -n NAMESPACE
不要盲目回滚。
6. Harness YAML 不要手写到底
推荐:
先用 Harness Visual Editor 创建 Native Helm Pipeline 跑通后切换 YAML 再纳入 Git 管理
7. 不要手工改集群资源
如果资源由 Helm 管理,不建议直接:
kubectl edit deployment
kubectl patch service
否则会造成:
Helm 状态和实际资源漂移 下次 upgrade 覆盖手工修改 回滚结果不可预测
四十八:练习 1:创建 Helm Chart 并本地渲染
目标:
创建 charts/harness-helm-demo,并能用 values-dev.yaml 渲染出 Kubernetes YAML。
步骤:
- 创建 Chart.yaml
- 创建 values.yaml
- 创建 deployment/service/configmap 模板
- 执行 helm lint
- 执行 helm template
验收:
helm lint 通过 helm template 输出 Deployment、Service、ConfigMap kubectl dry-run 通过
四十九:练习 2:Harness HelmDeploy 发布 dev 环境
目标:
使用 Harness Native Helm + HelmDeploy 发布到 dev namespace。
步骤:
- 创建 Native Helm Service
- 添加 Helm Chart Manifest
- 配置 dev Environment 和 Infrastructure
- 创建 dev Input Set
- 运行 Pipeline
验收:
helm list -n dev 能看到 release Pod Running /health 返回 200
五十:练习 3:修改 Chart 值并升级
目标:
把 dev replicaCount 从 1 改成 2,重新发布。
步骤:
- 修改 values-dev.yaml
- 提交代码
- 运行 Pipeline
- 查看 helm history
- 查看 Deployment replicas
验收:
Helm revision 增加 Pod 数变为 2 服务健康检查通过
五十一:练习 4:回滚旧版本
目标:
将 release 回滚到上一个 revision。
步骤:
- helm history 查看 revision
- 使用 Helm Rollback Step 或 helm rollback
- 查看 helm status
- 查看 Pod 和应用版本
验收:
回滚后应用恢复到旧配置 helm history 出现新 revision 健康检查通过
五十二:练习 5:发布生产环境
目标:
使用 prod Input Set 发布固定 Chart version 和 image tag。
要求:
chart_version = 0.1.0 image_tag = v1.0.0 namespace = prod release_name = harness-helm-demo-prod values_file = values-prod.yaml
验收:
生产发布前有审批 禁止 latest HelmDeploy 使用 --atomic --wait --timeout 发布后健康检查通过 helm history 可追溯
五十三:验收标准
完成本文后,应达到:
已理解 Harness-managed Helm 与 Native Helm 的区别 已能编写标准 Helm Chart 已能编写 values-dev/test/prod 已能使用 helm lint 检查 Chart 已能使用 helm template 渲染并 dry-run 已能打包 Chart 已能推送 Chart 到 Harbor OCI 已能在 Harness 中创建 Native Helm Service 已能使用 HelmDeploy 发布应用 已能部署后查看 helm status 和 helm history 已能修改 values 并升级 release 已能使用 Helm Rollback 回滚旧版本 已能处理国内环境下 Harbor、Chart 仓库、自签证书、Delegate 访问问题
五十四:本篇总结
本篇完成了 Kubernetes + Helm 在 Harness 中的完整部署示例。
你需要重点记住:
Helm Chart 是 Kubernetes 应用的标准化包 values.yaml 管默认值,values-dev/test/prod 管环境差异 Chart version 和 image tag 要分开管理 Harness-managed Helm 使用 helm template + kubectl apply Native Helm 使用 helm install/upgrade 和 helm rollback 如果依赖 Helm hooks/subcharts/release history,优先 Native Helm 发布前必须 helm lint 和 helm template 生产环境必须固定 chart version 和 image tag 生产不要使用 latest 回滚前先看 helm history 国内环境优先使用 Harbor OCI / Nexus / 企业 Git 作为 Chart 仓库 Harness Delegate 必须能访问 Git、Harbor、Kubernetes API
更多推荐




所有评论(0)