☰
Jenkins 创建节点并连接:SSH 与 Inbound 分布式构建指南
2026/10/1 6:20:48 网站建设 项目流程

上周三晚上十一点,我还在盯着 Jenkins 的构建队列发呆——三个 Java 服务同时发版,一台 Master 又要编译又要打镜像又要推部署,队列从"等待中"排到了第十八位,测试同学在群里连着问了三遍"还要多久"。那天之后我做的第一件事,就是把编译和部署这两类活儿拆出去,给 Jenkins 创建了第一批专用节点,让它们各自认领标签、并行干活。这篇就聊透一件事:Jenkins 创建节点并连接,从头到尾怎么落地。不管你是刚开始接触 jenkins教程 的新手,还是已经在跑 jenkins自动化部署、想把构建能力横向扩一扩的老手,这里面的字段含义、连接方式选型、密钥配置、报错排查,我都会按我实际操作的顺序讲一遍,能直接抄的配置我都贴出来。

1. 先想清楚:为什么非得给 Jenkins 加节点

1.1 单机 Master 撑不住的三道坎

很多人搭 Jenkins 的第一天,就是把 Master 当万能机器用:拉代码、跑单测、打 jar、构建 Docker 镜像、推镜像仓库、SSH 到目标服务器重启服务,全在这一个实例上完成。小团队、低频发版的时候这么干确实省事,但只要你踩过下面三道坎中的任意一道,就会开始琢磨加节点这件事。

第一道坎是算力争抢。Jenkins 本身是个 Java 进程,构建任务也是一个个 Java 或者 shell 子进程,编译大型项目时 CPU 和内存会被瞬间抽干。这时候你会发现 Jenkins 的 Web 界面点一下卡三秒,日志刷新转圈圈,甚至出现 Master 直接被 OOM Killer 干掉的情况。第二道坎是环境冲突。前端项目要 Node 18,老项目只能跑 Node 14,Python 项目之间还互相嫌弃虚拟环境版本,全塞在一台机器上迟早打架。第三道坎是环境异构,比如你的构建产物需要在 Windows 上跑 MSBuild,或者需要一台 macOS 机器做特定打包任务,那 Master 是 Linux 就彻底没辙了。

节点(Node)这个东西,本质就是把"执行构建"这件事从 Master 上剥离出去。Jenkins 的架构天生就是 Master-Agent 模型:Master 负责调度、存配置、展示界面、记录构建历史,Agent(也就是节点)负责真正跑活儿。这个拆分不是可有可无的优化,而是这套工具设计时就留好的扩展口。

1.2 节点能解决的三类问题,以及解决不了什么

先说节点能解决的。算力横向扩展是最直接的收益:一台 16 核的机器加三个 8 核节点,并行构建能力直接翻倍多,发版日排队的问题几乎立刻缓解。构建环境隔离是第二个收益:我把前端构建固定在标签为node-frontend的节点上,Node 版本锁死;把 Java 构建固定在jdk17标签的节点上,各版本 JDK 各回各家,谁也别污染谁。异构平台支持是第三个收益:Windows 节点跑 MSBuild 和批处理脚本,Linux 节点跑 shell 和 Docker 构建,需要一个特殊 ARM 环境就单独挂一个 ARM 节点,非常灵活。

但节点解决不了什么,这点我必须提前泼盆冷水。节点不会让单条流水线本身变快,它只是让多条流水线能同时跑;节点也不解决磁盘空间问题,如果你的构建产物堆在节点本地不清理,节点照样会被塞满;节点更不解决网络问题,如果节点拉代码要跨机房,那拉代码的时间反而更长。所以加节点之前,先确保你的痛点确实属于"资源不够"或者"环境冲突",而不是"流水线写得有问题"。

提示:判断是否需要加节点有个很实用的信号——看 Jenkins 首页的"构建执行状态"里,队列长度是否长期大于执行器总数。如果长期是这样,加节点就是对的;如果队列很空但单条构建特别慢,那问题在流水线本身,加节点没用。

2. 动手前必须敲定的四件事

2.1 连接方式怎么选:SSH 还是 Inbound

