企业级 Jenkins (CentOS 7.9 & JDK 21) 高可用分布式架构与负载均衡实践指南
1. 企业级应用背景与痛点
在企业研发协同规模较小时,单机部署的 Jenkins 能够满足基本的持续集成需求。然而,随着企业业务增长、微服务架构演进以及研发人员的增加,单机版 Jenkins 往往会面临以下核心痛点,直接影响研发效率与交付稳定性:
- 计算资源瓶颈与服务宕机风险:
在敏捷开发模式下,各项目组高频提交代码,触发大规模并发构建。编译、打包(尤其是 JVM 类重型编译)会瞬间消耗大量的 CPU 与内存。在单机模式下,这极易引发 OOM(内存溢出)导致 Jenkins 进程崩溃,造成企业内部持续集成和生产发布链路中断。 - 缺乏环境隔离与任务相互干扰:
不同项目对编译环境(如不同版本的 JDK、NodeJS、Maven 插件等)的要求各异。在单机上频繁切换和共用全局工具,容易导致环境变量污染。此外,某一个异常的构建任务(如死循环或内存泄漏)可能会耗尽系统资源,从而导致其他关键发布任务排队挂起。 - 缺乏高可用与故障容灾能力:
单机运行意味着“单点故障(SPOF)”。一旦该服务器发生硬件故障或网络抖动,整个企业的软件交付流水线将完全瘫痪。
为了解决上述问题,企业急需构建一套分布式、高可用且具备自动负载均衡能力的 Jenkins 集群架构:通过主节点(Controller)仅负责任务调度与指令下发,将实际的构建压力均匀地分摊到多台低成本的从节点(Agent)上,从而保障企业级交付链路的连续性与高效性。
2. 系统架构设计与环境规划
为了实现负载均衡和任务分担,我们规划了一台主节点和两台从节点。两个从节点打上相同的标签,以便 Jenkins 能够在这两个计算资源之间进行自动调度与负载均衡。
| 节点角色 | 主机名 | IP 地址 | 预配 JDK 版本 | 配置标签 (Labels) | 职责说明 |
|---|---|---|---|---|---|
| Controller (主) | jenkins-master |
192.168.108.31 |
JDK 21 (Temurin) | 无 | 仅做调度,不运行具体构建任务 |
| Agent 01 (从) | jenkins-agent01 |
192.168.108.40 |
JDK 21 (Temurin) | test-agent-group |
参与负载均衡的计算节点 01 |
| Agent 02 (从) | jenkins-agent02 |
192.168.108.41 |
JDK 21 (Temurin) | test-agent-group |
参与负载均衡的计算节点 02 |
2.1 系统基础环境配置(所有节点执行)
在主从节点上分别关闭防火墙、同步系统时间并关闭 SELinux。
# 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
# 关闭 SELinux
setenforce 0
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
# 同步时间
yum install -y ntpdate
ntpdate ntp.aliyun.com
3. 基础环境部署:手动安装 JDK 21(所有节点执行)
由于 CentOS 7.9 已于 2024 年 6 月 30 日停止维护(EOL),官方 YUM 源中通常没有现成的 OpenJDK 21。我们采用手动下载 Eclipse Temurin JDK 21 压缩包的方式进行安装。
3.1 手动部署 JDK 21
# 创建 Java 安装目录
mkdir -p /usr/local/java
# 下载 Eclipse Temurin JDK 21 稳定版(x64 Linux)
wget https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.2%2B13/OpenJDK21U-jdk_x64_linux_hotspot_21.0.2_13.tar.gz
# 解压至指定目录
tar -zxvf OpenJDK21U-jdk_x64_linux_hotspot_21.0.2_13.tar.gz -C /usr/local/java/
# 重命名目录便于管理
mv /usr/local/java/jdk-21.0.2+13 /usr/local/java/jdk21
3.2 配置环境变量
# 写入环境变量配置文件
cat >> /etc/profile.d/java.sh << 'EOF'
export JAVA_HOME=/usr/local/java/jdk21
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib
EOF
# 重新加载环境变量
source /etc/profile
# 验证安装
java -version
3.3 创建专用的 jenkins 运行用户(仅在 Agent01 / Agent02 执行)
# 创建独立的运行用户
groupadd jenkins
useradd -g jenkins -m -d /home/jenkins -s /bin/bash jenkins
# 设置密码
echo "Jenkins@123" | passwd --stdin jenkins
# 创建工作目录并授权
mkdir -p /var/jenkins/agent
chown -R jenkins:jenkins /var/jenkins/agent
4. 核心安全防护与 SSH 免密登录配置
在标准的生产加固环境中,主节点(.31)上的 jenkins 用户的登录 Shell 通常会被设置为受限的 /bin/false(如 /etc/passwd 中所示:jenkins:x:996:993:Jenkins Automation Server:/var/lib/jenkins:/bin/false)。
由于该限制,我们无法通过普通的 su - jenkins 切换用户来执行密钥生成与分发。我们必须在主节点的 root 用户下,采用以下免切换用户的专业配置方法:
4.1 确认/生成主节点密钥对(在 Controller 192.168.108.31 执行)
如果您的主节点 /var/lib/jenkins/.ssh/ 目录下已存在密钥文件,请直接跳到 4.2 进行分发。若不存在,请在 root 用户下使用 sudo -u 命令以 jenkins 身份生成密钥对:
# 以 jenkins 用户身份生成 3072 位 RSA 密钥
sudo -u jenkins ssh-keygen -t rsa -b 3072 -f /var/lib/jenkins/.ssh/id_rsa -N ""
4.2 免密分发公钥至从节点(在 Controller 192.168.108.31 执行)
在 root 用户下,使用 -i 参数显式指定 jenkins 用户的公钥路径,分别分发至从节点 .40 和 .41:
# 分发公钥到从节点 192.168.108.40
ssh-copy-id -i /var/lib/jenkins/.ssh/id_rsa.pub jenkins@192.168.108.40
# 分发公钥到从节点 192.168.108.41
ssh-copy-id -i /var/lib/jenkins/.ssh/id_rsa.pub jenkins@192.168.108.41
注:分发过程中会提示输入从节点 jenkins 用户的密码(即 Jenkins@123)。
4.3 免密通道双向验证(核心步骤)
由于主节点的 jenkins 用户无法交互登录,我们需要在主节点(.31)的 root 用户下,利用 sudo -u jenkins 模拟该用户,远程读取从节点上追加生成的 /home/jenkins/.ssh/authorized_keys(授权文件)以验证免密通道:
# 验证远程连接 .40 节点
sudo -u jenkins ssh -i /var/lib/jenkins/.ssh/id_rsa -o StrictHostKeyChecking=no jenkins@192.168.108.40 "cat ~/.ssh/authorized_keys"
# 验证远程连接 .41 节点
sudo -u jenkins ssh -i /var/lib/jenkins/.ssh/id_rsa -o StrictHostKeyChecking=no jenkins@192.168.108.41 "cat ~/.ssh/authorized_keys"
验证通过标准:命令执行时未提示输入密码,而是直接打印出了以 ssh-rsa ... jenkins@jenkins 结尾的公钥内容。
5. 在 Jenkins UI 中对接并配置从节点
登录 Jenkins Web 控制台,依次进入 Manage Jenkins -> Nodes。
5.1 创建 Credentials(凭据)
- 进入 Manage Jenkins -> Credentials -> System -> Global credentials。
- 点击 Add Credentials:
- Kind:
SSH Username with private key - ID:
jenkins-ssh-key-21 - Username:
jenkins - Private Key: 选择
Enter directly,点击 Add,并在主节点上运行cat /var/lib/jenkins/.ssh/id_rsa,将输出的私钥内容完整粘贴进去。
- Kind:
- 点击 Create 保存。

