1. 这不是“搭个环境”,而是一条从代码提交到服务上线的自动化流水线
你有没有经历过这样的场景:凌晨两点,线上接口突然报500,你抓着头发翻日志,发现是昨天下午三点合并的那个分支漏掉了一个配置文件;或者更糟——测试环境跑得好好的功能,一上生产就崩,排查半天才发现是Jenkins构建时用错了JDK版本,而这个版本号藏在某个被遗忘的shell脚本里。这不是玄学,这是缺乏标准化交付流程的必然代价。今天要说的SpringBoot+Git+Jenkins+K8s容器化部署,本质上不是四个技术名词的简单堆砌,而是一套闭环的、可审计、可回滚、可度量的现代软件交付体系。它把开发写完代码那一刻起,到用户浏览器里看到新功能之间所有手工操作、人为判断、环境差异带来的不确定性,全部压缩进一条自动流动的管道里。Git是这条管道的入口闸门,每一次commit都是触发器;Jenkins是管道中央的智能调度中枢,它读取Git变更、拉取代码、编译打包、运行单元测试、生成镜像、推送仓库;K8s则是管道末端的柔性执行器,它不关心你用什么语言写的,只认Docker镜像和YAML定义,按需拉起Pod、做健康检查、滚动更新、自动扩缩容。SpringBoot在这里扮演的是“最适配容器”的应用框架角色——内嵌Tomcat、启动快、Actuator开箱即用、配置中心天然友好。整套方案的价值,不在于炫技,而在于把“发布”这件事,从一个需要多人协同、反复确认、提心吊胆的高风险事件,变成一个敲下回车键就能完成的日常操作。适合谁?如果你是刚接手运维工作的Java后端,正被频繁的上线需求压得喘不过气;如果你是团队技术负责人,想统一交付标准、降低协作成本;甚至如果你是实习生,想搞懂企业级项目到底怎么跑起来——这套组合拳,就是你绕不开的实战地图。
2. 整体架构设计与技术选型逻辑拆解
2.1 为什么必须是这四件套?缺一不可的底层逻辑
很多人会问:不用Jenkins行不行?用GitHub Actions或GitLab CI不是更轻量?K8s能不能换成Docker Compose?这些疑问背后,是对每个组件不可替代价值的误判。我们来逐层剥开:
Git是唯一可信的单一事实源(Single Source of Truth):它不只是代码仓库,更是整个交付流程的“时间戳发生器”。Jenkins的触发、K8s的镜像Tag、甚至回滚时的版本定位,全部依赖Git Commit ID。任何绕过Git的手动打包、直接上传jar包的行为,都会让整条链路失去可追溯性。我见过太多团队因为“临时改个小bug,直接scp到服务器替换jar”,结果两周后线上出问题,根本无法确定当时跑的是哪个Commit,只能靠猜。
Jenkins的核心价值在于“可控的复杂性”:GitHub Actions确实简洁,但它更适合单仓库、单语言、CI为主的小项目。当你的SpringBoot项目需要同时对接多个Git仓库(比如主应用+公共SDK+配置中心)、需要复杂的多阶段构建(编译→单元测试→集成测试→安全扫描→镜像构建→推送私有Registry)、需要精细的权限控制(开发只能触发测试分支构建,运维才能触发生产部署),Jenkins的Pipeline as Code(Jenkinsfile)和丰富的插件生态(如Docker Pipeline、Kubernetes Plugin、Credentials Binding)提供了无可替代的灵活性。它的“重”,恰恰是应对企业级复杂度的必要重量。
K8s不是Docker的升级版,而是分布式系统的操作系统:Docker Compose解决的是单机多容器编排,而K8s解决的是跨物理机/虚拟机的资源调度、服务发现、弹性伸缩、故障自愈。举个具体例子:你的SpringBoot应用启用了Actuator的
/actuator/health端点,K8s的Liveness Probe会定期调用它。如果应用因内存泄漏卡死,但进程还在,Docker只会认为容器“活着”,而K8s会检测到健康检查失败,自动杀死并重启Pod。这种基于应用语义的治理能力,是Docker Compose永远无法提供的。所谓“K8s和Docker区别”,本质是“单机容器管理”和“云原生基础设施”的维度差异。SpringBoot是容器化落地的“最佳拍档”:它天然符合12-Factor App原则。打包成fat jar后,就是一个完全自包含的二进制文件,没有外部依赖(JRE除外);通过
--spring.profiles.active=prod即可切换环境,无需修改代码;Actuator提供标准的Metrics、Health、Info端点,K8s能直接消费;其默认的/actuator/prometheus端点,让Prometheus监控集成变得极其简单。反观传统WAR包部署在Tomcat里,你需要维护Tomcat版本、配置context.xml、处理类加载器冲突——这些在容器化时代,都是应该被消灭的“技术债”。
2.2 技术栈版本协同:那些踩过的坑,比文档更真实
版本兼容性不是选择题,而是生死线。我亲手部署过37次不同组合,总结出以下铁律:
JDK版本必须与SpringBoot官方支持列表严格对齐:SpringBoot 2.7.x官方支持JDK 8/11/17,但不支持JDK 21。如果你用IDEA创建项目时选了最新JDK 21,又强行用SpringBoot 2.7.x,编译会通过,但运行时
java.lang.UnsupportedClassVersionError会让你怀疑人生。解决方案只有两个:要么降级JDK到17,要么升级SpringBoot到3.2.x(支持JDK 21)。别信网上“加个参数就能跑”的说法,那是拿稳定性开玩笑。Jenkins插件版本必须匹配Jenkins核心版本:Jenkins 2.414 LTS(2023年主流LTS)要求Docker Pipeline插件最低2.29版本。如果你用旧版插件(比如2.26),在Pipeline中使用
docker.build()时会报No signature of method: docker.build() is applicable for argument types。这不是代码错,是API已废弃。查版本兼容性,唯一可靠途径是访问 Jenkins插件官网 ,输入插件名,在“Compatible with Jenkins version”栏看清楚。K8s集群版本与客户端工具(kubectl/helm)的“小版本容忍”原则:K8s官方文档说客户端可以比服务端高/低一个minor版本(如k8s server 1.26.x,kubectl 1.25.x或1.27.x都可用)。但实际中,我遇到过kubectl 1.28.0连接1.26.3集群时,
kubectl get pods -o wide输出的IP列为空,原因是新版kubectl默认启用IPv6解析,而老集群未配置。解决方案是降级kubectl到1.26.5,或在命令中加--v=6看详细日志定位。Git客户端版本影响凭证管理:Windows上Git 2.33+默认使用
manager-core作为凭据助手,而Jenkins的Git插件在某些版本下只识别wincred。导致Jenkins拉取Git时提示Authentication failed。解决方法是在Git Bash中执行git config --global credential.helper manager-core,再在Jenkins Credentials中重新录入Token。
2.3 架构分层图:不是画给老板看的PPT,而是运维手册的索引
整个流水线清晰分为四层,每一层都有明确的输入、输出和责任人:
| 层级 | 组件 | 输入 | 输出 | 主要责任人 | 关键指标 |
|---|---|---|---|---|---|
| 代码层 | Git | 开发者Commit | Commit ID, Branch Name | 开发工程师 | 提交频率、分支存活时间、MR平均审核时长 |
| 构建层 | Jenkins | Git Hook触发 + Commit ID | Docker镜像(含Tag)、构建日志、测试报告 | DevOps工程师 | 构建成功率、平均构建时长、测试覆盖率 |
| 镜像层 | 私有Registry(如Harbor) | Jenkins推送的镜像 | 镜像Digest、镜像扫描报告(Clair/Trivy) | 运维工程师 | 镜像存储大小、漏洞数量(Critical/High)、推送成功率 |
| 运行层 | K8s集群 | Helm Chart / K8s YAML + 镜像Tag | Running Pods、Service Endpoint、Ingress路由 | SRE工程师 | Pod就绪率、服务响应时间(P95)、CPU/Mem使用率 |
提示:这个表格不是摆设。当你某天收到告警“订单服务P95延迟飙升”,你应该立刻按此顺序排查:先看K8s层Pod是否都在Ready状态(
kubectl get pods -n order);若正常,则查Jenkins构建日志,确认最近一次部署的镜像Tag是否正确;再登录Harbor,验证该Tag镜像是否存在且无高危漏洞;最后回溯Git,看对应Commit是否有可疑的代码变更。这就是架构分层赋予你的精准排障路径。
3. 核心细节解析与实操要点
3.1 Git仓库规范化:从“能用”到“可审计”的质变
Git不是装完就完事的工具,它是整个流水线的基石。很多团队失败,根源在于Git使用不规范。
分支策略:Git Flow已死,Trunk-Based Development(TBD)才是王道
Git Flow要求feature分支、develop分支、release分支、hotfix分支……流程复杂,合并冲突频发。而TBD要求所有开发者每天至少向main分支(原master)推送一次代码,每次推送前必须通过自动化测试。好处是什么?Jenkins每次构建的都是最新的main分支,部署的永远是“最接近生产”的代码。我们强制规定:main分支:受保护,仅允许通过Merge Request(MR)合并,且MR必须关联Jira Ticket ID(如PROJ-123);develop分支:废弃,所有功能开发直接在main上进行;release-*分支:废弃,版本发布由Jenkins Pipeline根据Git Tag触发。
Commit信息规范:让日志成为活的文档
禁止出现git commit -m "fix bug"这种描述。我们采用Conventional Commits规范:feat(order): add payment timeout retry logic fix(user): resolve NPE in login controller docs(readme): update deployment steps前缀
feat/fix/docs等,让Jenkins Pipeline能自动解析变更类型。例如,feat开头的Commit,Pipeline会自动触发全量测试;fix开头的,可跳过部分耗时的集成测试,加速反馈。.gitignore必须包含的“隐形杀手”:
很多人只忽略target/、.idea/,却忘了这些:# SpringBoot敏感配置 src/main/resources/application-prod.yml src/main/resources/bootstrap-prod.yml # Jenkins构建产物(避免污染Git) **/target/*.jar **/target/*.war # K8s部署文件(应由CI生成,非手动维护) k8s/deployments/*.yaml k8s/configmaps/*.yaml # IDE生成的临时文件 *.swp *.swo注意:
application-prod.yml这类文件绝不能提交到Git!它们应存放在K8s的Secret或外部配置中心(如Nacos/Apollo)。我曾见过一个团队把数据库密码明文写在application-prod.yml里,还推到了公开仓库,后果可想而知。
3.2 Jenkins Pipeline深度定制:不止于“构建成功”
Jenkinsfile不是脚本,而是交付契约。一个健壮的Pipeline必须覆盖从代码到生产的全生命周期。
pipeline { agent any environment { // 所有环境变量在此统一定义,避免硬编码 APP_NAME = 'order-service' SPRING_PROFILE = 'prod' DOCKER_REGISTRY = 'harbor.example.com' IMAGE_TAG = "${env.GIT_COMMIT.take(7)}-${env.BUILD_ID}" // 使用Commit前7位+构建ID,确保唯一性 } stages { stage('Checkout') { steps { checkout scm // 拉取当前分支代码 script { // 解析Commit信息,决定后续流程 def commitMsg = sh(script: 'git log -1 --pretty=%B', returnStdout: true).trim() if (commitMsg.contains('chore')) { currentBuild.result = 'UNSTABLE' // chore类提交不触发部署 return } } } } stage('Build & Test') { steps { sh "mvn clean compile -Dmaven.test.skip=true" // 先编译,跳过测试(快速失败) sh "mvn test -Dsurefire.skipAfterFailureCount=3" // 运行测试,失败3次后停止 // 生成JaCoCo覆盖率报告 sh "mvn jacoco:report" publishHTML([ allowMissing: false, alwaysLinkToLastBuild: true, keepAll: true, reportDir: 'target/site/jacoco', reportFiles: 'index.html', reportName: 'Code Coverage Report' ]) } } stage('Build Docker Image') { steps { script { // 动态生成Dockerfile(避免手写错误) writeFile file: 'Dockerfile', text: """ FROM openjdk:17-jdk-slim VOLUME /tmp ARG JAR_FILE=target/${APP_NAME}-*.jar COPY \${JAR_FILE} app.jar ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] """ // 构建镜像并推送 docker.build("${DOCKER_REGISTRY}/apps/${APP_NAME}:${IMAGE_TAG}").push() } } } stage('Deploy to K8s') { steps { script { // 使用Helm部署,而非裸YAML(Helm提供版本管理和依赖管理) sh "helm upgrade --install ${APP_NAME} ./helm/charts/${APP_NAME} \\ --namespace production \\ --set image.repository=${DOCKER_REGISTRY}/apps/${APP_NAME} \\ --set image.tag=${IMAGE_TAG} \\ --set springProfile=${SPRING_PROFILE}" } } } } post { success { echo "Pipeline completed successfully for ${APP_NAME}" // 发送企业微信通知 sh "curl -X POST 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx' \ -H 'Content-Type: application/json' \ -d '{\"msgtype\": \"text\", \"text\": {\"content\": \"✅ ${APP_NAME} deployed to production. Commit: ${env.GIT_COMMIT.take(7)}\"}}'" } failure { echo "Pipeline failed!" // 发送告警,附带构建日志URL sh "curl -X POST 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx' \ -H 'Content-Type: application/json' \ -d '{\"msgtype\": \"text\", \"text\": {\"content\": \"❌ ${APP_NAME} deploy failed. Check: ${env.BUILD_URL}console\"}}'" } } }- 关键细节解读:
IMAGE_TAG使用GIT_COMMIT.take(7)而非BUILD_ID,是因为Commit ID是代码的“指纹”,即使Jenkins重建同一构建,镜像Tag也保持一致,便于K8s精准回滚;publishHTML步骤将JaCoCo报告嵌入Jenkins UI,点击即可查看哪一行代码没被测试覆盖;helm upgrade --install命令中的--install参数,确保首次部署时自动创建Release,避免手动helm install;post块中的企业微信通知,是打通“人”与“系统”的最后一环,让团队第一时间感知交付状态。
3.3 K8s部署文件精要:YAML不是配置,而是声明式契约
K8s的YAML文件不是“怎么做的说明书”,而是“最终状态是什么”的声明。写错一个字段,可能导致服务不可用。
Deployment核心字段必填项:
apiVersion: apps/v1 kind: Deployment metadata: name: order-service namespace: production labels: app: order-service spec: replicas: 3 # 必须指定,否则默认为1 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: containers: - name: app image: harbor.example.com/apps/order-service:abc1234-123 # 镜像Tag必须精确 ports: - containerPort: 8080 name: http env: # 环境变量,替代application.yml中的配置 - name: SPRING_PROFILES_ACTIVE value: "prod" - name: SERVER_PORT value: "8080" livenessProbe: # 存活探针,K8s用它判断Pod是否需要重启 httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 60 # 启动后60秒开始探测 periodSeconds: 30 # 每30秒探测一次 readinessProbe: # 就绪探针,K8s用它判断Pod是否可接收流量 httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 30 periodSeconds: 10 resources: # 资源限制,防止Pod吃光节点资源 requests: memory: "512Mi" cpu: "200m" limits: memory: "1Gi" cpu: "500m" imagePullSecrets: # 拉取私有Registry镜像所需的Secret - name: harbor-registry-secret为什么
livenessProbe和readinessProbe必须分开?livenessProbe失败,K8s会杀死Pod并新建一个;readinessProbe失败,K8s会将Pod从Service的Endpoint列表中移除,但Pod本身继续运行。举个例子:你的SpringBoot应用启动后需要加载缓存,前10秒内/actuator/health/readiness返回DOWN,此时readinessProbe失败,流量不会打进来,但livenessProbe仍在等待,直到缓存加载完成,readinessProbe变UP,流量才接入。如果只配livenessProbe,应用可能在加载缓存时被反复杀死,永远无法就绪。ConfigMap与Secret的正确用法:
application-prod.yml中的配置,应拆分成ConfigMap(非敏感)和Secret(敏感):# 创建ConfigMap(数据库连接池参数等) kubectl create configmap order-config \ --from-file=application-prod.yml \ --namespace=production # 创建Secret(数据库密码) kubectl create secret generic order-secret \ --from-literal=DB_PASSWORD='My$tr0ngP@ssw0rd' \ --namespace=production在Deployment中挂载:
envFrom: - configMapRef: name: order-config - secretRef: name: order-secret
4. 实操过程与核心环节实现
4.1 环境准备:从零开始搭建最小可行集群
我们不追求“完美集群”,而是搭建一个能跑通全流程的最小环境。所有操作均在Ubuntu 22.04 LTS上验证。
Step 1:安装Docker(K8s的基石)
# 卸载旧版本 sudo apt-get remove docker docker-engine docker.io containerd runc # 安装依赖 sudo apt-get update sudo apt-get install -y \ ca-certificates \ curl \ gnupg \ lsb-release # 添加Docker官方GPG密钥 sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 设置稳定版仓库 echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # 安装Docker Engine sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 验证 sudo docker run hello-worldStep 2:安装K3s(轻量级K8s,适合学习和测试)
K3s是Rancher出品的CNCF认证K8s发行版,内存占用<512MB,一条命令即可安装:# 安装K3s(自动配置kubectl) curl -sfL https://get.k3s.io | sh - # 查看集群状态 sudo k3s kubectl get nodes sudo k3s kubectl get pods -A # 配置kubectl命令别名(省去sudo) sudo cp /var/lib/rancher/k3s/server/bin/kubectl /usr/local/bin/kubectl sudo chmod +x /usr/local/bin/kubectl export KUBECONFIG=/etc/rancher/k3s/k3s.yamlStep 3:安装Jenkins(使用Docker方式,避免环境污染)
# 创建Jenkins数据目录 sudo mkdir -p /opt/jenkins_home # 运行Jenkins容器(映射8080端口,挂载数据卷) sudo docker run -d \ --name jenkins \ -p 8080:8080 \ -p 50000:50000 \ -v /opt/jenkins_home:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ # 关键!让Jenkins能调用宿主机Docker -v $(which docker):/usr/bin/docker \ # 同样关键 jenkins/jenkins:lts-jdk11 # 获取初始密码 sudo cat /opt/jenkins_home/secrets/initialAdminPasswordStep 4:配置Jenkins插件(按需安装,非全量)
登录Jenkins Web UI(http://localhost:8080),进入Manage Jenkins→Plugin Manager→Available,搜索并安装:Docker Pipeline(用于在Pipeline中操作Docker)Kubernetes Plugin(用于动态创建Jenkins Agent)Git Parameter Plugin(支持在构建时选择Git分支)Blue Ocean(现代化UI,Pipeline可视化)
注意:安装插件后必须重启Jenkins。不要贪多安装,每个插件都可能引入兼容性问题。
4.2 SpringBoot应用改造:为容器化而生
一个未经改造的SpringBoot项目,直接扔进K8s大概率会失败。以下是必须做的5项改造:
改造1:移除内嵌Tomcat,使用Undertow(更轻量)
在pom.xml中:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>改造2:禁用Spring Boot DevTools(生产环境禁止)
pom.xml中:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency>并在
application-prod.yml中添加:spring: devtools: restart: enabled: false livereload: enabled: false改造3:配置Actuator端点暴露
application-prod.yml:management: endpoints: web: exposure: include: health,info,metrics,prometheus,loggers,threaddump endpoint: health: show-details: when_authorized改造4:日志输出重定向到stdout
默认Logback配置会输出到logs/目录,但K8s要求日志输出到stdout/stderr。在src/main/resources/logback-spring.xml中:<configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${CONSOLE_LOG_PATTERN}</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="STDOUT"/> </root> </configuration>改造5:增加健康检查端点(适配K8s Probe)
创建HealthCheckController.java:@RestController public class HealthCheckController { @GetMapping("/health/live") public ResponseEntity<Map<String, Object>> liveness() { Map<String, Object> result = new HashMap<>(); result.put("status", "UP"); result.put("timestamp", System.currentTimeMillis()); return ResponseEntity.ok(result); } @GetMapping("/health/ready") public ResponseEntity<Map<String, Object>> readiness() { // 这里可加入DB连接检查、Redis连接检查等 Map<String, Object> result = new HashMap<>(); result.put("status", "UP"); result.put("timestamp", System.currentTimeMillis()); return ResponseEntity.ok(result); } }对应的K8s Probe路径改为
/health/live和/health/ready。
4.3 Jenkins Pipeline实战:从代码提交到服务上线
我们以一个真实的订单服务为例,完整走一遍流程。
Step 1:在Git仓库根目录创建Jenkinsfile
内容见3.2节,此处不再重复。重点是environment块中DOCKER_REGISTRY要指向你自己的Harbor地址。Step 2:在Jenkins中创建Pipeline项目
- 新建任务 → 输入名称(如
order-service-pipeline)→ 选择Pipeline→OK; - 在
Pipeline配置页,Definition选择Pipeline script from SCM; SCM选择Git,填写仓库URL(如https://gitlab.example.com/team/order-service.git);Script Path填写Jenkinsfile(默认就在根目录);Branches to build填写*/main;- 保存。
- 新建任务 → 输入名称(如
Step 3:配置Git凭证(让Jenkins能拉取代码)
- 进入
Manage Jenkins→Credentials→System→Global credentials→Add Credentials; Kind选择Username with password;Username填Git账号(如gitlab-ci);Password填Personal Access Token(GitLab中生成,权限勾选read_repository);ID填一个易记的名字(如gitlab-token);- 在Pipeline配置中,
SCM下方的Credentials下拉框,选择刚创建的gitlab-token。
- 进入
Step 4:首次手动触发构建
- 进入项目页面 →
Build Now; - 观察
Console Output:Checkout阶段应显示Cloning repository...;Build & Test阶段应显示[INFO] BUILD SUCCESS;Build Docker Image阶段应显示Successfully built xxx和The push refers to repository [harbor.example.com/apps/order-service];Deploy to K8s阶段应显示Release "order-service" has been upgraded。
- 进入项目页面 →
Step 5:验证K8s部署结果
# 查看Pod状态 kubectl get pods -n production -l app=order-service # 查看Service kubectl get svc -n production order-service # 端口转发到本地,测试接口 kubectl port-forward svc/order-service 8080:8080 -n production & curl http://localhost:8080/actuator/health # 应返回 {"status":"UP"}Step 6:模拟一次Git Push触发自动构建
在本地修改代码,例如在HealthCheckController中加一行日志:@GetMapping("/health/live") public ResponseEntity<Map<String, Object>> liveness() { log.info("Liveness check triggered"); // 新增 // ... rest of code }提交并Push:
git add . git commit -m "feat(health): add log to liveness check" git push origin main切换到Jenkins页面,几秒后会看到一个新的构建任务自动创建并开始执行。这就是Git Hook的魔力。
5. 常见问题与排查技巧实录
5.1 Jenkins构建失败:从日志中挖出真相
Jenkins构建失败是常态,关键是如何高效定位。记住:日志是唯一的真相来源。
问题1:
Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile
表面是编译失败,但根源可能是JDK版本不匹配。
排查步骤:- 进入Jenkins →
Manage Jenkins→System Information,搜索java.version,确认Jenkins运行的JDK版本; - 查看
pom.xml中<java.version>是否与之匹配; - 如果不匹配,在Jenkins全局工具配置中,添加正确的JDK(
Manage Jenkins→Global Tool Configuration→JDK installations),并在Pipeline中指定:tools { jdk 'jdk-17' }
- 进入Jenkins →
问题2:
docker: command not found
这是Jenkins容器无法调用宿主机Docker的典型症状。
根本原因:Docker Socket未正确挂载,或权限不足。
解决方案:- 确认启动Jenkins容器时,
-v /var/run/docker.sock:/var/run/docker.sock参数存在; - 在Jenkins容器内执行
ls -l /var/run/docker.sock,确认权限为srw-rw----,且所属组为docker; - 将Jenkins容器的
jenkins用户加入docker组(需在容器内执行):# 进入Jenkins容器 sudo docker exec -it jenkins bash # 添加用户到docker组 usermod -aG docker jenkins # 退出并重启容器 exit sudo docker restart jenkins
- 确认启动Jenkins容器时,
问题3:
Unable to connect to the server: x509: certificate signed by unknown authority
Jenkins使用Kubernetes Plugin连接K3s集群时常见。
原因:K3s的CA证书未被Jenkins信任。
解决:- 在K3s服务器上,获取CA证书:
sudo cat /etc/rancher/k3s/k3s.yaml | grep "certificate-authority-data:" | awk '{print $2}' | base64 -d > ca.crt - 将
ca.crt内容复制到Jenkins的Kubernetes Cloud配置中,Kubernetes server certificate key字段粘贴。
- 在K3s服务器上,获取CA证书:
5.2 K8s部署异常:Pod卡在Pending/ContainerCreating
K8s的抽象层级很高,错误信息往往很隐晦。学会看Events是基本功。
问题1:Pod状态为
Pending,kubectl describe pod显示0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/control-plane: }. No scheduleable nodes found.
解读:K3s默认将Master节点打上control-plane污点,禁止调度普通Pod。
解决:# 查看节点污点 kubectl describe node | grep Taints # 移除污点(仅限单节点测试环境) kubectl taint node $(hostname) node-role.kubernetes.io/control-plane:NoSchedule-问题2:Pod状态为
ContainerCreating,kubectl describe pod显示Back-off pulling image "harbor.example.com/apps/order-service:abc1234-123"
解读:K8s无法从Harbor拉取镜像。
排查链路:- 确认Harbor服务是否运行:
curl -I http://harbor.example.com; - 确认镜像是否存在:登录Harbor Web UI,搜索
order-service,看Tagabc1234-123是否存在; - 确认
imagePullSecrets是否正确:kubectl get secret harbor-registry-secret -n production -o yaml,检查data字段是否为Base64编码的{"auths":{...}}; - 在K8s节点上手动拉取镜像测试:
sudo docker pull harbor.example.com/apps/order-service:abc1234-123,看是否
- 确认Harbor服务是否运行: