一:教程定位
在前几篇中,我们已经完成了 Harness 平台搭建、CI/CD 流水线、代码触发器、测试与制品发布。
第 5 篇开始聚焦 CD 部署阶段:如何把已经构建好的镜像部署到 Kubernetes 集群。
本篇面向初学者,目标是让你掌握两种 Kubernetes 部署方式:
方式一:使用 Harness 内置 Kubernetes Rolling Deploy 步骤 方式二:使用 Shell Script 执行 kubectl apply
企业落地推荐优先使用 Harness 内置 Kubernetes 部署能力,因为它能更好地接入 Harness 的 Service、Environment、Infrastructure、Artifact、Rollback、Execution History 等体系。
kubectl apply 方式适合:
初学者理解 Kubernetes 部署原理 临时调试 迁移已有脚本 部分特殊资源部署 企业内部已有 kubectl 脚本体系
本文使用国内网络环境:
GitLab / Gitee Harbor / 阿里云 ACR / 腾讯云 TCR / 华为云 SWR 自建 K8s / ACK / TKE / CCE Harness Delegate 企业代理 / 防火墙 / 自签证书
二:适合人群
本文适合:
DevOps 初学者 Kubernetes 初学者 后端开发人员 测试工程师 运维工程师 正在把 Jenkins / GitLab CI 部署脚本迁移到 Harness 的团队
建议已经具备:
理解 Git 基础操作 知道 Docker 镜像是什么 知道 Kubernetes Deployment 和 Service 的作用 已经能把镜像推送到 Harbor 已经安装 Harness Delegate 已经创建 GitLab / Harbor Connector
三:学习目标
完成本文后,你应该能够:
创建 Kubernetes Connector 理解 Delegate 访问 Kubernetes 的方式 编写 Deployment、Service、ConfigMap 基础清单 配置 imagePullSecrets 拉取 Harbor 私有镜像 创建 Harness Service 配置 Harness Environment 和 Infrastructure Definition 使用 Harness K8s Rolling Deploy 部署应用 使用 kubectl apply 部署应用 验证 Pod、Deployment、Service 状态 观察 Kubernetes 滚动升级 修改副本数并验证扩缩容 修改镜像版本并验证滚动发布 排查 ImagePullBackOff、CrashLoopBackOff、Service 不通等常见问题
四:国内网络环境下的整体架构
本文部署架构如下:
开发人员 ↓ git push GitLab / Gitee ↓ Webhook Harness Pipeline ↓ CI Build Harness Delegate ↓ docker build / push Harbor ↓ CD Deploy Harness Delegate ↓ Kubernetes API 自建 K8s / ACK / TKE / CCE ↓ pull image Harbor
关键点:
Harness SaaS 不直接访问企业内网 Kubernetes Harness Delegate 部署在可访问 Kubernetes API 的网络中 Kubernetes 节点必须能访问 Harbor Harbor 私有镜像需要 imagePullSecrets Kubernetes Connector 推荐通过 Delegate 继承凭证 部署到 dev/test/pre/prod 时建议使用不同 Namespace 或不同集群
国内企业常见网络模型:
Harness SaaS ↑ 出站 HTTPS/WSS 企业 VPC / 内网 K8s ├── Harness Delegate ├── GitLab ├── Harbor ├── SonarQube └── Kubernetes API Server
如果企业不能直接访问 Harness SaaS,需要在 Delegate 上配置企业代理:
HTTP_PROXY=proxy.company.com:3128 HTTPS_PROXY=proxy.company.com:3128 NO_PROXY=localhost,127.0.0.1,.svc,.cluster.local,gitlab.company.com,harbor.company.com,kubernetes.default.svc
五:本篇最终效果
本篇完成后,流水线结构如下:
Pipeline: nodejs-k8s-deploy-dev
Stage 1: CI Build Step 1: npm install Step 2: npm test Step 3: docker build Step 4: docker push Harbor
Stage 2: Deploy Dev Step 1: Harness K8s Rolling Deploy Step 2: kubectl rollout status Step 3: Service 健康检查
Stage 3: Optional kubectl apply Step 1: kubectl apply -f k8s/ Step 2: kubectl rollout status
其中,Stage 2 是推荐方式,Stage 3 是学习和迁移脚本用的补充方式。
六:环境准备清单
1. Harness 资源
需要提前准备:
Org: devops-lab Project: harness-demo Delegate: cn-k8s-dev Git Connector: gitlab_company Docker Registry Connector: harbor_company Kubernetes Connector: k8s_dev_cluster
2. Git 仓库
示例:
目录结构:
harness-demo-app/
├── app/
│ ├── package.json
│ └── server.js
├── docker/
│ └── Dockerfile
├── k8s/
│ ├── templates/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── configmap.yaml
│ └── values-dev.yaml
└── scripts/
├── k8s-apply.sh
├── k8s-verify.sh
└── k8s-rollback.sh
3. Harbor
示例:
镜像地址:
harbor.company.com/devops/harness-demo-app:
需要准备:
Harbor 项目:devops Harbor Robot Account:robot$devops+harness-ci 权限:Pull Repository、Push Repository、List Repository
4. Kubernetes
示例环境:
dev namespace test namespace pre namespace prod namespace
本文使用:
dev
创建命名空间:
kubectl create namespace dev
创建 Harbor 拉取密钥:
kubectl create secret docker-registry harbor-pull-secret \
--docker-server=harbor.company.com \
--docker-username='robot$devops+harness-ci' \
--docker-password='替换为 Harbor Robot Token' \
--docker-email=devops@company.com \
-n dev
验证:
kubectl get secret harbor-pull-secret -n dev
七:Kubernetes Connector 配置
1. 推荐方式:Delegate 继承凭证
如果 Harness Delegate 部署在目标 Kubernetes 集群内,推荐 Kubernetes Connector 使用:
Use the credentials of a specific Harness Delegate
含义:
Harness 使用 Delegate Pod 所在集群的 ServiceAccount 权限访问 Kubernetes API 不需要把 kubeconfig 明文交给 Harness 适合企业内网 K8s、自建 K8s、ACK/TKE/CCE 内部集群
Kubernetes Connector YAML 参考:
connector:
name: k8s-dev-cluster
identifier: k8s_dev_cluster
description: 国内 dev Kubernetes 集群
orgIdentifier: devops_lab
projectIdentifier: harness_demo
type: K8sCluster
spec:
credential:
type: InheritFromDelegate
spec:
delegateSelectors:
- cn-k8s-dev
skipValidation: false
变量说明:
k8s-dev-cluster:Connector 名称 k8s_dev_cluster:Connector Identifier cn-k8s-dev:Delegate 标签 skipValidation:是否跳过连接验证,正式环境建议 false
2. 权限要求
Delegate 使用的 ServiceAccount 至少需要对目标命名空间具备:
get list watch create update patch delete
常用资源:
deployments replicasets pods services configmaps secrets events ingresses
入门阶段可以使用 namespace admin 权限。生产环境建议最小权限控制。
八:为 Delegate 配置命名空间权限
如果不想直接给 cluster-admin,可以创建 namespace 级别 Role。
k8s/rbac/harness-deploy-role.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: harness-deploy-sa
namespace: dev
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: harness-deploy-role
namespace: dev
rules:
- apiGroups: [""]
resources:
- pods
- services
- configmaps
- secrets
- events
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups: ["apps"]
resources:
- deployments
- replicasets
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups: ["networking.k8s.io"]
resources:
- ingresses
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: harness-deploy-rolebinding
namespace: dev
subjects:
- kind: ServiceAccount
name: harness-deploy-sa
namespace: dev
roleRef:
kind: Role
name: harness-deploy-role
apiGroup: rbac.authorization.k8s.io
应用:
kubectl apply -f k8s/rbac/harness-deploy-role.yaml
注意:
如果你的 Delegate 已经安装在 harness-delegate-ng namespace,并使用自己的 ServiceAccount,那么需要把 RoleBinding 的 subject 改成 Delegate 实际使用的 ServiceAccount。
查看 Delegate 使用的 ServiceAccount:
kubectl get deploy -n harness-delegate-ng
kubectl get deploy firstk8sdel -n harness-delegate-ng -o jsonpath='{.spec.template.spec.serviceAccountName}'
如果输出为空,默认使用:
default
这时 RoleBinding subject 应该指向 Delegate 所在命名空间的 ServiceAccount,而不是 dev 里的 harness-deploy-sa。
示例:
subjects:
- kind: ServiceAccount
name: default
namespace: harness-delegate-ng
九:Kubernetes 清单编写原则
本文使用三类资源:
ConfigMap:保存非敏感配置 Deployment:部署 Node.js 应用 Service:提供集群内访问入口
目录:
k8s/
├── templates/
│ ├── configmap.yaml
│ ├── deployment.yaml
│ └── service.yaml
└── values-dev.yaml
注意:使用 Harness 内置 K8s Manifest 时,不建议在 Kubernetes 对象清单中直接写 <+pipeline.sequenceId> 或 <+codebase.commitSha> 这类 Harness 表达式。推荐方式是:
Kubernetes manifest 中使用 Go template 变量,例如 {{.Values.image}} Values YAML 中写 Harness 表达式,例如 image: <+artifacts.primary.image>
这样更符合 Harness 的 K8s Manifest 使用方式。
十:编写 ConfigMap
k8s/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Values.name}}-config
namespace: {{.Values.namespace}}
labels:
app: {{.Values.name}}
data:
APP_ENV: {{.Values.appEnv | quote}}
LOG_LEVEL: {{.Values.logLevel | quote}}
说明:
name:应用名称 namespace:部署命名空间 APP_ENV:环境标识 LOG_LEVEL:日志级别
十一:编写 Deployment
k8s/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{.Values.name}}
namespace: {{.Values.namespace}}
labels:
app: {{.Values.name}}
spec:
replicas: {{.Values.replicas}}
revisionHistoryLimit: 5
selector:
matchLabels:
app: {{.Values.name}}
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: {{.Values.maxSurge | quote}}
maxUnavailable: {{.Values.maxUnavailable | quote}}
template:
metadata:
labels:
app: {{.Values.name}}
spec:
imagePullSecrets:
- name: {{.Values.imagePullSecret}}
containers:
- name: {{.Values.name}}
image: {{.Values.image}}
imagePullPolicy: IfNotPresent
ports:
- containerPort: {{.Values.containerPort}}
envFrom:
- configMapRef:
name: {{.Values.name}}-config
env:
- name: APP_VERSION
value: {{.Values.appVersion | quote}}
readinessProbe:
httpGet:
path: /health
port: {{.Values.containerPort}}
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 6
livenessProbe:
httpGet:
path: /health
port: {{.Values.containerPort}}
initialDelaySeconds: 20
periodSeconds: 20
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
cpu: {{.Values.resources.requests.cpu | quote}}
memory: {{.Values.resources.requests.memory | quote}}
limits:
cpu: {{.Values.resources.limits.cpu | quote}}
memory: {{.Values.resources.limits.memory | quote}}
说明:
replicas:副本数,用于练习扩缩容 strategy:滚动升级策略 imagePullSecrets:拉取 Harbor 私有镜像 readinessProbe:判断 Pod 是否可接流量 livenessProbe:判断容器是否存活 resources:资源请求与限制 revisionHistoryLimit:保留可回滚版本数量
十二:编写 Service
k8s/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{.Values.name}}
namespace: {{.Values.namespace}}
labels:
app: {{.Values.name}}
spec:
type: ClusterIP
selector:
app: {{.Values.name}}
ports:
- name: http
port: {{.Values.servicePort}}
targetPort: {{.Values.containerPort}}
说明:
ClusterIP:仅集群内访问,适合 dev/test 如果需要外部访问,建议通过 Ingress 暴露 Service selector 必须和 Deployment Pod label 一致
十三:编写 Values YAML
k8s/values-dev.yaml
name: harness-demo-app
namespace: dev
replicas: 2
image: <+artifacts.primary.image>
imagePullSecret: harbor-pull-secret
containerPort: 3000
servicePort: 80
appEnv: dev
appVersion: <+pipeline.sequenceId>
logLevel: info
maxSurge: 1
maxUnavailable: 0
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
说明:
image 使用 <+artifacts.primary.image> appVersion 使用 <+pipeline.sequenceId> replicas 可用于练习修改副本数 maxSurge 和 maxUnavailable 控制滚动更新过程
如果你希望用 Git Commit 作为版本:
appVersion: <+codebase.commitSha>
如果想直接部署指定镜像:
image: harbor.company.com/devops/harness-demo-app:105
十四:创建 Harness Service
进入 Harness:
Project → Deployments → Services → New Service
填写:
Name: harness-demo-app Identifier: harness_demo_app Description: Node.js 示例应用 Kubernetes Service
选择:
Service Definition: Kubernetes
十五:添加 Kubernetes Manifest
在 Service Definition 中:
Manifests → Add Manifest → K8s Manifest
选择:
Manifest Store: GitLab Connector: gitlab_company Repository: devops/harness-demo-app Branch: main Manifest File/Folder Path: k8s/templates Values YAML: k8s/values-dev.yaml
说明:
k8s/templates 存放 deployment.yaml、service.yaml、configmap.yaml k8s/values-dev.yaml 提供模板变量值 Harness 会在部署时渲染模板并应用到 Kubernetes
注意:
不要把 <+pipeline.sequenceId> 直接写进 deployment.yaml 应该写在 values-dev.yaml 中
十六:添加 Artifact Source
在 Service Definition 中添加 Artifact:
Artifacts → Add Artifact Source → Docker Registry
填写:
Connector: harbor_company Artifact Source Name: harbor-harness-demo-app Image path: devops/harness-demo-app Tag: <+input>
说明:
Image path 不要带 harbor.company.com 前缀时,以 Harness UI 要求为准 如果 UI 要求完整路径,则填写 harbor.company.com/devops/harness-demo-app Tag 设置为 Runtime Input,运行 Pipeline 时选择具体镜像版本
企业建议:
dev 环境可以使用 <+pipeline.sequenceId> test/pre/prod 建议使用 commit sha 或 release tag
十七:创建 Environment
进入:
Project → Deployments → Environments → New Environment
填写:
Name: dev Identifier: dev Type: Pre-Production
说明:
dev/test/pre 通常选择 Pre-Production prod 选择 Production 后续 RBAC、审批、策略可基于 Environment Type 区分
十八:创建 Infrastructure Definition
进入:
Environment: dev → Infrastructure Definitions → New Infrastructure Definition
填写:
Name: dev-k8s Identifier: dev_k8s Deployment Type: Kubernetes Infrastructure Type: Direct Connection Cluster Connector: k8s_dev_cluster Namespace: dev Release Name: release-<+INFRA_KEY_SHORT_ID>
说明:
Cluster Connector:连接目标 Kubernetes 集群 Namespace:实际部署命名空间 Release Name:Harness 用于跟踪发布的名称 allowSimultaneousDeployments:建议生产环境关闭并发部署
如果是企业多个集群:
dev-k8s test-k8s pre-k8s prod-k8s
每个环境绑定不同 Infrastructure Definition。
十九:创建 Deploy Pipeline
进入:
Project → Pipelines → New Pipeline
填写:
Name: nodejs-k8s-deploy-dev Identifier: nodejs_k8s_deploy_dev Description: 部署 Node.js 应用到 Kubernetes dev 环境
二十:添加 Deploy Stage
点击:
Add Stage → Deploy
配置:
Stage Name: Deploy Dev Identifier: deploy_dev Deployment Type: Kubernetes
选择 Service:
Service: harness-demo-app
选择 Environment:
Environment: dev Infrastructure Definition: dev-k8s
Execution 选择:
Rolling Deployment
二十一:添加 K8s Rolling Deploy Step
在 Execution 中添加或使用默认:
K8s Rolling Deploy
配置:
Name: Rollout Deployment Identifier: rollout_deployment Timeout: 10m Skip Dry Run: false
建议:
入门阶段不要跳过 Dry Run 生产环境必须保留 Dry Run 如果 CRD 或特殊资源导致 Dry Run 失败,再单独分析处理
二十二:添加部署后验证 Step
在 Deploy Stage 后添加 Shell Script:
Name: Verify Kubernetes Deployment Identifier: verify_k8s_deployment Shell: Bash Timeout: 5m
脚本:
set -e
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
APP_NAME="harness-demo-app"
NAMESPACE="<+infra.namespace>"
echo "检查 Deployment rollout 状态"
kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=180s
echo "查看 Deployment"
kubectl get deploy ${APP_NAME} -n ${NAMESPACE} -o wide
echo "查看 Pod"
kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} -o wide
echo "查看 Service"
kubectl get svc ${APP_NAME} -n ${NAMESPACE}
echo "端口转发验证 /health"
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 "Kubernetes 部署验证通过"
说明:
kubectl rollout status 验证滚动发布是否完成 kubectl get pods 查看 Pod 是否 Ready kubectl port-forward + curl 验证应用健康接口 curl -f 非 2xx 会失败并终止流水线
二十三:完整 Pipeline YAML 参考
以下 YAML 为参考版本。不同 Harness 版本 UI 生成字段可能略有不同,建议先用 Visual Editor 创建成功,再切换 YAML 保存。
pipeline:
name: nodejs-k8s-deploy-dev
identifier: nodejs_k8s_deploy_dev
projectIdentifier: harness_demo
orgIdentifier: devops_lab
tags:
tutorial: harness-05
network: china
stages:
- stage:
name: Deploy Dev
identifier: deploy_dev
type: Deployment
spec:
deploymentType: Kubernetes
service:
serviceRef: harness_demo_app
serviceInputs:
serviceDefinition:
type: Kubernetes
spec:
artifacts:
primary:
primaryArtifactRef: harbor_harness_demo_app
sources:
- identifier: harbor_harness_demo_app
type: DockerRegistry
spec:
tag: <+input>
environment:
environmentRef: dev
deployToAll: false
infrastructureDefinitions:
- identifier: dev_k8s
execution:
steps:
- step:
name: Rollout Deployment
identifier: rollout_deployment
type: K8sRollingDeploy
timeout: 10m
spec:
skipDryRun: false
- step:
name: Verify Kubernetes Deployment
identifier: verify_k8s_deployment
type: ShellScript
timeout: 5m
spec:
shell: Bash
source:
type: Inline
spec:
script: |
set -e
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
APP_NAME="harness-demo-app"
NAMESPACE="<+infra.namespace>"
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
kubectl get svc ${APP_NAME} -n ${NAMESPACE}
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 "Kubernetes 部署验证通过"
运行时输入:
Artifact Tag: 例如 105、commit sha、release-20260617-001
二十四:方式二:使用 kubectl apply 部署
如果你需要保留已有 kubectl 脚本,可以使用 Shell Script Step。
这种方式适合:
已有 Jenkins 脚本迁移 初学者理解部署过程 特殊资源需要自定义处理 临时调试
不建议长期完全依赖脚本方式,因为:
Harness 无法完整理解你的资源模型 回滚和审计能力不如内置 K8s Rolling Deploy 模板复用成本更高
二十五:为 kubectl apply 准备普通清单
如果使用 kubectl apply,建议使用普通 YAML,不使用 Go template。
k8s/apply/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: harness-demo-app
namespace: dev
labels:
app: harness-demo-app
spec:
replicas: 2
revisionHistoryLimit: 5
selector:
matchLabels:
app: harness-demo-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: harness-demo-app
spec:
imagePullSecrets:
- name: harbor-pull-secret
containers:
- name: harness-demo-app
image: harbor.company.com/devops/harness-demo-app:IMAGE_TAG_PLACEHOLDER
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
env:
- name: APP_ENV
value: dev
- name: APP_VERSION
value: IMAGE_TAG_PLACEHOLDER
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
k8s/apply/service.yaml
apiVersion: v1
kind: Service
metadata:
name: harness-demo-app
namespace: dev
spec:
type: ClusterIP
selector:
app: harness-demo-app
ports:
- name: http
port: 80
targetPort: 3000
二十六:kubectl apply 脚本
scripts/k8s-apply.sh
#!/usr/bin/env bash
set -e
APP_NAME="harness-demo-app"
NAMESPACE="${NAMESPACE:-dev}"
IMAGE_TAG="${IMAGE_TAG:?IMAGE_TAG is required}"
IMAGE="harbor.company.com/devops/${APP_NAME}:${IMAGE_TAG}"
WORKDIR="/tmp/${APP_NAME}-k8s"
rm -rf "${WORKDIR}"
mkdir -p "${WORKDIR}"
cp k8s/apply/*.yaml "${WORKDIR}/"
sed -i "s|harbor.company.com/devops/${APP_NAME}:IMAGE_TAG_PLACEHOLDER|${IMAGE}|g" "${WORKDIR}/deployment.yaml"
sed -i "s|IMAGE_TAG_PLACEHOLDER|${IMAGE_TAG}|g" "${WORKDIR}/deployment.yaml"
echo "准备应用以下 Kubernetes 清单:"
cat "${WORKDIR}/deployment.yaml"
cat "${WORKDIR}/service.yaml"
kubectl apply -f "${WORKDIR}/service.yaml"
kubectl apply -f "${WORKDIR}/deployment.yaml"
kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=180s
kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} -o wide
kubectl get svc ${APP_NAME} -n ${NAMESPACE}
echo "kubectl apply 部署完成"
在 Harness Shell Script Step 中执行:
set -e
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
chmod +x scripts/k8s-apply.sh
IMAGE_TAG="<+pipeline.sequenceId>" \
NAMESPACE="dev" \
scripts/k8s-apply.sh
如果使用 commit sha:
IMAGE_TAG="<+codebase.commitSha>" \
NAMESPACE="dev" \
scripts/k8s-apply.sh
二十七:验证 Kubernetes 部署结果
1. 查看 Deployment
kubectl get deploy -n dev
预期:
NAME READY UP-TO-DATE AVAILABLE harness-demo-app 2/2 2 2
2. 查看 Pod
kubectl get pods -n dev -l app=harness-demo-app -o wide
3. 查看 Service
kubectl get svc -n dev
4. 查看镜像版本
kubectl get deploy harness-demo-app -n dev \
-o jsonpath='{.spec.template.spec.containers[0].image}'
预期:
harbor.company.com/devops/harness-demo-app:105
5. 健康检查
kubectl port-forward svc/harness-demo-app 8080:80 -n dev
访问:
预期:
{
"status": "UP"
}
二十八:练习 1:修改副本数,观察扩缩容
修改 k8s/values-dev.yaml:
replicas: 3
重新运行 Pipeline。
观察:
kubectl get deploy harness-demo-app -n dev
kubectl get pods -n dev -l app=harness-demo-app
预期:
Pod 数量从 2 个变为 3 个 Deployment READY 显示 3/3
也可以使用 kubectl 手动扩容:
kubectl scale deployment/harness-demo-app --replicas=4 -n dev
观察:
kubectl get pods -n dev -l app=harness-demo-app -w
注意:
如果你手动 scale 到 4,但随后重新 apply values-dev.yaml 中 replicas: 3,Deployment 会被重新调整回 3。 因此企业落地中,副本数应由 Git 管理,或者由 HPA 管理,不建议长期手动修改。
二十九:练习 2:修改镜像版本,观察滚动升级
假设 Harbor 中已有两个镜像:
harbor.company.com/devops/harness-demo-app:101 harbor.company.com/devops/harness-demo-app:102
第一次部署输入:
Artifact Tag: 101
查看当前镜像:
kubectl get deploy harness-demo-app -n dev \
-o jsonpath='{.spec.template.spec.containers[0].image}'
第二次部署输入:
Artifact Tag: 102
观察滚动升级:
kubectl rollout status deployment/harness-demo-app -n dev
kubectl get rs -n dev
kubectl get pods -n dev -l app=harness-demo-app -w
预期:
新的 ReplicaSet 被创建 新 Pod 逐步 Ready 旧 Pod 逐步缩容 Service 始终指向 app=harness-demo-app 的 Ready Pod
三十:练习 3:模拟错误镜像,观察失败
将 Artifact Tag 输入一个不存在的版本:
not-exist-tag
观察:
kubectl get pods -n dev
kubectl describe pod <pod-name> -n dev
常见状态:
ImagePullBackOff ErrImagePull
原因:
镜像 Tag 不存在 Harbor 认证失败 imagePullSecrets 不正确 K8s 节点无法访问 Harbor Harbor 使用自签证书但节点不信任
恢复方式:
重新运行 Pipeline,选择正确 Artifact Tag 或执行 kubectl rollout undo
三十一:回滚脚本
scripts/k8s-rollback.sh
#!/usr/bin/env bash
set -e
APP_NAME="${APP_NAME:-harness-demo-app}"
NAMESPACE="${NAMESPACE:-dev}"
echo "查看发布历史"
kubectl rollout history deployment/${APP_NAME} -n ${NAMESPACE}
echo "回滚到上一版本"
kubectl rollout undo deployment/${APP_NAME} -n ${NAMESPACE}
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
echo "回滚完成"
运行:
chmod +x scripts/k8s-rollback.sh
NAMESPACE=dev scripts/k8s-rollback.sh
企业建议:
dev/test 可允许自动回滚 pre/prod 回滚建议走审批或故障流程 生产回滚前确认数据库变更是否兼容
三十二:常见问题排查
1. Kubernetes Connector 测试失败
常见原因:
Delegate 没有目标集群权限 Delegate 标签选择错误 Kubernetes API Server 不可访问 ServiceAccount 权限不足 企业代理错误地代理了 kubernetes.default.svc 证书不受信任
排查:
kubectl get pods -n harness-delegate-ng
kubectl logs -f deployment/firstk8sdel -n harness-delegate-ng
kubectl auth can-i create deployment -n dev
kubectl auth can-i patch deployment -n dev
kubectl auth can-i list pods -n dev
2. 部署失败,提示 namespace 不存在
解决:
kubectl create namespace dev
或确认 Infrastructure Definition 中 Namespace 是否正确。
3. ImagePullBackOff
排查:
kubectl describe pod <pod-name> -n dev
kubectl get secret harbor-pull-secret -n dev
常见原因:
镜像 Tag 不存在 imagePullSecrets 不存在 Harbor 地址写错 Harbor Robot Token 错误 K8s 节点不能访问 Harbor Harbor 自签证书未被 containerd/docker 信任
4. CrashLoopBackOff
排查:
kubectl logs <pod-name> -n dev
kubectl describe pod <pod-name> -n dev
常见原因:
应用启动失败 环境变量缺失 端口配置错误 配置文件不存在 容器命令错误
5. Service 访问不通
排查:
kubectl get svc harness-demo-app -n dev
kubectl get endpoints harness-demo-app -n dev
kubectl get pods -n dev --show-labels
重点检查:
Service selector 是否和 Pod labels 一致 Pod 是否 Ready targetPort 是否等于 containerPort 应用是否监听 0.0.0.0 而不是 127.0.0.1
6. readinessProbe 失败
排查:
kubectl describe pod <pod-name> -n dev
kubectl logs <pod-name> -n dev
常见原因:
/health 路径不存在 应用启动时间超过 initialDelaySeconds 端口写错 服务依赖数据库或 Redis 未就绪
7. Harness 部署成功但 Pod 没更新
常见原因:
镜像 Tag 没变 imagePullPolicy 为 IfNotPresent 且 Tag 复用 Deployment 模板没有变化 Harbor 中 latest 被覆盖但 Kubernetes 没重新拉
解决:
不要在生产使用 latest 每次构建使用唯一 Tag Deployment 中 image 使用唯一 Tag
三十三:企业最佳实践
1. 推荐使用 Harness 内置 K8s Rolling Deploy
原因:
便于统一管理 Service、Environment、Infrastructure 部署记录清晰 更容易接入审批、验证、回滚、策略 比纯 Shell 脚本更适合平台化治理
2. Manifest 和 Values 分离
推荐:
templates/deployment.yaml:只放 Kubernetes 模板 values-dev.yaml:放 dev 参数 values-test.yaml:放 test 参数 values-prod.yaml:放 prod 参数
示例:
k8s/
├── templates/
│ ├── deployment.yaml
│ └── service.yaml
├── values-dev.yaml
├── values-test.yaml
├── values-pre.yaml
└── values-prod.yaml
3. 镜像版本不可变
生产不要使用:
latest
推荐使用:
Git Commit SHA release-20260617-001 v1.0.0
4. Namespace 隔离
建议:
dev namespace test namespace pre namespace prod namespace
更严格的企业可以:
dev/test 用一个集群 pre/prod 用独立集群 prod 独立 VPC 或独立节点池
5. 权限最小化
建议:
dev:namespace admin test:namespace admin pre:受限 Role + 审批 prod:受限 Role + 审批 + 操作审计
不要所有环境都使用 cluster-admin。
6. 探针必须配置
至少配置:
readinessProbe livenessProbe resources requests/limits
生产建议增加:
startupProbe PodDisruptionBudget HorizontalPodAutoscaler NetworkPolicy Ingress
7. 发布前后验证
发布前:
Dry Run Manifest 校验 镜像是否存在 权限校验
发布后:
rollout status Pod Ready Service endpoint 健康检查接口 业务冒烟测试
三十四:验收标准
完成本文后,你应该达到:
已创建 Kubernetes Connector 已确认 Delegate 能访问 Kubernetes API 已准备 dev namespace 已创建 harbor-pull-secret 已编写 ConfigMap、Deployment、Service 已使用 Values YAML 管理参数 已创建 Harness Service 已添加 K8s Manifest 已添加 Harbor Artifact Source 已创建 Environment 和 Infrastructure Definition 已使用 K8s Rolling Deploy 部署应用 已完成 kubectl rollout status 验证 已通过 /health 健康检查 已修改 replicas 并观察扩缩容 已修改镜像 tag 并观察滚动升级 已理解 kubectl apply 方式的适用场景
三十五:本篇总结
本篇完成了 Harness 部署应用到 Kubernetes 的基础落地。
你需要重点记住:
Kubernetes Connector 推荐通过 Delegate 继承凭证 国内环境下 Delegate 必须能访问 Kubernetes、GitLab、Harbor K8s 节点必须能访问 Harbor 私有镜像必须配置 imagePullSecrets Harness 内置 K8s Rolling Deploy 优先于纯 kubectl 脚本 Kubernetes object manifest 中不要直接写 Harness 表达式 Harness 表达式应放在 Values YAML 或 Artifact 配置中 Deployment 默认支持 RollingUpdate 修改副本数会触发扩缩容 修改镜像版本会触发滚动升级 rollout status 是验证部署是否成功的重要命令 生产环境不能依赖 latest 权限、命名空间、探针、资源限制是企业落地必备项