5.2 添加从节点 Agent 01 (192.168.108.40)
在节点管理页面,点击 New Node:
- Node name:
jenkins-agent01 - Type: 选择
Permanent Agent,点击 Create。
![3ff1a206dad961bdfff4ccba99b15cf4]()
详细配置参数如下:
- Number of executors:
2(允许同时运行 2 个并发任务) - Remote root directory:
/var/jenkins/agent - Labels:
test-agent-group(负载均衡的关键标签) - Usage:
Only build jobs with label expressions matching this node - Launch method:
Launch agents via SSH - Host:
192.168.108.40 - Credentials: 选择
jenkins-ssh-key-21 - JavaPath:
/usr/local/java/jdk21/bin/java(显式指定 JDK 21 绝对路径) - Host Key Verification Strategy:
Non-verifying Verification Strategy


点击 Save 保存。
5.3 添加从节点 Agent 02 (192.168.108.41)
重复上述步骤添加 jenkins-agent02,配置差异点如下:
- Node name:
jenkins-agent02 - Number of executors:
2 - Labels:
test-agent-group - Host:
192.168.108.41 - Credentials: 选择
jenkins-ssh-key-21 - JavaPath:
/usr/local/java/jdk21/bin/java

5.4 禁用主节点(Controller)的构建功能
为了确保任务全部调度下发到专门的从节点上运行,需要将主节点的计算资源清零。
- 在节点列表中,点击
Built-In Node的设置齿轮图标。 - 将 Number of executors 修改为
0并保存。
![f8bc60553b58ca4db4c042ced0737d46]()
5.5 配置成功,成功添加 agent01和agent02