Jenkins 创建节点时,最核心的一个选择就是"启动方式"(Launch method)。这个选项决定了 Master 和节点之间谁主动发起连接,也决定了后面所有的配置细节。

SSH 方式是 Master 主动去连节点,用的是标准 SSH 协议,走 22 端口(或者你自己改的端口)。它的优点是配置简单、稳定性好、日志直观,节点机器只要开了 SSH 服务并配好密钥就行,不需要在节点上常驻一个 Java 客户端进程。缺点也很明确:Master 必须能直接访问节点的 SSH 端口,而且必须在节点上有一个可用的登录账号。我把公司内网里的 Linux 构建机全部用这种方式接入,一共十几个节点,两年下来基本没出过连接层面的问题。

Inbound 方式(界面上叫"Launch agent by connecting it to the controller",老版本叫 JNLP)是反过来的:节点主动去连 Master。它的优势是穿透性强,节点不需要开放任何入站端口,只要节点能访问 Master 的地址就行,特别适合那种"节点在内网隔离区、Master 在外围"的网络结构。代价是要在节点上跑一个常驻的 agent 进程(agent.jar),并且要用 systemd 之类的工具做成服务,不然连接一断就得手动重来。

选型的判断标准其实只有一条:谁在网络拓扑上更容易被连到。Master 能直连节点 SSH,就选 SSH;节点只能单向访问 Master,就选 Inbound。两种方式的字段配置、日志位置都不一样,后面我会分别走一遍。

2.2 Java 版本对齐,这是最容易翻车的前置条件

Jenkins 2.357 之后,Master 和 Agent 的最低 Java 要求都提到了 Java 11,更新的版本(2.463 之后)已经要求 Java 17。这里有一个特别常见的误解:很多人以为节点上装的 Java 版本只要比 Master 低一点没关系,实际上Agent 的 Java 版本必须满足它所连接的那版 Jenkins 的最低要求,否则连接会在启动后几秒内断开,日志里抛出一堆UnsupportedClassVersionError。

我一般的处理方式是:在节点机器上装一个与 Master 主版本一致的 JDK(比如都用 Temurin 17),而不是用系统自带的 OpenJDK。Ubuntu 上执行java -version看看当前版本,Ubuntu 22.04 自带的是 Java 11,如果 Master 是要求 Java 17 的新版本,就得手动装。安装完之后要确认JAVA_HOME指向正确的路径,因为 SSH 方式启动节点时,Jenkins 会用你配置的javaPath或者 PATH 里的 java,找错版本就直接连不上。

另外,节点上的 Java 只是用来跑 Agent 进程的,跟你的项目构建用哪个 JDK 是两码事。项目构建的 JDK 可以通过 Jenkins 的"全局工具配置"指定,也可以直接在节点上用 Docker 隔离,不要混为一谈。

2.3 执行器数量、远程工作目录和标签怎么规划

