环境说明Kubernetes (v1.28) 集群Harbor 私有镜像仓库Jenkins 持续集成Gitee 代码仓库使用硬性 Pod 反亲和 requiredDuringSchedulingIgnoredDuringExecutionGit 使用 SSH 协议拉取代码。 整体链路开发提交代码 (Gitee) → Jenkins 拉取源码编译构建镜像 → 推送镜像至 Harbor 私有仓库 → Jenkins 调用 kubectl 部署至 K8s → Ingress 对外提供访问前置准备清单先确认环境Harbor 已经部署完成可网页访问Jenkins 服务器已经安装 maven、nerdctl (containerd)jenkins 服务器已经配置 kubeconfig可以执行 kubectl 访问 k8s 集群K8s 集群 ingress‑nginx‑controller 正常运行准备业务代码工程。第一部分Gitee‑SSH 密钥配置仓库访问1、Jenkins 服务器生成 ssh 密钥无密码在 Jenkins 机器执行ssh-keygen -t ed25519 -N -f ~/.ssh/id_ed25519-t ed25519现代加密算法-N 密钥不设置密码Jenkins 必须无密码否则构建卡住 查看公钥cat ~/.ssh/id_ed25519.pub复制全部输出内容公钥2、Gitee 网页端添加 SSH 公钥Gitee 网页右上角头像 → 设置 →安全设置 → SSH 公钥标题填写jenkins‑deploy粘贴刚刚复制的公钥保存。3、复制私钥文本用于 Jenkins 网页凭据cat ~/.ssh/id_ed25519⚠️完整复制全部内容包含-----BEGIN OPENSSH PRIVATE KEY-----一直到结束行不要上传私钥到 gitee私钥只给 Jenkins 网页保存4、Jenkins 网页创建 SSH 凭据管理 Jenkins → 凭据 → 系统 → 全局凭据 → 添加凭据凭据类型SSH Username with private keyUsernamegit固定gitee ssh 用户名必须写 git不要写 rootPrivate key粘贴上面复制完整私钥IDgitee‑ssh‑jenkins自定义便于识别 保存。测试连通性可选jenkins 服务器上 root 用户测试ssh -T gitgitee.com出现欢迎提示代表 ssh 连通 gitee 正常。第二部分Harbor 端准备1、Harbor 网页登录新建私有项目Harbor 网页 → 新建项目项目名称例如api‑business访问级别设置私有❗后面镜像推送地址就是harbor.devops.local/api‑business/业务镜像名:版本号⚠️Jenkins 服务器、所有 k8s 节点的 containerd 必须信任 harbor 的 https 证书如果是自签证书需要部署 ca 证书否则 nerdctl push 镜像报错2、记录 harbor 账号密码后续流水线推送镜像使用在 Jenkins 新建另一个凭据类型Username with passwordIDharbor‑auth填写 harbor 登录用户名、密码用于流水线登录 harbor 推送镜像。第三部分业务工程编写 3 个核心文件Dockerfile、deployment.yaml、Jenkinsfile文件存放位置在业务工程内提交到 gitee v4.0 分支业务工程根目录 ├── Dockerfile ├── Jenkinsfile └── k8s └── deployment.yaml └── service.yaml └── ingress.yaml① DockerfileJava 后端示例根据实际业务调整注意基础镜像版本jar 包输出路径暴露端口不要用 root 运行尽量。FROM openjdk:21-jdk-slim WORKDIR /app COPY target/*.jar app.jar EXPOSE 8080 ENTRYPOINT [java,-jar,app.jar]⚠️注意事项maven 编译输出 jar 路径要和 COPY 保持一致生产环境尽量不要使用 latest 标签镜像版本由 Jenkinsfile 的 BUILD_NUMBER 自动生成。② k8s/deployment.yaml 硬性 Pod 反亲和配置❗预留镜像占位符__IMAGE_TAG__流水线 sed 替换硬性反亲和不要写 weight 字段apiVersion: apps/v1 kind: Deployment metadata: name: authingcenter namespace: data-platform spec: replicas: 3 #内部系统副本用1个就行 revisionHistoryLimit: 2 #算上当前版本一共保留2个版本 selector: matchLabels: app: authingcenter template: metadata: labels: app: authingcenter spec: containers: - name: authingcenter image: __IMAGE__ # 此处占位符流水线sed替换镜像地址 ports: - containerPort: 9001 # resources: # requests: # cpu: 100m # memory: 256Mi # limits: # cpu: 500m # memory: 512Mi imagePullSecrets: - name: harbor-secret affinity: podAntiAffinity: # 前缀硬反亲和 (required) 软反亲和 (preferred), 软反亲和配置和下面的还有些差异需要加权重 requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - authingcenter topologyKey: kubernetes.io/hostname --- apiVersion: v1 kind: Service metadata: name: authingcenter-svc namespace: data-platform spec: selector: app: authingcenter ports: - port: 80 targetPort: 9001 type: ClusterIP --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: authingcenter-ingress namespace: data-platform spec: ingressClassName: nginx rules: - http: # 删除host字段不绑定域名依靠URL前缀区分业务 paths: - path: /authing pathType: Prefix backend: service: name: authingcenter-svc port: number: 80 #spec: # ingressClassName: nginx # rules: # - host: authingcenter.devops.local # http: # paths: # - path: / # pathType: Prefix # backend: # service: # name: authingcenter-svc # port: # number: 80反亲和注意required硬性副本数不能大于集群可用节点数量否则 Pod 直接 Pending 调度失败硬性反亲和不允许写 weight 字段weight 只属于 preferred 软性优先不要嵌套 podAffinityTerm。同时准备 service.yaml、ingress.yaml放在 k8s 目录ingress 绑定业务域名指向 service。或者向上面的配置信息将service.yaml、ingress.yaml融合进deployment.yaml文件中注意三部分的分隔符【---】③ Jenkinsfile存放在工程根目录gitee 仓库内采用 SCM 流水线模式不在 jenkins 页面写脚本pipeline { agent any environment { // harbor镜像完整地址BUILD_NUMBER是jenkins内置构建编号 IMAGE_NAME harbor.devops.local/api-authingcenter/authingcenter:${BUILD_NUMBER} HARBOR_CREDS credentials(harbor-auth) } stages { stage(Maven编译打包) { steps { sh # 在仓库根目录编译全部模块install到本地maven仓库 /opt/apache-maven/maven-3.9.6/bin/mvn clean install -DskipTests } } stage(构建镜像推送Harbor) { steps { sh cd AuthingCenter nerdctl login harbor.devops.local -u ${HARBOR_CREDS_USR} -p ${HARBOR_CREDS_PSW} nerdctl build -t ${IMAGE_NAME} -f ./Dockerfile . nerdctl push ${IMAGE_NAME} } } stage(部署到K8s集群) { steps { sh cd AuthingCenter sed -i s#__IMAGE__#${IMAGE_NAME}#g k8s/deployment.yaml kubectl apply -f k8s/deployment.yaml } } } post { success { echo AuthingCenter子模块流水线执行完毕部署到命名空间 data‑platform } } }⚠️Jenkinsfile 注意事项credentialsId 必须和 jenkins 网页 harbor 凭据 ID 完全一致sed 占位符前后符号#避免镜像地址里面斜杠造成 sed 语法报错kubectl apply -f k8s/会一次性加载目录下面 deployment/service/ingress 全部 yamlJenkins 执行 kubectl 依赖 jenkins 服务器上的/.kube/config务必提前拷贝集群 kubeconfig。提交文件到 Gitee 仓库 v4.0 分支git add . git commit -m 初始化部署文件 git push origin v4.0第四部分Jenkins 上创建流水线项目Jenkins 首页 → 新建任务输入任务名字authingcenter‑deploy选择流水线 (Pipeline)确定进入配置页面。流水线部分选择Pipeline script from SCM从源代码管理读取 Jenkinsfile重点SCM 选择 GitRepository URL 填写 ssh 仓库地址gitgitee.com:cg‑family‑admin/data_platform.git凭据下拉选择前面建好的gitee‑ssh‑jenkinsssh 凭据分支*/v4.0Script Path填写工程内 Jenkinsfile 实际路径例如 AuthingCenter/Jenkinsfile大小写严格匹配仓库目录保存配置。第五部分执行构建发布页面点击【Build Now】开始流水线构建点开构建 #号 → Console Output 观察日志正常流水线阶段顺序① maven 编译打包② nerdctl 构建 推送镜像到 harbor③ sed 替换 deployment 镜像占位符④ kubectl apply 部署 k8s 资源。构建完成之后验证步骤发布完必须做在 k8s 集群查看 pod 状态kubectl get pods -n data-platform kubectl get svc,ingress -n data-platform访问 ingress 域名验证业务接口Harbor 网页确认对应项目下已经生成对应版本镜像版本号就是 jenkins 构建号 BUILD_NUMBER。常见故障排查清单踩坑点汇总Gitee 拉取代码失败ssh 密钥问题确认凭据用户名是 git私钥完整公钥已经上传 gitee不要使用服务器磁盘密钥直接读取。nerdctl push 镜像报错 harbor 证书不信任所有节点jenkinsk8s部署 harbor 自签 ca 证书到 containerd 信任目录。k8s deployment yaml 解析报错亲和性字段错误required 硬性反亲和禁止 weight 字段禁止 podAffinityTerm 嵌套preferred 软性反亲和必须 weight podAffinityTerm 嵌套。Jenkins 执行 kubectl 报错权限jenkins 服务器缺少 kubeconfig复制集群 admin 的.kube/config 到 jenkins 服务器 jenkins 用户家目录。Script‑Path 路径写错提示找不到 Jenkinsfile严格对照 gitee 仓库里面的目录大小写。硬性 pod 反亲和导致 pod pending副本数大于集群可用节点数量减少 replicas 副本或者改成 preferred 软性反亲和。sed 镜像替换失败检查 deployment.yaml 占位符__IMAGE_TAG__书写完全匹配 Jenkinsfile 里面 sed 脚本。后续迭代发布流程日常上线开发修改业务代码提交推送 v4.0 分支在 Jenkins 页面直接点击 Build Now 即可完成新版本发布镜像版本号自动使用 jenkins 构建自增编号。如果需要配置 webhook 自动触发构建可以后续再配置 Gitee 的 webhook 回调 Jenkins实现提交代码自动发布。