《企业级K8s容器化部署方案:Jenkins+Harbor+Helm+Gitee+Jenkinsfile自动化实践》

企业级 K8s 容器化部署方案:Jenkins + Harbor + Helm/kubectl + Gitee + Jenkinsfile 自动化实践

本文档基于 jenkinsfileTest/cigroovy.jenkinsfile 及共享库 jenkinslib 编写,描述 Java 微服务从 Maven/Gradle 编译、Docker 镜像推送 Harbor,到 K8s 集群部署(kubectl 动态模板 / Helm Chart)、初始化与服务更新、版本回滚的完整 CI/CD 流程。


一、方案架构

1.1 组件说明

组件 角色 说明
Gitee 代码仓库 SSH 拉取,含 Dockerfile
Jenkins (K8s Agent Pod) CI/CD mvn/docker/kubectl/helm 容器内执行
Harbor 镜像 / Chart 仓库 Docker 镜像;Helm 模式存 Chart
Kubernetes 运行环境 Deployment / DaemonSet / StatefulSet
SkyWalking 链路追踪 skywalking 模板 initContainer 注入 Agent
jenkinslib 共享库 构建、Harbor、模板、K8s 部署、公共流水线

1.2 两条 CD 路径

路径 deployMethod 说明
kubectl(默认) kubectl Jenkins 动态生成 YAML → kubectl apply
Helm helm 仓库 helm/ Chart → helm upgrade --install

构建阶段(CheckOut → Build → 镜像推送 Harbor)相同,差异在部署阶段。

1.3 流水线与共享库

文件 说明
cigroovy.jenkinsfile K8s CI/CD 入口
vars/k8s.groovy + k8s.groovy Slave Pod(mvn/docker/kubectl/helm)
pipeline.groovy checkout、发布门禁
build.groovy compile(mvn/gradle)
Harbor.groovy docker build/push(校验 Dockerfile)
template.groovy K8s YAML 模板生成
k8sdeploy.groovy kubectl / Helm 部署与回滚
resources/k8s-skywalking-*.yaml SkyWalking 通用微服务模板
resources/k8s-basic-*.yaml 基础精简模板

二、发布模式(publishMode)

publishMode 行为
自动发布(默认) 镜像推送后自动部署到 K8s
手动发布 input 确认后部署
仅构建 仅编译 + 推镜像,不生成 YAML、不部署
回滚 kubectl rollout undo 或 Helm 回滚