执行器数量(# of executors)决定了这个节点能同时跑几个任务。这个数字不是越大越好。我的算法是:先看节点的物理内存,减去系统占用,再除以"单次构建的峰值内存",取小值;同时不超过 CPU 核数。举个例子,一台 32GB 内存、16 核的机器,系统和其他服务占 8GB,单次 Maven 构建峰值大概 2.5GB,那 24 / 2.5 ≈ 9,但更保守点我一般设 4 到 6,因为并发构建时磁盘 IO 和网络带宽也会互相压制。设成 16 个执行器看起来能跑很多任务,实际结果是所有任务一起变慢。

远程工作目录(Remote root directory)是节点上存放工作空间的路径,比如/data/jenkins-agent。这个目录必须存在、必须属于运行 Agent 的账号、必须有足够空间。我踩过的坑是用默认路径导致构建产物堆在系统盘上,某天日志暴涨把/撑满,整台机器上的所有服务都开始报错。所以从第一天起就把工作目录放到独立的数据盘上,并且给它设个监控。

标签(Labels)是调度的核心。标签本质上是给节点打的分类记号,流水线里写agent { label 'jdk17 && linux' }就能精确指定到某个节点。我的习惯是至少打三类标签:操作系统(linux / windows)、工具链(jdk17 / node18 / docker)、用途(build / deploy / test)。标签不要用中文,不要带空格,用短横线或者下划线连接,这样在流水线的表达式里最不容易出错。

2.4 凭据设计:专门建账号,别用 root

这一步是我见过最多人偷懒的地方。用 root 连 SSH 确实一路顺畅,但后果是构建脚本里任何一个rm -rf都可能是灾难级的,而且 root 的密钥一旦泄露,整台机器的边界就没了。正确做法是创建一个专用的jenkins用户,给它配置好工作目录的读写权限,再按需给它有限的 sudo 权限。

具体来说,我会给这个用户配免密 sudo 到特定命令,比如只有systemctl restart xxx.service这一条,而不是给全量 sudo。配置写在/etc/sudoers.d/jenkins里,用visudo -f编辑,避免把主配置文件写坏。至于 SSH 密钥,统一在 Master 上用一个专用密钥对,私钥加密后存进 Jenkins 凭据库,不要在多个节点之间复用同一把私钥,更不要把私钥直接放在节点的authorized_keys之外的地方。

3. 跟做一遍:SSH 方式创建并连接 Linux 节点

3.1 节点机器前置准备

假设我们有一台干净的内网 Ubuntu 机器,IP 是10.0.20.31,目标是把它的前八个核和 16GB 内存贡献给构建。整套前置准备分四步,每步我都会说明为什么要做。

先用 root 或有 sudo 权限的账号创建 jenkins 用户并建工作目录:

sudo useradd -m -s /bin/bash jenkins sudo mkdir -p /data/jenkins-agent sudo chown -R jenkins:jenkins /data/jenkins-agent

-m的作用是自动创建家目录,因为 SSH 密钥默认放~/.ssh,没有家目录会直接连不上。工作目录单独放/data,是为了让它和系统盘分离,这条我在上一节已经解释过原因。

接着装 JDK。以 Temurin 17 为例,Ubuntu 上的标准做法是加仓库再装:

sudo apt update sudo apt install -y wget apt-transport-https gnupg wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/adoptium.gpg echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | sudo tee /etc/apt/sources.list.d/adoptium.list sudo apt update sudo apt install -y temurin-17-jdk

装完务必验证java -version和which java,把路径记下来,待会儿创建节点时要填。如果你的环境不能直连外网,那就用离线包解压安装,把 tar.gz 解到/opt再配JAVA_HOME,效果一样。

然后是给 jenkins 用户配 SSH 密钥登录。在节点上先切换到该用户,把 Master 生成的公钥写进去:

sudo su - jenkins mkdir -p ~/.ssh && chmod 700 ~/.ssh echo "ssh-ed25519 AAAAC3N...你的公钥内容" >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys

权限位这两个数字不是可选项,SSH 服务端对~/.ssh和authorized_keys的权限检查非常严格,权限过宽会直接拒绝登录。这一步和下面生成密钥的顺序其实可以互换,只要最终两端匹配就行。

3.2 在 Master 上生成密钥并创建凭据

登录 Master 的服务器(或者用有权访问 Jenkins 家目录的账号),切换到 Jenkins 运行用户,生成专用密钥:

sudo su - jenkins ssh-keygen -t ed25519 -C "jenkins-agent-key" -f ~/.ssh/id_ed25519_agent -N "" cat ~/.ssh/id_ed25519_agent.pub

-N ""表示私钥不加口令,这样 Jenkins 连接时可以全自动,不需要人工输入。安全性靠的是私钥文件本身的权限和服务器访问控制,所以这个私钥文件权限必须是 600,绝不能放到代码仓库里。

打开 Jenkins 界面,进入Manage Jenkins→Credentials→System→Global credentials,点Add Credentials。类型选SSH Username with private key,ID 起个有意义的名字比如agent-10.0.20.31-ssh,Username 填jenkins,Private Key 选Enter directly把刚生成的私钥内容粘贴进去。ID 一定要有意义,因为后面在流水线里可能会用credentials()引用它,随手打的 ID 过两周你自己都不认识。

注意:粘贴私钥时要包含完整的-----BEGIN OPENSSH PRIVATE KEY-----和-----END OPENSSH PRIVATE KEY-----两行,少一行 Jenkins 会报凭据无法解析,而且报错信息很不友好,只会告诉你连接失败。

3.3 在 Manage Nodes 里新建节点并逐字段填写

进入Manage Jenkins→Nodes(新版本在Manage Jenkins→System下的 Nodes 区域,或者直接访问/computer/),点New Node。填写名称,比如linux-build-01,勾选Permanent Agent(永久节点),确定后进入详细配置页。

页面上的字段看起来多,实际必须理解的就七八个,我按重要性排一遍:

字段建议值说明
Namelinux-build-01节点名,全唯一,用短横线不用中文
Remote root directory/data/jenkins-agent必须是已存在且 jenkins 用户可写的绝对路径
Labelslinux jdk17 build空格分隔,流水线按这个匹配
Usage尽量使用这个节点让调度器尽量把任务分过来
Launch methodLaunch agents via SSH连接方式,本方案选它
Host10.0.20.31节点 IP 或可解析的主机名
Credentials上一步创建的凭据选 SSH Username with private key 那条
Host Key Verification StrategyNon verifying verification strategy内网固定 IP 环境可接受,见下方说明
AvailabilityKeep this agent online as much as possible保持在线,除非你做动态节点

这里有两个字段值得多说两句。Host Key Verification Strategy默认是"Known hosts file verification",需要你在 Master 的 known_hosts 里提前存好节点的主机指纹,麻烦但更安全。如果你管理的是内网固定 IP 的机器,选Non verifying能省去每次加节点都要 ssh-keyscan 的麻烦,但代价是失去主机身份校验,所以只建议在可控内网里这么干。Availability那一栏如果选"Take this agent online/offline based on schedule",可以实现工作时段在线、夜间下线,适合按需节约资源的场景。

填完后点 Save,Jenkins 会立刻尝试连接。这时候别急着刷页面看状态,先去节点页面点进去看日志,日志才是判断连接成功与否的唯一依据。

3.4 验证连接:日志在哪看,成功长什么样

节点创建后,在节点列表里点击节点名,左侧有个Log菜单,这就是实时连接日志。成功的日志大概长这样(不同版本措辞略有差异):

[SSH] Opening SSH connection to 10.0.20.31:22. [SSH] Success. (Connection established) [SSH] Checking java version of /usr/bin/java [SSH] 17.0.10 [SSH] Starting agent process... Agent successfully connected and online

几个关键判断点:看到Success说明 SSH 认证通过了,如果你的凭据或者权限有问题,这里就会停住并报Auth fail或者Permission denied;看到Checking java version说明已经开始检查 Java,这一行如果报版本过低,就回头去节点上换 JDK;最后一行online才是真正的成功。从点击保存到变成在线,正常情况十秒以内,如果需要更久,多半是在等 SSH 超时。

如果日志停在中途,我一般按这个顺序排查:先在 Master 上用ssh -i 私钥文件 jenkins@10.0.20.31手工连一次,手工能通说明网络和密钥没问题,问题在 Jenkins 侧配置;手工不通就直接看 SSH 服务端的/var/log/auth.log,里面的拒绝原因写得非常明确,比 Jenkins 的日志详细得多。

顺便提一个界面访问的细节。有时候你从公网地址跳转到内网 Jenkins 控制台,浏览器会因为私有网络访问策略拦截请求,页面提示某个连接被阻止。这不是 Jenkins 的问题,是浏览器对新版本安全策略的执行结果。最稳妥的规避方式就是始终用内网地址或者带域名的内网入口访问 Jenkins,别用公网页面作为跳板。

3.5 用流水线验证任务是不是真的落到节点上了

节点显示在线只是第一步,真正的验证是让一个任务跑上去。新建一个 Pipeline 任务,脚本这么写:

pipeline { agent { label 'linux && jdk17' } stages { stage('CheckEnv') { steps { sh ''' echo "当前节点: $(hostname)" echo "工作目录: $(pwd)" echo "Java 版本: $(java -version 2>&1 | head -n 1)" ''' } } } }

跑完之后看控制台输出,hostname如果是节点机器的主机名,说明调度成功;pwd应该落在/data/jenkins-agent/workspace/任务名下面;Java 版本应该是节点上装的那个。三项都对,这个节点就算真正接入完成了。我强烈建议把这段检查脚本固化成一个"环境自检"任务,每次新加节点都跑一遍,比人工登机器查快得多。

另外,agent { label 'xxx' }里的表达式支持&&和||,写jdk17 && linux是要求两个标签同时具备,写jdk17 || jdk21则是满足其一即可。标签表达式一旦写错,Jenkins 不会报错,而是会让任务一直卡在"等待可用的执行器"上,这个现象要能识别出来。

4. 内网隔离环境:Inbound 方式怎么接

4.1 什么情况下必须用 Inbound

有些环境里,节点机器位于一个只能出、不能入的网络区域,Master 根本没法主动去连它的 SSH 端口。举个我遇到的真实场景:构建机被放在一个受控区,只允许它访问 Master 的 8080,不允许 Master 反向连它的任何端口。这种情况下 SSH 方式无解,必须让节点主动发起连接。

Jenkins 对这种模式的官方叫法是"通过让 agent 连接到 controller 来启动 agent",在新建节点时选Permanent Agent之后,"Launch method" 那一栏选Launch agent by connecting it to the controller。这种模式下,Master 不再作为客户端,而是作为一个监听服务端等待节点连过来,两种角色的对调带来了完全不同的配置方式和排错思路。

4.2 拆解 secret 和 agent.jar 启动命令

创建完这类节点后,节点详情页会给你一段现成的启动命令,大意是这样:

java -jar agent.jar -url http://jenkins.example.com:8080/ \ -secret 0f1e2d3c4b5a69788796a5b4c3d2e1f0a1b2c3d4e5f60718293a4b5c6d7e8f90 \ -name "linux-inbound-01" \ -workDir "/data/jenkins-agent"

这段命令里有四个参数,每个都不能省。-url是 Master 的地址,如果 Master 前面有反向代理,要填对外可访问的域名和端口;-secret是节点身份的密钥,每个节点独立,泄露了等于别人可以冒充你的节点,所以它在 Jenkins 里是加密存储的;-name必须和你在 Jenkins 里创建的节点名完全一致,差一个字符就会认证失败;-workDir是节点上的工作目录,要确保目录存在并且有写权限。

agent.jar可以从 Master 的/jnlpJars/agent.jar路径下载,也就是在浏览器里访问http://你的jenkins地址/jnlpJars/agent.jar。如果这台节点不能访问外网也没关系,agent.jar 是从 Master 拿的,本身就是内网流量。

这里涉及一个通信层面的细节:Inbound 模式的节点和 Master 之间保持的是一条 TCP 长连接,而不是每传一次数据就重新握手。这条长连接会周期性发心跳,一旦网络抖动导致连接断开,节点会尝试自动重连,重连期间节点状态显示为离线。Jenkins 新版本默认使用 WebSocket 作为传输层,好处是可以复用 Master 的 HTTP 端口(8080 或 443),不需要额外开放那个传统的 50000 端口。如果你的环境防火墙卡得很死,只放行了 443,那就一定要确认 WebSocket 模式开启,否则连接会一直停在"握手失败"。

4.3 做成 systemd 服务,别让它挂在终端里

用nohup或者screen跑 agent 命令,短期看着能工作,但只要机器重启、会话超时或者运维同学误关窗口,节点就掉了,而且没有任何自动恢复机制。正确做法是注册成 systemd 服务:

sudo tee /etc/systemd/system/jenkins-agent.service > /dev/null <<'EOF' [Unit] Description=Jenkins Agent After=network.target [Service] Type=simple User=jenkins WorkingDirectory=/data/jenkins-agent ExecStart=/usr/bin/java -jar /data/jenkins-agent/agent.jar \ -url http://jenkins.example.com:8080/ \ -secret 你的secret \ -name "linux-inbound-01" \ -workDir "/data/jenkins-agent" Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable --now jenkins-agent sudo systemctl status jenkins-agent

Restart=always配合RestartSec=10是关键,它保证了节点进程无论因为什么原因退出,十秒后都会自动拉起来。这样网络抖动、Master 重启、机器重启这些情况都不需要人工干预。做完之后用journalctl -u jenkins-agent -f看日志,能看到和 Web 界面同样的连接过程信息。

5. 常见报错与排查速查表

5.1 SSH 连接类问题的定位思路

SSH 方式的失败几乎都集中在认证和端口两件事上。我整理了一张速查表,按"现象—原因—处理"三列来组织,遇到问题直接对号入座。

现象大概率原因处理方式
Auth fail / Permission denied用户名不匹配、私钥无权限、authorized_keys 权限过宽在 Master 手工 ssh 验证,检查 600 / 700 权限位
Connection refused节点 SSH 服务没启动或端口不对`ss -tlnp
Connection timed out防火墙拦截或 IP 不通telnet 节点IP 22或nc -zv测试连通性
Host key verification failedMaster 的 known_hosts 没有节点指纹用 ssh-keyscan 补指纹,或改用非验证策略
No such file or directoryRemote root directory 不存在在节点上手动创建目录并授权

排查 SSH 问题时有个特别好用的方法:用 Jenkins 完全相同的私钥和用户名,在 Master 上用ssh -vvv手工连一次。三档 verbose 会把密钥协商、认证过程全部打出来,最后失败的那一步在输出里一目了然。这个操作能省掉你在 Jenkins 界面上反复试错的时间。

5.2 节点能连上但频繁掉线怎么办

有一种情况比彻底连不上更折磨人:节点显示在线,跑着跑着突然离线,过几秒又回来了,构建任务跟着失败。这种抖动通常来自四个方面。

一是网络层有心跳超时限制。中间的网络设备可能会把长时间空闲的 TCP 连接清掉,而 Agent 默认的心跳间隔如果比这个超时时间长,连接就会被中途掐断。解决办法是在节点启动参数里加-pingTimeout之类的调优参数,或者让心跳更频繁。二是Java 堆内存不足,Agent 进程 OOM 之后被系统杀掉,systemd 拉起来又跑一阵又崩,表现就是周期性掉线,这时候去看journalctl里有没有 OOM 相关的记录。三是时钟不同步,Master 和节点时间差太大时,TLS 握手或者某些基于时间戳的校验会失败,装个 NTP 客户端同步一下就好。四是磁盘写满导致的工作目录写入失败,这个最隐蔽,因为连接本身是好的,只有构建时会出问题。

顺便说一句,节点的日志分两层:连接层日志在 Jenkins 界面的节点页面里,Agent 进程自身的日志在系统日志里(systemd 管的就用journalctl,SSH 启动的在节点工作目录下有slave.log之类的文件)。两层都要看,只看一层很容易误判。

5.3 任务明明配了标签却不上节点

这大概是被问得最多的一个问题。任务卡在"等待可用的执行器"上不动,节点明明是绿的。原因一般有这几种:标签表达式写错,比如节点的标签是jdk-17而任务里写的是jdk17,少一个横线就匹配不上;节点的 Usage 被设成了"只允许绑定到此节点的任务"(Only build jobs with label expressions matching this node),而任务没有显式绑定这个节点;节点的执行器被占满了,也就是并发数不够;还有一种是任务在agent里用了node直接调用,绕过了标签匹配逻辑。

排查顺序建议是:先在节点的详情页看"构建执行状态",确认执行器是不是真的被占满;再用脚本控制台执行Jenkins.instance.getNodes()或者在节点列表页看标签;最后把任务的标签表达式简化成只写一个标签试试。把大表达式拆开来测,比一次写完整表达式更容易定位问题。

5.4 环境变量和工具路径对不上

同一条流水线在 Master 上跑得好好的,一到节点上就报"命令找不到",这通常是因为节点上的 PATH 和 Master 不一样。SSH 方式启动的非交互式会话里,/etc/profile和~/.bashrc不一定会被加载,所以你在 shell 里手敲能跑通的命令,在 Jenkins 里可能就找不到。

处理方式有两种。一种是在节点配置页面的"Node Properties"里勾选"Environment variables",把PATH、JAVA_HOME、M2_HOME这些关键变量显式写进去,这样它们会注入到节点的 Agent 进程环境中。另一种是在流水线里用withEnv临时设置。我更推荐前者,因为它是节点级的全局配置,一次配好所有任务都受益。至于构建工具的具体版本,比如 Maven、Gradle、Node,交给 Jenkins 的"全局工具配置"来管理最规范,配置好后在流水线里用tools { maven 'maven-3.9' }引用就行,这块和节点配置是配套使用的。

5.5 节点用久了越来越慢

节点跑几个月之后开始变慢、磁盘告警,多半是历史工作空间和构建缓存没清理。Jenkins 默认不会自动清理旧的工作空间,每次构建都会在workspace/任务名下面堆积文件。我的做法是在节点上挂一个定时任务,或者干脆用 Jenkins 的"Discard old builds"策略配合工作空间清理插件来控制。另外,节点上的 Docker 如果被用于构建,镜像和容器层会疯狂膨胀,定期执行docker system prune这类清理是必要的运维动作。

还有一个容易被忽略的点是连接数。如果节点参与的任务很多,同时打开的连接和文件句柄数量会上来,系统级的ulimit -n如果还是默认的 1024,可能会在高并发时出现"打开文件过多"的错误。把nofile提到 65535,改在 systemd unit 的LimitNOFILE里,比重启整个系统影响面小得多。

6. 几条我踩过坑之后总结的经验

6.1 那些低级但致命的小问题

第一,尽量不要复用同一把私钥给所有节点。一开始图省事复制粘贴,后来某个节点退役了,私钥还散落在各处,只能全量轮换一遍,工作量比当初多建几把密钥大得多。第二,节点目录一定不要建在/tmp下,很多系统会定期清理/tmp,某天重启之后工作目录连同 Agent 的 jar 一起消失了,报错信息还指向一个根本不存在的路径。第三,SSH 方式下一定要确认节点的~/.ssh目录权限是 700,authorized_keys 是 600,这两个数字我见过太多人栽在上面,而且 SSH 的报错往往只说"认证失败",不会告诉你真正原因是权限。

第四,关于 Jenkins 插件和升级,如果你在内网环境,插件市场访问不了的时候要提前准备好离线插件包,或者把更新站点配置成可访问的镜像地址,这类配置在离线环境和受限网络里是必须项,否则某天需要装个插件会让你很被动。

6.2 节点规模上来以后怎么维护

当节点从两三个变成十几个,手工维护就不现实了。我现在基本采用"镜像化 + 标签规范"两条腿走路。镜像化是指所有节点的环境用同一份系统镜像或者 Docker 镜像构建出来,JDK 版本、目录结构、用户配置全部一致,出问题直接重建而不是登机器修。标签规范是指标签的命名规则写进团队文档,谁加节点都要遵守,避免出现jdk17、jdk-17、JDK17三种写法混用导致调度失灵的尴尬局面。

如果是纯粹为了跑构建、对环境一致性要求高的场景,其实可以考虑用容器化的动态节点,任务来了自动起一个容器,跑完销毁,环境天然干净。这类方案适合构建量波动大、又不想长期维护一堆物理机的团队,代价是镜像构建和拉取会有额外开销,冷启动慢一些。我在构建量大的项目上用过这种模式,配合镜像预热之后效果很好;构建量小的时候还是固定节点更划算。

至于节点健康监控,我一般会写一个定时流水线,每隔一段时间遍历所有节点,检查在线状态、工作目录剩余空间、Java 版本是否仍然匹配,结果推到内部通知渠道。这套自检跑起来之后,节点掉线这个问题从"用户报障才发现"变成了"提前十分钟自己知道"。

再补一个小技巧。如果你需要在节点上跑 Docker 构建,把节点上的 jenkins 用户加入 docker 组是常见的做法,但要清楚这等于给了这个用户近似 root 的权限,因为它可以挂载宿主机文件系统。所以要么接受这个风险,要么改用无 root 的构建工具方案。这个取舍没有标准答案,取决于你的安全边界要求有多高。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询