Skip to content

03 Jenkins 高可用部署

3.1 高可用方案说明

开源版 Jenkins Controller 不支持 active-active 多活(JENKINS_HOME 不允许多实例并发写)。 本次采用社区标准的 K8s 高可用模式:

层面措施
控制器可用性K8s StatefulSet 自愈:Pod/节点故障时自动在健康节点重建,Recreate 策略保证单实例写
数据持久化NFS StorageClass(ReadWriteMany,20Gi),节点故障不丢数据
构建可用性Kubernetes 插件弹性 Agent:构建 Pod 动态调度到任意健康节点,控制器 0 执行器
配置可用性JCasC 配置即代码 + ConfigMap,控制器可在数分钟内无损重建/迁移
配置热更新kiwigrid/k8s-sidecar 监听 ConfigMap 变更自动 reload JCasC

如需 RTO 更短的"双活"方案,需商业版 CloudBees CI(Hazelcast 主备)。开源方案下 控制器重建耗时约 1-2 分钟(Pod 重建 + Jenkins 启动),期间正在运行的 Agent 构建不受影响。

3.2 预创建资源

bash
export KUBECONFIG=~/.kube/config-122.31
K=./tools/kubectl

# 命名空间
$K create namespace jenkins

# SonarQube token 占位 secret(真实 token 在 SonarQube 部署后更新,见 04 文档)
$K -n jenkins create secret generic sonar-credentials --from-literal=sonar-token=xxx

# Maven 阿里云镜像 settings.xml(agent 构建加速)
$K -n jenkins create configmap maven-settings --from-file=settings.xml=deploy/jenkins/maven-settings.xml

# Java 示例项目源码(ConfigMap 挂载到 agent pod /examples)
$K -n jenkins create configmap jenkins-examples \
  --from-file=java-demo-pom.xml=deploy/examples/java-demo/pom.xml \
  --from-file=java-demo-App.java=deploy/examples/java-demo/src/main/java/demo/App.java \
  --from-file=java-demo-AppTest.java=deploy/examples/java-demo/src/test/java/demo/AppTest.java

3.3 Helm 安装

values 文件:deploy/jenkins/values.yaml,关键点:

  • controller.image: 指向 Harbor jenkins/jenkins:lts-jdk21
  • controller.serviceType: NodePortnodePort: 30880
  • controller.persistence: storageClass nfs、RWX、20Gi
  • controller.existingSecret: sonar-credentials:secret 挂载到 /run/secrets/additional/,JCasC 用 ${readFile:...} 引用
  • controller.additionalPlugins: sonarjob-dsl(chart 默认已含 kubernetes、workflow-aggregator、git、configuration-as-code)
  • controller.JCasC.configScripts: 系统信息 / SonarQube 服务器与凭据 / 种子任务(详见 05 文档)
  • agent.podTemplates: maven8、maven21、node21、golang、python 五个构建模板
bash
helm install jenkins ./charts/jenkins -n jenkins -f deploy/jenkins/values.yaml --timeout 10m

插件下载说明:controller 的 init 容器会从 updates.jenkins.io 下载插件。 本次实测集群可正常访问。若不可访问,可在 values 中配置国内更新中心:

yaml
controller:
  initContainerEnv:
    - name: JENKINS_UC
      value: https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates

3.4 验证

bash
$K -n jenkins get pods -o wide          # jenkins-0 Running 2/2
$K -n jenkins get pvc                   # jenkins 20Gi Bound
$K -n jenkins get svc                   # NodePort 30880 / agent 50000
$K -n jenkins logs jenkins-0 -c jenkins | tail

浏览器访问 http://192.168.122.23:30880 ,账号 admin / xxx

3.5 高可用验证(故障演练,已执行)

bash
# 写入数据标记 → 删除控制器 Pod → 观察自愈
$K -n jenkins exec jenkins-0 -c jenkins -- sh -c 'date +%s > /var/jenkins_home/HA_MARKER'
$K -n jenkins delete pod jenkins-0

实测结果(2026-08-14):

  • Pod 被删除后 StatefulSet 立即重建,调度到 sza122030.local
  • NFS 上 JENKINS_HOME 完整保留:HA_MARKER 内容前后一致(1786686009)
  • 恢复时间:首次经历冷启动约 4-5 分钟(NFS 插件加载慢);预热后实测约 2 分 20 秒(13:50 重建 → 14:05 就绪,含探针等待)
  • 恢复后所有配置(JCasC)、任务、凭据、构建历史无丢失

加速建议见 07 运维手册与"性能优化"章节(插件预装自定义镜像可显著缩短恢复时间)。

3.6 踩坑记录

  1. 探针预算不足:chart 默认 startupProbe 12×10s=120s,NFS 上首次启动(插件解压/加载) 远超此值,导致启动循环。已在 values 中设置 controller.probes.startupProbe.failureThreshold: 60。 注意探针配置嵌套在 controller.probes.* 下(不是 controller.startupProbe)。
  2. JCasC 缩进错误:configScripts 中 job-dsl script('''...''') 内嵌 pipeline 的 缩进必须深于 YAML script: | 键,否则 snakeyaml 解析失败导致 Jenkins 启动即退出(exit 5)。
  3. envVars 语法:kubernetes 插件 JCasC 中容器环境变量必须写 - envVar: {key, value}, 不能直接写 - key: ...
  4. location 冲突unclassified.location.url 由 chart 默认配置生成(来自 controller.jenkinsUrl),在自定义 configScript 中重复定义会触发 ConfiguratorConflictException。正确做法:设置 controller.jenkinsUrl 值。