#mermaid-svg-eBjFu7mng7jKFcdT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eBjFu7mng7jKFcdT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eBjFu7mng7jKFcdT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eBjFu7mng7jKFcdT .error-icon{fill:#552222;}#mermaid-svg-eBjFu7mng7jKFcdT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eBjFu7mng7jKFcdT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eBjFu7mng7jKFcdT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eBjFu7mng7jKFcdT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eBjFu7mng7jKFcdT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eBjFu7mng7jKFcdT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eBjFu7mng7jKFcdT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eBjFu7mng7jKFcdT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eBjFu7mng7jKFcdT .marker.cross{stroke:#333333;}#mermaid-svg-eBjFu7mng7jKFcdT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eBjFu7mng7jKFcdT p{margin:0;}#mermaid-svg-eBjFu7mng7jKFcdT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eBjFu7mng7jKFcdT .cluster-label text{fill:#333;}#mermaid-svg-eBjFu7mng7jKFcdT .cluster-label span{color:#333;}#mermaid-svg-eBjFu7mng7jKFcdT .cluster-label span p{background-color:transparent;}#mermaid-svg-eBjFu7mng7jKFcdT .label text,#mermaid-svg-eBjFu7mng7jKFcdT span{fill:#333;color:#333;}#mermaid-svg-eBjFu7mng7jKFcdT .node rect,#mermaid-svg-eBjFu7mng7jKFcdT .node circle,#mermaid-svg-eBjFu7mng7jKFcdT .node ellipse,#mermaid-svg-eBjFu7mng7jKFcdT .node polygon,#mermaid-svg-eBjFu7mng7jKFcdT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eBjFu7mng7jKFcdT .rough-node .label text,#mermaid-svg-eBjFu7mng7jKFcdT .node .label text,#mermaid-svg-eBjFu7mng7jKFcdT .image-shape .label,#mermaid-svg-eBjFu7mng7jKFcdT .icon-shape .label{text-anchor:middle;}#mermaid-svg-eBjFu7mng7jKFcdT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eBjFu7mng7jKFcdT .rough-node .label,#mermaid-svg-eBjFu7mng7jKFcdT .node .label,#mermaid-svg-eBjFu7mng7jKFcdT .image-shape .label,#mermaid-svg-eBjFu7mng7jKFcdT .icon-shape .label{text-align:center;}#mermaid-svg-eBjFu7mng7jKFcdT .node.clickable{cursor:pointer;}#mermaid-svg-eBjFu7mng7jKFcdT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eBjFu7mng7jKFcdT .arrowheadPath{fill:#333333;}#mermaid-svg-eBjFu7mng7jKFcdT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eBjFu7mng7jKFcdT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eBjFu7mng7jKFcdT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eBjFu7mng7jKFcdT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eBjFu7mng7jKFcdT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eBjFu7mng7jKFcdT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eBjFu7mng7jKFcdT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eBjFu7mng7jKFcdT .cluster text{fill:#333;}#mermaid-svg-eBjFu7mng7jKFcdT .cluster span{color:#333;}#mermaid-svg-eBjFu7mng7jKFcdT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eBjFu7mng7jKFcdT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eBjFu7mng7jKFcdT rect.text{fill:none;stroke-width:0;}#mermaid-svg-eBjFu7mng7jKFcdT .icon-shape,#mermaid-svg-eBjFu7mng7jKFcdT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eBjFu7mng7jKFcdT .icon-shape p,#mermaid-svg-eBjFu7mng7jKFcdT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eBjFu7mng7jKFcdT .icon-shape .label rect,#mermaid-svg-eBjFu7mng7jKFcdT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eBjFu7mng7jKFcdT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eBjFu7mng7jKFcdT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eBjFu7mng7jKFcdT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 回滚
其他
仅构建
自动/手动
kubectl
helm
publishMode
回滚阶段
CheckOut → Build
镜像构建并推送 Harbor
publishMode
结束
deployMethod
生成部署模板
跳过模板
发布到K8s
post 邮件


三、时序图

3.1 kubectl 自动发布

k8sdeploy template Harbor build Jenkins Pod k8sdeploy template Harbor build Jenkins Pod #mermaid-svg-enYvI2GvxJmeu7KN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-enYvI2GvxJmeu7KN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-enYvI2GvxJmeu7KN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-enYvI2GvxJmeu7KN .error-icon{fill:#552222;}#mermaid-svg-enYvI2GvxJmeu7KN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-enYvI2GvxJmeu7KN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-enYvI2GvxJmeu7KN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-enYvI2GvxJmeu7KN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-enYvI2GvxJmeu7KN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-enYvI2GvxJmeu7KN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-enYvI2GvxJmeu7KN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-enYvI2GvxJmeu7KN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-enYvI2GvxJmeu7KN .marker.cross{stroke:#333333;}#mermaid-svg-enYvI2GvxJmeu7KN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-enYvI2GvxJmeu7KN p{margin:0;}#mermaid-svg-enYvI2GvxJmeu7KN .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-enYvI2GvxJmeu7KN text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-enYvI2GvxJmeu7KN .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-enYvI2GvxJmeu7KN .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-enYvI2GvxJmeu7KN .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-enYvI2GvxJmeu7KN .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-enYvI2GvxJmeu7KN #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-enYvI2GvxJmeu7KN .sequenceNumber{fill:white;}#mermaid-svg-enYvI2GvxJmeu7KN #sequencenumber{fill:#333;}#mermaid-svg-enYvI2GvxJmeu7KN #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-enYvI2GvxJmeu7KN .messageText{fill:#333;stroke:none;}#mermaid-svg-enYvI2GvxJmeu7KN .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-enYvI2GvxJmeu7KN .labelText,#mermaid-svg-enYvI2GvxJmeu7KN .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-enYvI2GvxJmeu7KN .loopText,#mermaid-svg-enYvI2GvxJmeu7KN .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-enYvI2GvxJmeu7KN .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-enYvI2GvxJmeu7KN .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-enYvI2GvxJmeu7KN .noteText,#mermaid-svg-enYvI2GvxJmeu7KN .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-enYvI2GvxJmeu7KN .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-enYvI2GvxJmeu7KN .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-enYvI2GvxJmeu7KN .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-enYvI2GvxJmeu7KN .actorPopupMenu{position:absolute;}#mermaid-svg-enYvI2GvxJmeu7KN .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-enYvI2GvxJmeu7KN .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-enYvI2GvxJmeu7KN .actor-man circle,#mermaid-svg-enYvI2GvxJmeu7KN line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-enYvI2GvxJmeu7KN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} compile(mvn/gradle)1BuildImage + push2generateWorkloadTemplate + Service(init时)3Deploy(kubectl apply + rollout)4

