Jenkins离线插件部署:依赖解析与避坑指南

发布时间:2026/10/16 13:35:12
Jenkins离线插件部署:依赖解析与避坑指南 简介针对内网环境无法访问Jenkins官方插件中心的问题这份配套资料提供了2.346.1版本所需的离线插件包与配置模板。包内共有2000个文件包含大量js、html前端资源以及jar、jpi、hpi等插件本体xml与properties则为配置和初始化所需整体压缩包约314MB。已有3171人学习过适合负责内网持续集成环境搭建的运维人员参考。资料围绕插件离线下载、离线插件包组织、jenkins.xml初始化配置、启动验证及后续更新维护展开包含插件清单、目录结构示例和版本匹配说明。通过这套资源读者可以快速梳理内网部署的完整流程避免逐一排查依赖关系同时了解如何在受限网络中保持Jenkins插件体系的安全与可控。1. 内网 Jenkins 2.346.1 离线部署插件没有外网插件依赖才是最大黑匣子在一台只能访问内网、无法连接公网的服务器上部署 Jenkins 2.346.1最折磨人的不是装 Jenkins 本身而是接下来这步装插件。你从公网文档里拷贝了 Git、Pipeline、Kubernetes 等插件名打开管理台发现上传按钮只认本地文件而内网机器上什么都没有。更麻烦的是Jenkins 插件之间还有一套复杂的依赖关系直接下载目标插件传进去启动时往往报错一大片。2.346.1 是 2022 年的 LTS 版本官方更新中心已经不再把它作为默认热推对象不少新版插件要么要求更高的核心版本要么引入了新的依赖项。于是离线部署插件成了一项专门技术活先在能联网的机器上把插件和依赖一次性收集齐再整体搬到内网安装。这篇文章就把这套流程拆开讲从依赖怎么查、文件怎么下载到内网怎么装、装完怎么验证连踩坑点一起给你过一遍。2. 先搞懂 2.346.1 的插件机制更新中心、索引文件与依赖关系2.1 插件文件长什么样.hpi、.jpi 与更新中心 JSON 的对应关系Jenkins 插件本质上是一个 Java Web 应用的小型组件打包成.hpi文件少数老场景下会以.jpi结尾两者格式相同Jenkins 启动时会扫描plugins目录下的这些文件并加载。在公网环境下Jenkins 管理台通过更新中心Update Center动态获取插件列表和下载地址但内网环境访问不到那个 URL所以必须手动下载插件包。更新中心的核心是一个庞大的 JSON 文件Jenkins 官方或者你配置的镜像源会定期生成。以官方源为例访问路径通常是https://updates.jenkins.io/update-center.json。这个 JSON 里有一个plugins字段里面包含所有可用插件的信息。对每个插件你能拿到这些关键字段name插件 ID比如git、pipeline-stage-view。version插件版本。url该版本.hpi文件的下载地址。gavMaven 坐标形如org.jenkins-ci.plugins:git:5.x.x。dependencies依赖列表每个依赖项又包含name、version、optional是否可选、impl是插件依赖还是核心嵌入依赖。注意impl这个字段很多人忽略它。有的依赖是 Jenkins 核心自己提供的即impl: core这种不需要单独下载而impl: plugin才表示这是一个真实的插件依赖必须一并下载。由于 2.346.1 核心版本已经有点年头现在很多插件会依赖更新的核心功能导致离线安装时频繁出现 Required Jenkins core version ... is newer than ... 的报错这就是后面避坑章要讲的重点之一。2.2 依赖树是离线的第一道坎为什么只下载目标插件必翻车很多初次做离线插件的人都有一个朴素想法想装 Git 插件就去下载git.hpi传进内网完事。结果 Jenkins 重启后系统管理里一片红“Git plugin 依赖 credentials/commons-lang 等插件缺失”。Jenkins 的依赖是传递性的Git 插件本身依赖 credentials 插件credentials 又依赖 commons-text 等一层套一层。官方更新中心的 JSON 里只给你直接依赖如果你的下载脚本不做递归解析那么装完必缺。依赖还有两个容易踩的维度可选依赖optional字段为 true 的依赖缺失时插件依然能加载但功能受限。批量下载时我倾向于把可选依赖也补齐因为你无法预料内网用户会点到哪个功能菜单。核心依赖某些依赖标记为impl: core但要求一个最小版本。例如依赖定义是core 2.346.1而你内网正好是这个版本那没问题如果插件要求core 2.361而 2.346.1 低于这个值那你直接下载它就会启动失败且这种失败往往不是缺文件而是插件被拒绝加载。所以离线准备阶段必须构建一棵依赖树。正确做法是从目标插件开始递归读取dependencies中所有非 optional 的impl: plugin依赖再去 JSON 的plugins字段里找对应插件的最新版本或者最兼容版本继续展开直到没有新的插件依赖为止。2.3 2.346.1 的兼容策略Required Core 与依赖版本匹配针对 2.346.1 这个具体版本我建议先给自己定一个选型边界只选择那些Required Core不高于 2.346.1 的插件版本。插件在发布时通常会声明minimum Java version和Required Jenkins core version。2.346.1 对应 Java 8/11 都能跑但如果你内网 JDK 是 8那么一堆要求 Java 11 的新版插件也不可用即便核心版本匹配。因此离线选型要同时满足插件requiredCore.version 2.346.1插件要求的 Java 版本 你的运行 JDK 版本这实际上是在告诉你不要一味追最新。例如某个插件的 v2.x 版本可能已经要求核心 2.414但同一个插件的 v1.8 版本还在 2.346.1 的兼容范围内。离线环境里“能用”永远比“最新”重要。在后续下载脚本里我会加一个 core 版本过滤参数宁可多下载几个候选版本也不要直接拉最新版然后拿到内网翻车。3. 在能联网的机器上离线打包插件抓取依赖并批量下载3.1 准备工具用 Python 脚本解析 update-center.json在能连接公网的机器上准备一个工作目录拉取官方更新中心 JSON。由于这个文件通常有 100MB 以上直接传给内网不现实我们只需要提取插件下载信息。# fetch_updates.py # 用法: python fetch_updates.py 2.346.1 import json import os import sys import urllib.request CORE_VERSION sys.argv[1] if len(sys.argv) 1 else 2.346.1 JSON_URL https://updates.jenkins.io/update-center.json TARGET_DIR jenkins-offline-plugins os.makedirs(TARGET_DIR, exist_okTrue) # 官方 update-center.json 实际是 jsonp 格式要去掉前置的函数包裹 req urllib.request.Request(JSON_URL, headers{User-Agent: Mozilla/5.0}) raw urllib.request.urlopen(req, timeout120).read().decode(utf-8) json_text raw[raw.find({): raw.rfind(}) 1] # 截取合法 JSON data json.loads(json_text) plugin_meta {} # 缓存插件名 - meta for name, meta in data[plugins].items(): plugin_meta[name] meta print(fupdate-center 已加载共索引插件 {len(plugin_meta)} 个)这段脚本首先把update-center.json下载下来。注意官方返回的是jsonp格式直接json.loads会失败因此我截取了第一个{到最后一个}之间的部分。这里有个细节data[plugins]是字典键是插件名值是包含所有版本信息的对象。实际上官方这个索引里每个插件名下通常只有一个“推荐版本”的完整信息但gav字段里有历史版本坐标如果要多版本选择需要再去https://updates.jenkins.io/download/plugins/name/页面解析一般我们直接取当前索引推荐的版本即可。如果你公司自建了更新中心镜像那么字符串截取规则可能不同。常见做法是直接用json库加上错误重试逻辑或者干脆用requests库获取后手动去掉前后缀。这个脚本我刻意避免额外依赖只用了标准库保证在任意联网机器上都能直接跑。3.2 下载目标插件及依赖脚本代码与参数说明现在编写核心下载逻辑。我把它放在同一个脚本里方便一次执行搞定所有依赖。# download_deps.py # 在上一篇解析脚本的基础上继续递归展开依赖并下载 .hpi import json import os import subprocess import sys from urllib.request import Request, urlopen TARGET_DIR jenkins-offline-plugins CORE_VERSION sys.argv[1] if len(sys.argv) 1 else 2.346.1 WANTED_PLUGINS sys.argv[2:] if len(sys.argv) 2 else [git, pipeline] # 先复用上一个脚本的数据加载部分省略以节省篇幅 # 假设 data 对象为全局变量此处模拟已加载的 plugin_meta with open(plugin_meta.json, r, encodingutf-8) as f: plugin_meta json.load(f) downloaded set() to_download list(WANTED_PLUGINS) def get_plugin(name): if name in plugin_meta: return plugin_meta[name] # 有些依赖名带核心前缀去掉再查 return plugin_meta.get(name.split(.)[-1]) while to_download: name to_download.pop() if name in downloaded: continue meta get_plugin(name) if not meta: print(f[跳过] 找不到插件 {name}) continue # 核心版本过滤只接受RequiredCore不高于目标版本的插件 req_core_str meta.get(requiredCore, 2.0) req_core_parts [int(x) for x in req_core_str.split(.)] target_parts [int(x) for x in CORE_VERSION.split(.)] if tuple(req_core_parts) tuple(target_parts): print(f[过滤] {name} 要求 core {req_core_str}高于 {CORE_VERSION}跳过) continue ver meta.get(version) dl_url meta.get(url) local_file os.path.join(TARGET_DIR, f{name}.hpi) if not os.path.exists(local_file): print(f[下载] {name} {ver} - {local_file}) req Request(dl_url, headers{User-Agent: Mozilla/5.0}) with urlopen(req, timeout60) as resp, open(local_file, wb) as f: f.write(resp.read()) downloaded.add(name) # 解析依赖继续加入队列 for dep in meta.get(dependencies, []): # 只看插件依赖core依赖是Jenkins核心自带的 if dep.get(impl) ! plugin: continue # 可选依赖也一并下载避免内网使用时功能异常 dep_name dep.get(name) if dep_name and dep_name not in downloaded: to_download.append(dep_name) print(f全部完成共下载 {len(downloaded)} 个插件文件目录: {TARGET_DIR})这里有几个参数说明WANTED_PLUGINS你想安装的目标插件 ID 列表可以一次传多个。CORE_VERSION内网 Jenkins 的核心版本脚本用requiredCore过滤掉高版本插件。依赖判断impl ! plugin直接跳过这就排除了core和impl: bundle这类不需要单独下载的依赖。optional依赖没有判断直接全部拉取这是故意为之。我曾经只下载 mandatory 依赖结果内网某个功能一打开就报错后来发现缺的是可选依赖。实际运行命令是python download_deps.py 2.346.1 git pipeline kubernetes脚本完成后jenkins-offline-plugins目录下就会出现所有目标插件和依赖的.hpi文件。3.3 检查下载物文件命名、完整性校验与目录整理下载完成后不要急着传输。先做三件事第一确认.hpi文件不是错误页面。有些下载源会给 404 页面返回 HTML 后缀的伪文件可以用file命令或者 Java 的jar tf来验证。.hpi本质是 zip 包正常文件首字节是PK。cd jenkins-offline-plugins # 检查所有 .hpi 是否为有效 zip for f in *.hpi; do if [ $(head -c 2 $f) ! PK ]; then echo $f 不是有效 zip可能是下载失败 fi done第二整理一份插件清单。我习惯把插件名和版本号输出成manifest.txt内网安装后对照验证。这个文件也能作为后续升级的基线。ls -1 *.hpi | sed s/.hpi// | sort manifest.txt cat manifest.txt | wc -l第三注意目录结构不需要嵌套。Jenkins 的plugins目录要求所有.hpi文件平铺不能放在子目录里。你传输到内网后也不需要解压直接作为一个整体放置。# 打包方便传输 tar czf jenkins-offline-plugins.tar.gz jenkins-offline-plugins打包后可以把 tar 包拷贝到内网服务器。常见的传输方式有 U 盘拷贝、企业内部文件传输平台、或者 HTTP 下载站这个根据你内网的实际情况来本文不过多展开。4. 内网 Jenkins 2.346.1 安装离线插件的三种落地方式4.1 管理台上传适合少量插件注意“可选依赖”的坑进入 Jenkins 管理台找到“系统管理 - 插件管理 - 高级”在“上传插件”区域选择本地的.hpi文件点击上传。上传后会立即触发安装流程Jenkins 会自动检测该插件的依赖并在安装完成后提示是否重启。这个方法最直观适合 plugin 数量少、且你已经确认所有依赖都在同一批上传的插件包里。但它有两个明显的坑上传插件时 Jenkins 会处于状态锁定期如果插件依赖另一个尚未上传的插件这一步会直接安装失败。界面上传不会自动递归处理依赖。它只是检查当前已存在插件能否满足依赖不会帮你从上传包里找依赖。因此建议如果走管理台上传至少把目标插件和其直接依赖一起逐个上传顺序上没有硬性要求但尽量先传底层依赖再传上层插件。这个判断依据同样是更新中心 JSON 里的依赖关系。4.2 CLI 安装用 jenkins-cli 脚本化安装与依赖检查如果插件数量多或者你想把部署过程写成脚本使用 Jenkins CLI 是更可靠的办法。先找到你的 Jenkins 地址和端口并准备一个可以登录的账号。2.346.1 的 CLI 入口通常是/jnlpJars/jenkins-cli.jar在内网服务器上可以用 curl 从本地 Jenkins 拉取。# 假设 Jenkins 已经运行在内网 10.0.0.8:8080 curl -O http://10.0.0.8:8080/jnlpJars/jenkins-cli.jar # 安装插件-plugin 参数可以重复出现也可接收多个 java -jar jenkins-cli.jar -s http://10.0.0.8:8080 -auth admin:密码 install-plugin ./git.hpi ./credentials.hpi # 查看安装进度确定是否需要重启 java -jar jenkins-cli.jar -s http://10.0.0.8:8080 -auth admin:密码 restart这里有几点说明-auth参数在 2.346.1 版本上推荐使用用户名:密码或用户名:API Token。如果 Jenkins 启用了 CSRF 保护CLI 会要求你额外传一个-crypt参数不过本地 JAR 在 2.346.1 上通常会通过会话自动处理。install-plugin命令只上传并安装指定文件它同样不会解析依赖。你必须在命令行里列出所有需要的.hpi。安装后很多插件需要重启才能生效CLI 的restart命令可以让 Jenkins 优雅重启。如果内网 Jenkins 有大量插件要装我一般会在传输前就把依赖树展开成清单然后用 shell 循环一次性安装。比如cat manifest.txt | while read plugin; do java -jar jenkins-cli.jar -s http://localhost:8080 -auth admin:密码 install-plugin ./${plugin}.hpi || true done注意这个|| true并不是无视错误而是有些插件已经在系统里CLI 会返回非零状态。脚本结束后再统一检查安装结果。4.3 直接放置 plugins 目录批量初始化时的最快路径最原始也最直接的方法把.hpi文件复制到 Jenkins 的plugins目录下然后重启 Jenkins。这个方法适合新装 Jenkins 后首次初始化或者你手动管理插件目录的场景。具体操作步骤如下找到 Jenkins 安装目录下的plugins目录。用发行版安装的通常在/var/lib/jenkins/plugins用 Docker 挂载的则在你的JENKINS_HOME/plugins。清空或备份旧插件。如果这是全新安装目录基本为空如果是已有环境建议不要删旧文件而是把新插件直接放入冲突的插件 Jenkins 会根据目录名覆盖。复制文件并设置权限。Jenkins 运行用户需要可读这些文件。# 假设 tar 包已经解压到当前目录 install -d /var/lib/jenkins/plugins/ cp -n jenkins-offline-plugins/*.hpi /var/lib/jenkins/plugins/ chown -R jenkins:jenkins /var/lib/jenkins/plugins/重启服务systemctl restart jenkins注意直接放插件目录不会做任何依赖检查Jenkins 启动时如果发现依赖缺失它会只加载那些依赖完整的插件并把有问题的插件标记为“不可用”。所以这种方式的成败完全取决于你准备阶段的依赖树是否完整。而且如果你的插件包里有旧版本Jenkins 启动时会尝试迁移、更新这些插件日志里会留下大量信息后面验证章节要仔细看日志。我通常只在两类场景用这种方式一是刚装完 Jenkins 内核、没有任何插件的新环境二是需要批量推送几十个插件、且想让 Jenkins 一次启动加载完的场景。其余情况都优先走 CLI。5. 离线插件部署避坑指南5 个反复出现的翻车点5.1 现象一上传时提示“该插件未包含在更新中心”现象内网 Jenkins 管理台在上传.hpi后页面顶部出现红色错误提示这个插件不在更新中心甚至拒绝安装。原因Jenkins 在上传插件时会对照更新中心索引验证插件的名称和版本。如果你的内网 Jenkins 没有配置镜像源或者根本没有可访问的更新中心它会使用默认索引。但更常见的情况是你下载的插件版本过新更新中心索引里查不到对应元数据Jenkins 就认为这个插件是“未知”的。解决修改内网的更新中心地址为本地文件或者指向一个内网可达的镜像。2.346.1 支持在“插件管理 - 高级”里手动填写“更新站点”URL。如果你在内网架设了一个静态文件服务器把update-center.json放上去填上对应的 URL让 Jenkins 能访问到这个索引再上传插件就不会报这个错了。如果实在不想架文件服务器可以在 Jenkins 启动参数里加-Djenkins.plugin.version-validator.ignoretrue但这只是绕过校验不解决根源不建议生产环境这么做。5.2 现象二插件依赖缺失导致 Jenkins 启动失败现象Jenkins 重启后无法正常启动或者后台日志刷出大量Plugin ... is missing transitive dependency ...的错误某些插件直接无法加载。原因准备阶段没有递归解析依赖只下载了目标插件。比如你想装 Kubernetes 插件它的依赖链非常深一旦漏掉某个依赖整个插件树都可能崩掉。解决回头重新执行第 3 章的递归下载脚本同时检查两个点依赖列表里是否过滤了optional依赖。我建议全量下载别偷懒。检查是否有插件名拼写错误导致脚本把它识别为找不到meta而跳过。另外还有一种依赖缺失专门在启动时报NoClassDefFoundError这种情况通常是某个插件依赖了一个更高版本的第三方库而另一个插件内置了旧版。此时你需要查看插件之间的兼容性优先选择同一依赖族下版本较高的插件或者干脆放弃装其中之一。5.3 现象三插件版本与 2.346.1 的核心版本不兼容现象插件能装进去但 Jenkins 日志显示Plugin x required at least Jenkins Core version 2.365, but the current version is 2.346.1插件完全不生效。原因下载脚本没有做requiredCore过滤或者直接从公网拉取了最新版插件。这是离线部署里最典型的一个版本错位内网核心还是 2.346.1但插件的“最新版”往往已经跟随新核心走了。解决回到下载脚本把CORE_VERSION明确设为2.346.1强制只下载requiredCore不高于这个值的插件版本。如果某个插件的新版本全都要求高核心而旧版本又有安全问题那只能升级 Jenkins 内核或者接受旧插件功能。我见过不少项目为了一个安全插件的新版被迫把整个 Jenkins 从 2.346.1 升到 2.4xx这是不小的动作。5.4 现象四插件装上后一直没生效状态显示“无法加载”现象插件管理页面显示该插件已安装但状态不是绿色而是带警告标志点开之后提示加载失败或者缺少某个扩展。原因有时不是依赖问题而是插件之间发生了ClassLoader冲突或插件要求的 Java 版本不满足。2.346.1 默认支持 Java 8 和 Java 11但许多新插件编译目标是 Java 11 甚至 17在 Java 8 上运行会直接报UnsupportedClassVersionError。解决先确认你的 Java 版本java -version如果是1.8建议找插件的历史版本用声明minimumJavaVersion: 8的版本。如果是11那还要注意jenkins.war启动时的JAVA_HOME是否指向正确。另外插件状态异常不一定在启动日志里明显体现你需要访问Jenkins 系统管理 - 系统日志搜索failed、Unable to load这些关键词。日志里通常会打印出具体的类名和缺失依赖。5.5 现象五离线源文件损坏下载了一半或换源后校验不通过现象拷贝到内网后执行安装时报ZipException或者重启后插件无法加载。原因公网网络不稳定导致.hpi下载不完整或者你从镜像源下载镜像源本身同步更新不完整生成了损坏的文件。另外有人用 Ftp/SMB 传输时会修改文件内容。解决准备阶段做一次 MD5 或 SHA256 校验。在联网机器上下载时同步记录每个文件的哈希值传输后在内网重新算一遍再安装。# 在联网机器上生成校验文件 sha256sum *.hpi checksums.txt # 在内网校验 sha256sum -c checksums.txt如果校验不通过重新下载那个插件。在脚本里我还会加一个--verify参数下载后立即用 Python 的zipfile.is_zipfile判断文件有效性失败则自动重试一次。这些细节能让你省去大量内网排错的时间。6. 验证离线插件生效并固化离线插件库插件全部装好并重启之后不要急着跟团队宣称“部署完成”。先做一轮完整的验证。最直接的是看系统管理里的插件列表状态是否全是绿色。但在无法打开图形界面的场景下我更习惯用 API 验证# 需要启用 CSRF 或者带 API Token 认证 curl -u admin:密码 http://127.0.0.1:8080/pluginManager/api/json?depth1 | python -m json.tool这个 JSON 里plugins数组包含每个插件shortName、version、enabled、active等字段。activetrue表示插件已加载。我用下面这个命令快速列出有问题插件curl -u admin:密码 http://127.0.0.1:8080/pluginManager/api/json?depth1 | \ python3 -c import sys,json; djson.load(sys.stdin); print([p[shortName] for p in d[plugins] if not p[active]])如果输出为空说明插件加载正常。但还要验证功能确实可用比如装了 Git 插件就创建一个测试 job在源码管理里选 Git 并填一个内网仓库地址拉取一次代码。装了 Kubernetes 插件就去测试 agent 连接。这一步能发现那些“加载成功但功能报错”的边缘问题。之后是固化离线插件库。我会把整个jenkins-offline-plugins目录连同 manifest 和 checksums 一起放到版本库里标记好对应的 Jenkins 版本 2.346.1。下次再搭新环境时不需要重新去公网解析依赖直接拿来用。还有一个技巧通过下载脚本生成一个install_commands.sh里面按依赖拓扑顺序排列好所有 CLI 安装命令。这样在新环境里执行一行bash install_commands.sh就能装完所有插件。我每次部署完都会顺手维护这个脚本。我自己在维护一个旧版本 Jenkins 集群时吃过太多依赖顺序的亏。后来养成了习惯任何离线插件操作都在前一个环境里用 API 导出插件清单并和源 JSON 做 diff确认没有多余或缺失文件之后再复制到生产。这个过程虽然繁琐但比到了现场发现插件装不上再回头找依赖要省太多时间。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询