很多 CI/CD 教程默认使用 GitHub、DockerHub 和海外 npm 源。这个组合在国内企业网络里经常跑不稳:代码拉不下来、镜像推不上去、npm install 卡住、流水线日志看起来一切正常但任务实际超时。
所以这一篇我们换一个更接近国内企业落地的方案:
| 国外默认 | 国内替代方案 | | --- | --- | | 代码仓库 | GitLab / Gitee | | 镜像仓库 | Harbor / 国内云镜像仓库 | | npm 源 | npmmirror | | 构建环境 | 企业内网 Kubernetes | | 部署环境 | Kubernetes dev namespace | | 流水线编排 | Harness | | 执行入口 | Harness Delegate |
核心思路很简单:
-
Harness SaaS
负责编排和展示
-
Harness Delegate
放在企业内网
-
Git、Harbor、Kubernetes 都通过 Delegate 访问
-
基础镜像提前同步到 Harbor
-
业务镜像统一推送到 Harbor
本篇最终会完成一条可运行的流水线:
code复制
GitLab 拉取代码 → 安装依赖 → 执行单元测试 → 构建 Docker 镜像 → 推送镜像到 Harbor → 手动确认是否部署 → 部署到 Kubernetes dev 环境 → 验证服务健康状态
一、开始之前,你需要准备什么
如果你是跟着本系列做,上一篇应该已经准备好了这些内容:
-
Harness SaaS 账号
-
Harness Org / Project
-
Harness Delegate
-
GitLab / Gitee Connector
-
Harbor / 国内镜像仓库 Connector
-
Kubernetes Cluster Connector
-
第一个空 Pipeline
本篇默认使用下面这些名称,实际操作时替换成你自己的即可。
1. Harness 侧
-
Org:
devops-lab -
Project:
harness-demo -
Delegate:
cn-k8s-dev -
Delegate 状态:Connected / Available
2. 代码仓库
推荐使用企业内网 GitLab,或者 Gitee 企业版。
code复制
GitLab:
https://gitlab.company.com/devops/harness-demo-app.git
Gitee 企业版:
https://gitee.com/company/harness-demo-app.git
3. 镜像仓库
本文以 Harbor 为例。
code复制
Harbor 地址:
https://harbor.company.com
示例镜像:
harbor.company.com/devops/harness-demo-app
如果你使用的是阿里云 ACR、腾讯云 TCR、华为云 SWR,思路一样,只需要替换 Registry Connector 和镜像地址。
4. Kubernetes Namespace
准备两个 namespace:
-
harness-ci:用于 CI 构建任务
-
dev:用于部署应用
执行:
bash复制
kubectl create namespace harness-ci
kubectl create namespace dev
5. 国内 npm 源
项目根目录准备 .npmrc:
ini复制
registry=https://registry.npmmirror.com
fetch-retries=5
fetch-retry-mintimeout=20000
fetch-retry-maxtimeout=120000
这一步不要省。国内流水线里,npm install 慢、失败、偶发超时,很多时候就是没有提前处理 npm 源。
二、最终流水线长什么样
这条流水线分成三个 Stage:
code复制
Pipeline: nodejs-harbor-k8s-dev
Stage 1: CI Build
Step 1: 拉取代码
Step 2: npm install
Step 3: npm test
Step 4: docker build
Step 5: docker push Harbor
Stage 2: Manual Approval
Step 1: 人工确认是否部署到 dev
Stage 3: Deploy Dev
Step 1: 部署 Kubernetes Manifest
Step 2: 等待 Pod 稳定
Step 3: 访问健康检查接口
第一次做时,建议按这个顺序来:
-
先用 Visual Editor 跑通
-
再切换到 YAML 看结构
-
最后把 YAML 固化到 Git 仓库
不要一上来就手写完整 YAML。Harness 的字段比较多,初学阶段先跑通比"写得漂亮"更重要。
三、示例项目目录结构
本篇使用一个最小 Node.js 示例应用,目录如下:
code复制
harness-demo-app/
├── README.md
├── .npmrc
├── app/
│ ├── package.json
│ ├── server.js
│ └── server.test.js
├── docker/
│ └── Dockerfile
├── k8s/
│ ├── namespace.yaml
│ ├── deployment.yaml
│ └── service.yaml
├── harness/
│ ├── pipeline-nodejs-harbor-k8s-cn.yaml
│ └── inputsets/
│ └── dev.yaml
└── scripts/
├── local-build.sh
├── local-push.sh
└── local-deploy.sh
这个结构适合入门,也方便后续扩展成企业模板。
四、编写 Node.js 示例服务
1. .npmrc
项目根目录创建 .npmrc:
ini复制
registry=https://registry.npmmirror.com
fetch-retries=5
fetch-retry-mintimeout=20000
fetch-retry-maxtimeout=120000
作用主要有三个:
-
避免 CI 默认访问海外 npm 源
-
提高国内构建稳定性
-
降低流水线随机超时概率
2. app/package.json
json复制
{
"name":"harness-demo-app",
"version":"1.0.0",
"description":"Harness CI/CD demo for China network",
"main":"server.js",
"scripts":{
"start":"node server.js",
"test":"node server.test.js"
},
"dependencies":{
"express":"^4.18.3"
}
}
3. app/server.js
js复制
const express = require("express");
functioncreateApp() {
const app = express();
app.get("/", (req, res) => {
res.json({
app: "harness-demo-app",
env: process.env.APP_ENV || "dev",
version: process.env.APP_VERSION || "v1",
message: "Hello Harness CI/CD China Network"
});
});
app.get("/health", (req, res) => {
res.status(200).json({
status: "UP"
});
});
return app;
}
if (require.main === module) {
const app = createApp();
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`server started on port ${port}`);
});
}
module.exports = createApp;
4. app/server.test.js
先写一个最小可用的测试,不引入 Jest、Vitest、Mocha 这类测试框架。
js复制
const createApp = require("./server");
functionassert(condition, message) {
if (!condition) {
thrownewError(message);
}
}
const app = createApp();
assert(app, "app should be created");
assert(typeof app.listen === "function", "app should be an express instance");
console.log("unit test passed");
这个测试很简单,但有一个关键作用:只要测试失败,后面的镜像构建和部署就不应该继续执行。
五、编写国内版 Dockerfile
创建 docker/Dockerfile:
dockerfile复制
FROM harbor.company.com/library/node:20-alpine
WORKDIR /app
COPY .npmrc ./
COPY app/package*.json ./
RUN npm install --omit=dev
COPY app/server.js ./
ENV PORT=3000
ENV APP_ENV=dev
EXPOSE3000
CMD ["node", "server.js"]
这里有一个国内环境里的关键点:
FROM 使用 Harbor 中的基础镜像,不要直接使用 node:20-alpine
也就是说,不建议这样写:
dockerfile复制
FROM node:20-alpine
更推荐先把基础镜像同步到 Harbor:
code复制
harbor.company.com/library/node:20-alpine
这样 CI 构建 Pod 不需要依赖 DockerHub,流水线稳定性会高很多。
六、准备 Kubernetes Manifest
1. k8s/namespace.yaml
yaml复制
apiVersion:v1
kind:Namespace
metadata:
name:dev
2. k8s/deployment.yaml
yaml复制
apiVersion:apps/v1
kind:Deployment
metadata:
name:harness-demo-app
namespace:dev
labels:
app:harness-demo-app
spec:
replicas:1
selector:
matchLabels:
app:harness-demo-app
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:<+pipeline.sequenceId>
imagePullPolicy:Always
ports:
-containerPort:3000
env:
-name:APP_ENV
value:"dev"
-name:APP_VERSION
value:"<+pipeline.sequenceId>"
readinessProbe:
httpGet:
path:/health
port:3000
initialDelaySeconds:5
periodSeconds:10
livenessProbe:
httpGet:
path:/health
port:3000
initialDelaySeconds:15
periodSeconds:20
这里用到了 Harness 表达式:
code复制
<+pipeline.sequenceId>
它表示当前 Pipeline 的执行序号。每次流水线运行都会生成一个新的镜像 Tag,Deployment 也使用同一个 Tag,这样可以保证"构建出来的镜像"和"部署出去的镜像"一致。
如果你更希望用 Git Commit ID,也可以改成:
code复制
<+codebase.commitSha>
生产环境更推荐不可变 Tag,后面会专门说。
3. k8s/service.yaml
yaml复制
apiVersion:v1
kind:Service
metadata:
name:harness-demo-app
namespace:dev
spec:
selector:
app:harness-demo-app
ports:
-name:http
port:80
targetPort:3000
type:ClusterIP
七、提前创建 Harbor 拉取密钥
Kubernetes 部署时,需要能从 Harbor 拉取镜像。
bash复制
kubectl create secret docker-registry harbor-pull-secret \
--docker-server=harbor.company.com \
--docker-username='robot$harness-demo' \
--docker-password='替换成 Harbor 机器人账号 Token' \
--docker-email=devops@company.com \
-n dev
建议这样做:
-
每个项目创建独立 Harbor 机器人账号
-
不同环境使用不同机器人账号
-
生产环境不要使用个人账号
如果你的 Harbor 使用自签证书,还需要提前处理 Kubernetes 节点或运行时的证书信任问题。否则 Pod 可能会出现 ImagePullBackOff。
八、先在本地验证一遍
正式接入 Harness 之前,建议先在本地把应用、镜像和 Manifest 验证一遍。
1. 安装依赖
bash复制
npm --prefix app install
2. 执行测试
bash复制
npm --prefix app test
预期输出:
code复制
unit test passed
3. 本地构建镜像
创建 scripts/local-build.sh:
bash复制
#!/usr/bin/env bash
set -e
IMAGE="harbor.company.com/devops/harness-demo-app:local"
docker build \
-f docker/Dockerfile \
-t ${IMAGE} \
.
echo"build success: ${IMAGE}"
执行:
bash复制
chmod +x scripts/local-build.sh
./scripts/local-build.sh
4. 本地推送镜像
创建 scripts/local-push.sh:
bash复制
#!/usr/bin/env bash
set -e
IMAGE="harbor.company.com/devops/harness-demo-app:local"
docker login harbor.company.com
docker push ${IMAGE}
echo"push success: ${IMAGE}"
5. 本地部署验证
如果直接使用 k8s/deployment.yaml,里面的 <+pipeline.sequenceId> 只有在 Harness 里才会被解析。本地验证时,可以临时替换成 local。
创建 scripts/local-deploy.sh:
bash复制
#!/usr/bin/env bash
set -e
sed 's/<+pipeline.sequenceId>/local/g' k8s/deployment.yaml > /tmp/harness-demo-deployment-local.yaml
kubectl apply -f k8s/namespace.yaml
kubectl apply -f /tmp/harness-demo-deployment-local.yaml
kubectl apply -f k8s/service.yaml
kubectl rollout status deployment/harness-demo-app -n dev
kubectl get pods -n dev
kubectl get svc -n dev
这样做的好处是:不用修改仓库里的正式 Manifest,也能完成本地验证。
九、Visual Editor 和 YAML 怎么选
Harness 创建流水线有两种方式:
-
Visual Editor
:可视化编辑器
-
YAML Editor
:代码化配置
Visual Editor 适合什么场景
适合:
-
第一次配置 Connector
-
第一次配置 CI/CD 流程
-
团队培训和演示
-
快速理解 Stage、Step、Service、Environment、Infrastructure
优点是直观,不容易写错字段。缺点是不太适合版本管理和代码审查。
YAML Editor 适合什么场景
适合:
-
企业级落地
-
流水线纳入 Git 管理
-
模板化复用
-
多环境统一管理
-
平台工程团队统一治理
缺点也明显:字段较多,初学者容易写错。
所以本篇建议:
-
第一遍:Visual Editor 跑通
-
第二遍:切换 YAML 查看结构
-
第三遍:把 YAML 保存到 Git
-
第四遍:后续统一按 YAML 工程化维护
十、创建 Pipeline
进入 Harness 控制台:
Project → Pipelines → New Pipeline
填写:
-
Name
:
nodejs-harbor-k8s-dev -
Identifier
:
nodejs_harbor_k8s_dev -
Description
: 国内网络环境下 Node.js 应用 CI/CD 流水线
保存后,开始添加 Stage。
十一、创建 CI Build 阶段
1. 添加 CI Stage
点击:
Add Stage → Build
填写:
-
Stage Name
:
CI Build -
Stage Identifier
:
ci_build
构建基础设施选择:
code复制
Kubernetes
配置:
-
Kubernetes Connector
:
k8s_dev_cluster -
Namespace
:
harness-ci -
OS
: Linux
-
Delegate Selector
:
cn-k8s-dev
这表示 Harness 会使用 Kubernetes 作为 CI 执行环境。构建过程中会在 harness-ci namespace 中创建临时 Pod,构建完成后释放资源。
2. 配置代码仓库
在 CI Stage 中开启 Clone Codebase。
填写:
-
Codebase Connector
:
gitlab_company -
Repository
:
devops/harness-demo-app -
Branch
:
main -
Clone Directory
:
/harness
国内环境重点检查三件事:
- GitLab Connector 必须通过 Delegate 访问
- GitLab Token 必须有 read_repository 权限
- 如果 GitLab 使用自签证书,需要处理证书信任
3. 添加 npm install Step
添加 Step:
Add Step → Run
填写:
-
Name
:
Install Dependencies -
Identifier
:
install_dependencies -
Image
:
harbor.company.com/library/node:20-alpine -
Shell
: Sh
命令:
bash复制
cd app
npm config set registry https://registry.npmmirror.com
npm install
这里依然使用 Harbor 中的 Node.js 基础镜像,不直接拉 DockerHub 的 node:20-alpine。
4. 添加单元测试 Step
继续添加 Step:
Add Step → Run
填写:
-
Name
:
Unit Test -
Identifier
:
unit_test -
Image
:
harbor.company.com/library/node:20-alpine -
Shell
: Sh
命令:
bash复制
cd app
npm test
预期输出:
code复制
unit test passed
如果这个 Step 失败,后面的 Docker 镜像构建和部署都不应该继续执行。
5. 添加 Docker Build & Push Step
添加 Step:
Add Step → Build and Push to Docker Registry
填写:
-
Name
:
Build And Push Image -
Identifier
:
build_and_push_image -
Docker Registry Connector
:
harbor_company -
Dockerfile
:
docker/Dockerfile -
Context
:
. -
Repository
:
harbor.company.com/devops/harness-demo-app -
Tags
:
<+pipeline.sequenceId>+latest
说明:
-
<+pipeline.sequenceId>用作唯一版本号
-
latest只用于开发环境调试
-
生产环境不建议依赖 latest
这里的 Repository 字段要以你的 Harbor Connector 配置为准。有些团队会填写完整地址,有些团队会在 Connector 中配置 Registry 地址后,只填写 devops/harness-demo-app。如果不确定,优先按 Visual Editor 生成的字段为准。
国内环境重点检查:
- Harbor Connector 必须能通过 Delegate 访问
- Harbor 机器人账号必须有 push 权限
- 基础镜像必须提前同步到 Harbor
- Dockerfile 中 FROM 必须使用国内镜像地址
十二、创建手动审批阶段
虽然本篇部署的是 dev 环境,但建议加一个手动审批阶段,让大家先理解"发布卡点"的概念。
点击:
Add Stage → Approval
填写:
-
Stage Name
:
Manual Approval -
Stage Identifier
:
manual_approval -
Approval Type
: Harness Approval
审批说明可以写成这样:
请确认是否将本次构建镜像部署到 dev 环境。
应用:harness-demo-app
镜像地址:harbor.company.com/devops/harness-demo-app:
<+pipeline.sequenceId>触发分支:
<+codebase.branch>提交 ID:
<+codebase.commitSha>执行人:
<+pipeline.triggeredBy.name>
审批人配置:
-
Approvers
:
devops-admins -
Minimum approvals
:
1 -
Timeout
:
1h
建议:
-
dev 环境审批可以选配
-
test 环境按团队流程配置
-
pre / prod 环境必须配置审批
-
生产审批人不建议是流水线触发人本人
十三、创建 Deploy Dev 阶段
1. 添加 Deploy Stage
点击:
Add Stage → Deploy
填写:
-
Stage Name
:
Deploy Dev -
Stage Identifier
:
deploy_dev -
Deployment Type
: Kubernetes
2. 配置 Service
Service 表示这次要部署的应用。
填写:
-
Service Name
:
harness-demo-app -
Service Identifier
:
harness_demo_app -
Service Definition
: Kubernetes
Manifest 来源选择 Git:
-
Manifest Source
: Git Repository
-
Git Connector
:
gitlab_company -
Repository
:
devops/harness-demo-app -
Branch
:
main -
Manifest Path
:
k8s
Harness 会读取 k8s 目录下的 namespace.yaml、deployment.yaml 和 service.yaml。
3. 配置 Environment
创建环境:
-
Environment Name
:
dev -
Environment Identifier
:
dev -
Environment Type
: Pre-Production
4. 配置 Infrastructure
填写:
-
Infrastructure Name
:
dev-k8s -
Infrastructure Identifier
:
dev_k8s -
Cluster Connector
:
k8s_dev_cluster -
Namespace
:
dev -
Release Name
:
harness-demo-app
这里的含义是:
-
Cluster Connector 指向 Kubernetes 集群
-
Namespace 表示部署到 dev
-
Release Name 用于 Harness 记录发布历史
5. 选择部署策略
初学阶段建议选择:
code复制
Rolling Deployment
原因很简单:配置少、容易理解,符合普通 Kubernetes Deployment 的发布方式。Canary 和 Blue Green 可以放到后面的进阶篇。
6. 添加部署后健康检查 Step
在 Deploy Dev Stage 的 Post-deployment 中添加 Shell Script Step。
填写:
-
Name
:
Health Check -
Identifier
:
health_check -
Shell
: Bash
脚本:
bash复制
set -e
export KUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
cleanup() {
if [ -n "${PF_PID:-}" ]; then
kill"${PF_PID}" >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT
echo"检查 Deployment 状态"
kubectl rollout status deployment/harness-demo-app -n <+infra.namespace> --timeout=120s
echo"检查 Pod"
kubectl get pods -n <+infra.namespace> -l app=harness-demo-app
echo"启动端口转发并验证 health"
kubectl port-forward svc/harness-demo-app 18080:80 -n <+infra.namespace> > /tmp/port-forward.log 2>&1 &
PF_PID=$!
sleep 5
curl -f http://127.0.0.1:18080/health
echo"健康检查通过"
这里加了 trap cleanup EXIT,避免 curl 失败时端口转发进程残留。
十四、Pipeline YAML 参考
下面这份 YAML 只作为结构参考。不同 Harness 版本、不同连接器类型、不同 Service 配置方式,生成的字段可能略有差异。
企业落地时,不建议直接复制这段 YAML 硬跑。更稳妥的方式是:
-
先用 Visual Editor 配置成功
-
切换到 YAML 查看 Harness 自动生成结果
-
再基于自动生成结果整理成 Git 管理版本
yaml复制
pipeline:
name:nodejs-harbor-k8s-dev
identifier:nodejs_harbor_k8s_dev
projectIdentifier:harness_demo
orgIdentifier:devops_lab
tags:
network:china
app:nodejs
registry:harbor
stages:
-stage:
name:CIBuild
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:InstallDependencies
identifier:install_dependencies
type:Run
spec:
connectorRef:harbor_company
image:harbor.company.com/library/node:20-alpine
shell:Sh
command:|
cd app
npm config set registry https://registry.npmmirror.com
npm install
-step:
name:UnitTest
identifier:unit_test
type:Run
spec:
connectorRef:harbor_company
image:harbor.company.com/library/node:20-alpine
shell:Sh
command:|
cd app
npm test
-step:
name:BuildAndPushImage
identifier:build_and_push_image
type:BuildAndPushDockerRegistry
spec:
connectorRef:harbor_company
repo:harbor.company.com/devops/harness-demo-app
tags:
-<+pipeline.sequenceId>
-latest
dockerfile:docker/Dockerfile
context:.
-stage:
name:ManualApproval
identifier:manual_approval
type:Approval
spec:
execution:
steps:
-step:
name:ApproveDevDeploy
identifier:approve_dev_deploy
type:HarnessApproval
timeout:1h
spec:
approvalMessage:|
请确认是否部署到 dev 环境。
镜像:
harbor.company.com/devops/harness-demo-app:<+pipeline.sequenceId>
分支:
<+codebase.branch>
提交:
<+codebase.commitSha>
includePipelineExecutionHistory:true
approvers:
userGroups:
-devops_admins
minimumCount:1
disallowPipelineExecutor:false
-stage:
name:DeployDev
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:RolloutDeployment
identifier:rollout_deployment
type:K8sRollingDeploy
timeout:10m
spec:
skipDryRun:false
-step:
name:HealthCheck
identifier:health_check
type:ShellScript
timeout:5m
spec:
shell:Bash
source:
type:Inline
spec:
script:|
set -e
exportKUBECONFIG=${HARNESS_KUBE_CONFIG_PATH}
cleanup() {
if [ -n"${PF_PID:-}" ];then
kill"${PF_PID}">/dev/null2>&1||true
fi
}
trapcleanupEXIT
kubectlrolloutstatusdeployment/harness-demo-app-n<+infra.namespace>--timeout=120s
kubectlgetpods-n<+infra.namespace>-lapp=harness-demo-app
kubectlport-forwardsvc/harness-demo-app18080:80-n<+infra.namespace>>/tmp/port-forward.log2>&1&
PF_PID=$!
sleep5
curl-fhttp://127.0.0.1:18080/health
echo"健康检查通过"
十五、运行 Pipeline
点击:
Save → Run → Run Pipeline
运行时确认:
-
分支:
main -
环境:
dev -
镜像 Tag:
<+pipeline.sequenceId>
流水线执行顺序应该是:
code复制
CI Build
→ Install Dependencies
→ Unit Test
→ Build And Push Image
Manual Approval
→ Approve Dev Deploy
Deploy Dev
→ Rollout Deployment
→ Health Check
十六、验证部署结果
在运维机或本地 kubectl 环境执行:
bash复制
kubectl get pods -n dev
kubectl get deploy -n dev
kubectl get svc -n dev
查看当前部署的镜像版本:
bash复制
kubectl get deploy harness-demo-app -n dev \
-o jsonpath='{.spec.template.spec.containers[0].image}'
预期输出类似:
code复制
harbor.company.com/devops/harness-demo-app:12
做一次端口转发:
bash复制
kubectl port-forward svc/harness-demo-app 8080:80 -n dev
访问 http://localhost:8080,预期返回:
json复制
{
"app":"harness-demo-app",
"env":"dev",
"version":"12",
"message":"Hello Harness CI/CD China Network"
}
健康检查地址 http://localhost:8080/health,预期返回:
json复制
{
"status":"UP"
}
十七、国内环境常见问题排查
问题 1:CI Stage 拉代码失败
常见原因:
-
GitLab Connector 没有选择通过 Delegate 执行
-
Delegate 无法访问 GitLab
-
GitLab Token 权限不足
-
GitLab 使用自签证书
-
Repository 地址写错
-
Branch 写错
排查方式:
bash复制
kubectl exec -it deploy/firstk8sdel -n harness-delegate-ng -- sh
curl -k -I https://gitlab.company.com
处理建议:
-
确认 GitLab Connector 使用 Delegate
-
确认 Delegate Selector 正确
-
确认 Token 有 read_repository 权限
-
确认 GitLab 地址可以从 Delegate Pod 访问
问题 2:npm install 很慢或失败
常见原因:
-
没有
.npmrc -
Dockerfile 没有 COPY
.npmrc -
Run Step 中没有设置 registry
-
企业网络限制访问 npm 官方源
处理建议:
bash复制
npm config set registry https://registry.npmmirror.com
npm config get registry
同时确认 Dockerfile 里有这一行:
dockerfile复制
COPY .npmrc ./
问题 3:Docker Build 失败
常见原因:
-
基础镜像没有同步到 Harbor
-
Dockerfile 中 FROM 仍然是
node:20-alpine -
Harbor 证书不被信任
-
CI 构建 Pod 无法访问 Harbor
可以先在构建环境里验证:
bash复制
docker pull harbor.company.com/library/node:20-alpine
如果 Harbor 使用自签证书,需要在 Kubernetes 节点或构建环境中配置证书信任。
问题 4:Docker Push 失败
常见原因:
-
Harbor Connector 密码错误
-
机器人账号没有 push 权限
-
Harbor 项目不存在
-
镜像仓库地址写错
处理建议:
-
确认 Harbor 项目 devops 已创建
-
确认
robot$harness-demo有 push 权限 -
确认 Harness Secret 中保存的是机器人账号 Token
问题 5:Kubernetes 部署失败
常见原因:
-
dev namespace 不存在
-
imagePullSecrets 不存在
-
Deployment 中镜像地址错误
-
Kubernetes Connector 权限不足
-
ServiceAccount 没有部署权限
排查命令:
bash复制
kubectl describe pod -n dev
kubectl get events -n dev --sort-by=.metadata.creationTimestamp
kubectl get secret harbor-pull-secret -n dev
问题 6:Health Check 失败
常见原因:
-
Pod 没有 Ready
-
Service selector 和 Pod label 不一致
-
应用端口不是 3000
-
/health接口返回非 200
-
port-forward 没启动成功
排查命令:
bash复制
kubectl get pods -n dev -l app=harness-demo-app
kubectl logs -n dev deploy/harness-demo-app
kubectl describe svc harness-demo-app -n dev
十八、4 个练习:让流水线更接近真实项目
练习 1:补充单元测试
把 app/server.test.js 改成下面这样:
js复制
const createApp = require("./server");
functionassert(condition, message) {
if (!condition) {
thrownewError(message);
}
}
const app = createApp();
assert(app, "app should be created");
assert(typeof app.listen === "function", "app should be an express instance");
const routes = app._router.stack
.filter(layer => layer.route)
.map(layer => layer.route.path);
assert(routes.includes("/"), "root route should exist");
assert(routes.includes("/health"), "health route should exist");
console.log("unit test passed");
提交代码后重新运行 Pipeline。
预期结果:
code复制
Install Dependencies 成功
Unit Test 成功
Build And Push Image 成功
Deploy Dev 成功
Health Check 成功
练习 2:配置手动审批
将 Manual Approval Stage 调整为:
-
审批人组:
devops-admins -
最少审批人数:1
-
超时时间:30m
-
审批说明:展示镜像地址、分支、提交 ID、执行人
审批说明建议:
请确认是否部署到 dev 环境。
应用:harness-demo-app
镜像:harbor.company.com/devops/harness-demo-app:
<+pipeline.sequenceId>分支:
<+codebase.branch>提交:
<+codebase.commitSha>执行人:
<+pipeline.triggeredBy.name>
这个练习的目的不是让 dev 环境变复杂,而是让你理解发布治理中的"审批卡点"。
练习 3:测试失败时阻断部署
故意把 server.test.js 改成失败:
js复制
thrownewError("mock unit test failed");
提交后重新运行 Pipeline。
预期结果:
code复制
Unit Test 失败
Build And Push Image 不执行
Manual Approval 不执行
Deploy Dev 不执行
这个练习可以帮助你理解三件事:
- CI 测试失败必须阻断 CD
- 不要让有问题的代码进入镜像仓库
- 不要让有问题的镜像部署到 Kubernetes
练习 4:把 latest 从生产流程中移除
开发环境可以临时使用:
code复制
latest
<+pipeline.sequenceId>
但生产环境建议只使用不可变 Tag,例如:
code复制
<+codebase.commitSha>
v1.0.0
release-20260611-001
修改 Build And Push Image Step:
Tags 改为:<+codebase.commitSha>
同时把 Deployment 中镜像 Tag 也改成:<+codebase.commitSha>
这样做可以提高发布可追溯性,也能避免 latest 带来的回滚困难。
十九、企业落地建议
1. 国内网络环境不要依赖海外源
建议统一替换:
| 海外默认 | 国内替代方案 | | --- | --- | | GitHub | GitLab / Gitee | | DockerHub | Harbor / ACR / TCR / SWR | | npm 官方源 | npmmirror | | 公网构建机 | Kubernetes 内部构建环境 |
2. 不要把密码写进 YAML
错误示例:
yaml复制
password:"123456"
正确做法:
yaml复制
passwordRef:harbor_robot_token
所有敏感信息都应该放到 Harness Secret,或者企业统一密钥系统中。
3. 按环境管理发布策略
推荐:
-
dev
:自动部署,可选审批
-
test
:测试人员确认,可选审批
-
pre
:必须审批
-
prod
:必须审批 + 变更单 + 灰度策略
4. 镜像 Tag 要有规范
开发环境:
code复制
<+pipeline.sequenceId>
latest
测试环境:
code复制
<+codebase.commitSha>
生产环境:
code复制
release-日期-流水号
Git Tag
语义化版本号
例如:
code复制
release-20260611-001
v1.3.0
5. Pipeline YAML 要纳入 Git 管理
建议目录:
code复制
harness/
├── pipelines/
│ ├── nodejs-dev.yaml
│ ├── nodejs-test.yaml
│ └── nodejs-prod.yaml
├── inputsets/
│ ├── dev.yaml
│ ├── test.yaml
│ └── prod.yaml
└── templates/
├── nodejs-ci-template.yaml
└── k8s-deploy-template.yaml
后续可以把重复流程沉淀成模板:
-
Node.js 构建模板
-
Java Maven 构建模板
-
Docker Build Push 模板
-
Kubernetes Deploy 模板
-
人工审批模板
-
健康检查模板
二十、本篇验收标准
完成本篇后,你应该能看到下面这些结果:
-
✅ 已创建 Node.js 示例应用
-
✅ 已配置
.npmrc使用国内 npm 镜像 -
✅ 已编写 Dockerfile,并使用 Harbor 基础镜像
-
✅ 已编写 Kubernetes Deployment 和 Service
-
✅ 已创建 CI Build Stage
-
✅ 已完成 npm install
-
✅ 已完成 npm test
-
✅ 已完成 Docker 镜像构建
-
✅ 已推送镜像到 Harbor
-
✅ 已创建 Manual Approval Stage
-
✅ 已创建 Deploy Dev Stage
-
✅ 已部署应用到 Kubernetes dev namespace
-
✅ 已通过
/health健康检查 -
✅ 已理解 Visual Editor 和 YAML 的区别
二十一、总结
这篇文章完成了一条适合国内网络环境的 Harness CI/CD 入门流水线。
你需要重点记住这些结论:
- 国内环境不要默认依赖 GitHub、DockerHub、npm 官方源
- 代码仓库建议使用 GitLab / Gitee
- 镜像仓库建议使用 Harbor / 国内云镜像仓库
- npm 构建建议使用 npmmirror
- Harness SaaS 不直接访问内网资源,必须通过 Delegate
- CI 阶段负责构建和测试
- CD 阶段负责部署和验证
- 单元测试失败必须阻断部署
- 手动审批是企业发布治理的基础
- Visual Editor 适合入门,YAML 适合工程化