3.2 Helm 发布

  • 跳过「生成部署模板」
  • PackageAndPushCharthelm upgrade --install --atomic --wait
  • 注意 :Helm 模式下 Jenkins 侧 deployConfigType/Template 参数不生效,由仓库 Chart 决定

3.3 初始化部署 vs 服务更新

deployAction 生成 Service kubectl apply
初始化部署 工作负载 + Service
服务更新 仅工作负载

四、部署配置

4.1 部署配置类型(deployConfigType)

参数值 输出文件 K8s 资源
使用deployment模板 deployment.yaml Deployment
使用daemonset模板 daemonset.yaml DaemonSet
使用statefulset模板 statefulset.yaml StatefulSet

4.2 部署配置模板(deployConfigTemplate)

参数值 资源前缀 特性
通用微服务含skywalking k8s-skywalking-* initContainer Agent、探针、JAVA_OPTS/APOLLO_OPTS
基础模板 k8s-basic-* 精简,无 SkyWalking/探针

占位符:{``{PRONAME}} {``{FULL_IMAGE_NAME}} {``{NAMESPACE}} {``{REPLICAS}} {``{CONTAINER_PORT}} 及 CPU/Memory、Apollo、SkyWalking 等。


五、环境准备

5.1 Jenkins K8s 云

Slave Pod 容器:mvn、docker(挂载 docker.sock)、kubectl、helm。

5.2 Harbor

text 复制代码
镜像:{HARBOR_URL}/{PROJECT_GROUP}/{imageName}:{imageTag}_{Tenv}_{BUILD_TIME}
示例:harbor.jdicity.local/registry/order-service:v1.0_prod_20250618_143022

5.3 K8s 凭据(config.K8S_CLUSTER_CREDENTIALS)

Tenv 默认凭据 ID
prod k8s-prod-config
test k8s-test-config
dev k8s-dev-config

可用 K8S_CREDENTIAL_ID 覆盖。