6. Jenkins 内部负载均衡调度机制底层原理
在分布式架构部署完成后,我们必须理解 Jenkins 是如何管理和分发构建任务的。Jenkins 的调度并不是简单的 Nginx 式轮询(Round-Robin),而是采用了一套启发式调度算法(Heuristic Scheduling Algorithm)。
其核心调度原则包括:
- 标签约束优先(Hard Constraints):
首先过滤掉不满足label表达式要求、离线或临时下线(如磁盘空间不足)的从节点。 - 工作空间粘性与缓存局部性(Workspace Stickiness):
为了优化构建效率,调度算法利用一致性哈希计算任务名(Job Name)的偏好。如果一个 Job 上次是在 Agent 01 上运行的,且 Agent 01 当前有空闲的 Executor,调度器会优先将该任务派发给 Agent 01。这样做是为了重用本地已存在的 Git 仓库和 Maven/NodeJS 等依赖包缓存,节省不必要的带宽和时间。 - 最少负载优先(Least Load First):
当“粘性偏好节点”忙碌,或存在多个同等优先级的可用节点时,调度器会扫描并计算节点上的执行器占用比例:
$$\text{节点负载率} = \frac{\text{已占用执行器数}}{\text{该节点总执行器数}}$$
优先将任务分配给负载率最低、空闲插槽最多的节点,以防止单台服务器过载。
7. 负载均衡效果验证(极简测试 Pipeline)
我们编写一个极其轻量、不依赖任何外部工具链(仅通过系统自带的 hostname 和 sleep 命令)的测试 Pipeline,来验证这套分布式调度机制。
7.1 测试用 Pipeline 脚本
在 Jenkins 中新建一个 Pipeline(流水线) 任务,将以下脚本粘贴到配置中:
pipeline {
agent {
node {
// 指定我们在 .40 和 .41 上共同配置的标签
label 'test-agent-group'
}
}
stages {
stage('Load Balancing Test') {
steps {
echo "正在检测当前任务运行的目标节点..."
sh '''
echo "================================================="
echo "【当前运行主机名】: $(hostname)"
echo "【当前运行内网 IP】: $(hostname -I | awk '{print $1}')"
echo "================================================="
# 模拟任务运行耗时,便于我们在并发下观察任务在各节点的分流效果
sleep 15
'''
}
}
}
}
7.2 负载均衡验证步骤
- 串行构建测试:
点击一次 Build Now,任务运行结束(耗时约 15 秒)。再次点击 Build Now。- 现象:两次构建通常会连续在同一个节点(如
.40)上运行。 - 原理:此时无资源竞争,触发“工作空间粘性”机制,系统优先重用该节点以节省冷启动时间。
- 现象:两次构建通常会连续在同一个节点(如
- 并发构建测试(验证负载分担):
快速连续点击 Build Now 3 到 4 次,产生多个并发构建。- 现象:前往 Nodes 列表或主控台左下角的 Build Executor Status,您会直观地看到:部分构建任务在
192.168.108.40上执行,而另一部分任务同时在192.168.108.41上执行。 - 原理:当先发任务占用了第一个节点的 Executor 后,调度算法检测到其负载升高,硬性约束判定其无多余并发能力,从而打破“粘性偏好”,自动将后发任务分流到空闲的另一个节点上,实现负载均衡。
- 现象:前往 Nodes 列表或主控台左下角的 Build Executor Status,您会直观地看到:部分构建任务在





8. 企业级 Pipeline 与分布式负载均衡的最佳实践
在实际生产环境中,单凭 Jenkins 的默认配置无法获得最佳的构建效果。我们需要在编写企业级 Pipeline 时主动引导调度算法:
8.1 利用 parallel(并行阶段)实现跨节点分布式加速
如果项目拥有大量的独立子模块、测试集或代码扫描,如果单机顺序执行将耗时极长。
通过在 Pipeline 中使用 parallel 块并将 agent 定义在 Stage 级别(同时将顶层 agent 设为 none),Jenkins 的调度器会将并行的各个子任务当成独立的 Queue.Item 发送到不同的从节点上并发执行。
pipeline {
agent none // 顶层不指定 Agent,释放主节点,允许子阶段独立调度
stages {
stage('Parallel CI Stages') {
parallel {
stage('Unit Test') {
agent { label 'test-agent-group' }
steps {
echo "Running Unit Test on node: \$(hostname)"
sh "sleep 10"
}
}
stage('Static Code Analysis') {
agent { label 'test-agent-group' }
steps {
echo "Running SonarQube on node: \$(hostname)"
sh "sleep 10"
}
}
}
}
}
}
8.2 分布式缓存治理:解决“粘性失效”带来的性能退化
当高并发导致任务被频繁切换到不同的从节点时,本地缓存会失效。针对该问题,企业级优化方案包括:
- 搭建内网私有统一仓库代理:在企业内网部署 Nexus 或 Artifactory,千/万兆内网可以大幅弥补冷启动下载依赖时的时延。
- 挂载分布式共享存储:通过 CephFS、NFS 或 AWS EFS 将缓存路径(如
/home/jenkins/.m2或/home/jenkins/.npm)共享挂载到所有的从节点。 - 使用缓存归档插件:在流水线开始前增量拉取缓存压缩包,结束时增量上传,避免全局全量下载。
8.3 细粒度标签(Labels)与硬件资源画像绑定
不要将所有任务都打上通用标签。应该根据节点的硬件能力进行“资源画像”,并在 Pipeline 中精确匹配:
high-cpu:分配给配备高性能多核 CPU 的构建节点(适合繁重的 C++ 编译)。high-mem:分配给具备大内存的构建节点(适合大型打包/Sonar 深度扫描)。- 在 Pipeline 中通过逻辑算符指定:
agent { label 'high-cpu && jdk21' }。
8.4 引入防御性并发控制
为了防止由于某项目频繁提交代码触发了 50 次并发构建,从而占满整个从节点集群中的所有 Executor,导致其他紧急发布任务完全无法排队,流水线中应当引入并发配置限制:
pipeline {
agent { label 'test-agent-group' }
options {
// 禁止该 Job 自身并发执行,防堵塞
disableConcurrentBuilds()
// 设置构建超时时间,防止进程由于死循环等未知故障白白占用从节点插槽
timeout(time: 30, unit: 'MINUTES')
}
stages {
stage('Build') {
steps { sh 'echo "Safe Build"' }
}
}
}
9. ⚠️ 高并发分布式集群的关键注意事项与避坑指南
为了保障分布式系统在生产环境下的长期稳定运行,配置和维护时请务必注意以下几点:
9.1 CentOS 7.9 EOL 引起的 YUM 源失效问题
- 问题描述:CentOS 7 已经在 2024 年 6 月 30 日停止官方维护。后续在从节点上执行工具更新或使用
yum install可能会报错无法解析 baseurl。 - 应对措施:建议将从节点的
/etc/yum.repos.d/CentOS-Base.repo镜像源修改为阿里云的 CentOS-Vault 源或华为云等提供历史归档的 YUM 源,以保障后续正常安装网络排查或构建辅助工具。
9.2 严格的 SSH 权限匹配
- 问题描述:通过 SSH 免密连接从节点时,如果权限过大,从节点的 SSH 服务会主动拒绝登录,导致 Agent 报
Authentication failed错误。 - 应对措施:确保从节点(
.40和.41)上jenkins用户目录的权限严格符合标准:/home/jenkins目录权限必须为700或755。/home/jenkins/.ssh目录权限必须为700。/home/jenkins/.ssh/authorized_keys文件权限必须为600。- 上述目录及文件的属主与属组必须均为
jenkins:jenkins。
9.3 SSH 非交互式登录的环境变量丢失问题
- 问题描述:主节点通过 SSH 连接到从节点并执行命令时,属于“非交互式登录”,默认不会自动加载从节点的
/etc/profile。如果在从节点上直接运行诸如mvn或node的命令,可能会报command not found。 - 应对措施:
- 在配置节点时,务必在 UI 界面的高级选项中,将 JavaPath 显式指定为绝对路径(如本例中的
/usr/local/java/jdk21/bin/java)。 - 如果后续在从节点上调用自定义工具链(如 Maven/Node),建议在系统的
/usr/bin目录下创建对应工具的软链接,或者在 Jenkins 的“系统管理 -> 系统配置”中显式注入全局环境变量(PATH)。
- 在配置节点时,务必在 UI 界面的高级选项中,将 JavaPath 显式指定为绝对路径(如本例中的
9.4 JVM 内存管理与 CentOS OOM 机制
- 问题描述:每个
Executor(执行器)在执行任务时,如果任务内部频繁拉起外部子进程,会消耗大量系统物理内存。若从节点物理内存耗尽,CentOS 会触发 OOM Killer 机制,直接杀掉java(Agent) 进程,导致从节点无故离线。 - 应对措施:
- 不要盲目调大从节点的
Number of executors。建议的经验公式:物理内存(GB) / 2得到的值,与CPU 核心数取较小值作为执行器上限。 - 限制 JVM 堆内存。必要时在从节点连接参数的 JVM 选项中加上适当的限制限制,例如
-Xms512m -Xmx2048m。
- 不要盲目调大从节点的
9.5 磁盘空间阈值保护
- 问题描述:Jenkins 内部存在保护机制,会定期检测从节点工作目录(
/var/jenkins/agent)的可用磁盘空间。若空闲空间低于 1 GiB,主节点会强制将该从节点置为 Temporarily Offline(临时下线)。 - 应对措施:
- 在分布式流水线中,建议在 Pipeline 的
post块中加入cleanWs()步骤,构建完成后及时清理工作区。 - 可通过控制台 “Configure Monitors” 适当调整报警磁盘阈值。
- 在分布式流水线中,建议在 Pipeline 的
10. 总结
本方案在 CentOS 7.9 和 JDK 21 体系下,通过主从架构将 Controller 的运行计算资源转移到 .40 与 .41 两个从节点上,实现了“计算与调度分离”。在面对大规模并发构建时,通过多维度标签配置和 Pipeline 最佳实践,使 Jenkins 的内置启发式负载均衡调度算法发挥其优势,有效降低了单点崩溃风险,为企业 CI/CD 流程的高效、稳定运行提供了坚实的系统底座。


浙公网安备 33010602011771号