5.4 集群前置条件

  • Namespace 已创建
  • registry-secret imagePullSecrets
  • SkyWalking 模板:OAP、Apollo Meta 集群内可达
  • 应用暴露 /actuator/health/*(skywalking 模板探针)

5.5 Helm 额外要求

  • 仓库根目录 helm/Chart.yaml
  • Harbor Chart 仓库已启用

六、Jenkins 参数

6.1 基础参数

参数名 说明
Tenv dev/test/prod(邮件标题含环境)
publishMode 必配:自动发布/手动发布/仅构建/回滚
SrcURL / branchName 仓库与分支
buildType mvn / gradle
buildshell 如 clean package -DskipTests
imageName / imageTag 镜像名与 tag 前缀
k8s_ns Namespace
replicas / ContainerPort 副本数、端口
waitMins / emailUser 手动超时、邮件

6.2 部署参数

参数名 说明
deployMethod kubectl(默认)/ helm
deployAction 初始化部署 / 服务更新
deployConfigType Deployment/DaemonSet/StatefulSet 模板
deployConfigTemplate skywalking / 基础模板

6.3 SkyWalking 模板资源参数

containerCpuLimitscontainerMemoryLimitscontainerCpuRequestscontainerMemoryRequestsAPOLLO_*skywalkingAgentImageskywalkingCollectorJAVA_OPTSAPOLLO_OPTS

6.4 环境变量

变量 默认
HARBOR_URL harbor.jdicity.local
PROJECT_GROUP registry
K8S_CREDENTIAL_ID 覆盖 kubeconfig

七、流水线阶段

阶段 条件 说明
CheckOut publishMode ≠ 回滚 pipeline.checkoutCode
Build 同上 container(mvn) → build.compile
镜像构建并推送 同上 打 tag + harbor.BuildImage(含 Dockerfile 校验)
生成部署模板 auto/manual 且 kubectl template.generateWorkload/Service
发布到K8s auto/manual pipeline.runPublish → k8sdeploy.RunDeploy
回滚 rollback kubectl Rollback 或 HelmRollback
post always sendPost(notifyCtx 含 tenv)

八、Helm 在 CI/CD 中的作用

能力 说明
Chart 版本化 helm package,BUILD_NUMBER 为版本,推 Harbor
Release 管理 helm upgrade --install {imageName}-{Tenv}
参数化 --set image.repository/tag/replicas/containerPort
原子部署 --atomic --wait
回滚 helm history + upgrade --reuse-values

kubectl vs Helm 选型:

  • 动态 SkyWalking YAML、多工作负载类型 → kubectl
  • 稳定 Chart、Release 级管理 → Helm

九、共享库 API

pipeline.groovy

同前后端:checkoutCoderunPublishselectRollbackVersion

k8sdeploy.groovy

方法 说明
RunDeploy(config) 按 deployMethod 分发
Deploy(...) kubectl apply + rollout(含 DaemonSet)
PackageAndPushChart / HelmDeploy Helm 路径
Rollback / HelmRollback 回滚

build.groovy

groovy 复制代码
build.compile(buildType, buildshell)  // mvn / gradle 统一入口

十、典型场景

场景 publishMode 其他
首次 SkyWalking 上线 自动发布 deployAction=初始化部署,deployConfigTemplate=skywalking
日常发版 自动发布 deployAction=服务更新
只打镜像不部署 仅构建 ---
DaemonSet 日志 agent 自动发布 deployConfigType=daemonset
Helm 发版 自动发布 deployMethod=helm,仓库含 helm/
kubectl 回滚 回滚 deployMethod=kubectl

十一、排错

现象 处理
Dockerfile 不存在 仓库根目录添加 Dockerfile
Gradle 构建失败 buildType=gradle,Build 已统一支持
libraryResource 失败 确认 jenkinslib/resources/k8s-*.yaml 已发布
探针失败 改用基础模板或调整 actuator 路径
Helm 参数不生效 deployConfigType 仅 kubectl 有效
仅构建仍部署 publishMode 应为仅构建
邮件无环境 已修复:notifyCtx.tenv

十二、快速检查清单

  • K8s 云 kubernetes 已配置,Slave Pod 正常
  • 凭据 HARBOR_ID、环境 kubeconfig 已创建
  • publishMode、deployMethod 已配置
  • kubectl 路径:SkyWalking OAP、registry-secret 就绪
  • helm 路径:helm/Chart.yaml 存在
  • SMTP、emailUser 已配置

附录:DevOps 三条路径对照

维度 前端 后端 K8s
Jenkinsfile front.jenkinsfile backend.jenkinsfile cigroovy.jenkinsfile
制品 tar.gz JAR Docker 镜像
仓库 MinIO MinIO Harbor
部署 SSH 解压 SSH + launch.sh kubectl / Helm
保留策略 artifactRetainCount MinIO + 目标机 JAR ---
发布模式 publishMode publishMode publishMode
公共库 pipeline.groovy pipeline.groovy pipeline.groovy