ARTICLE · 1040468
D0204. Jenkins 插件装不上?403 与改升级源报错的实战修复手册
装个 Jenkins 插件能有多难?结果满屏
403;照着老教程去「升级源」填清华镜像,反而报Signature verification failed。这不是你网络差,是插件源的「两层结构」在作怪。本文给出从诊断、改源、清残留到自愈脚本的完整操作手册,照着敲就能好。
一、先看症状:你是不是也中招了
1.1 故障 A:装插件满屏 403(默认就走清华镜像)
刚装完 Jenkins,进 Manage Jenkins → Plugins → Available 勾一批插件点安装,后台日志一片红:
java.io.IOException: Server returned HTTP response code: 403 for URL:
https://mirrors.tuna.tsinghua.edu.cn/jenkins/plugins/commons-lang3-api/3.20.0-109.ve43756e2d2b_4/commons-lang3-api.hpi
Caused: java.io.IOException: Failed to download from
https://updates.jenkins.io/download/plugins/commons-lang3-api/3.20.0-109.ve43756e2d2b_4/commons-lang3-api.hpi
→ https://mirrors.tuna.tsinghua.edu.cn/jenkins/plugins/commons-lang3-api/3.20.0-109.ve43756e2d2b_4/commons-lang3-api.hpi
记住那个 → 箭头:Jenkins 本来要下官方地址,却从内置镜像列表里挑了清华,把下载重定向过去,清华回了 403。
1.2 故障 B:照老教程改「升级源」,反而报签名错
网上老教程让你去 Manage Jenkins → Plugins → Advanced → Update Site,把地址填成清华镜像:
https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
提交后再点 Check now,反而更糟,两种表现:
清华的 updates/update-center.json现在返回 404(元数据已下线),更新中心加载失败,可用插件列表直接空白;即便指向确实有元数据的镜像(例如华为云 https://mirrors.huaweicloud.com/jenkins/update-center.json,注意没有/updates/),Jenkins 2.300+ 默认强制签名校验,而镜像的 JSON 没有 Jenkins 信任的签名,于是报:
There were errors checking the update sites:
Signature verification failed in update site 'default'
结论:在页面把升级源改成镜像,不是解法,是第二种故障。
1.3 一分钟自测:你的环境到底哪一层挂了
别猜,直接测。把你报错日志里 → 后面的那个真实下载 URL 取出来,只替换域名,逐个镜像探一下,谁通填谁:
# 把下面这行换成你日志里 '→' 后的真实 URL(即被 403 的那个 .hpi)
BAD_URL="https://mirrors.tuna.tsinghua.edu.cn/jenkins/plugins/commons-lang3-api/3.20.0-109.ve43756e2d2b_4/commons-lang3-api.hpi"
# 截掉已经失败的域名,只保留 /jenkins/... 之后的路径
PATH_ONLY="${BAD_URL#https://mirrors.tuna.tsinghua.edu.cn/jenkins/}"
for base in \
"https://mirrors.huaweicloud.com/jenkins" \
"https://mirrors.cloud.tencent.com/jenkins" \
"https://mirrors.aliyun.com/jenkins" \
"https://mirrors.tuna.tsinghua.edu.cn/jenkins" ; do
printf "%-55s -> " "$base"
curl -s -o /dev/null -w "HTTP %{http_code}\n" "$base$PATH_ONLY" --max-time 20
done
结果判读:
403,华为云/腾讯云 200 | ||
403,所有国内云都 403 | .hpi 下载 | |
404、其它 200 | ||
200 但 Jenkins 仍 403 | default.json |
二、根因:Jenkins 插件源是「两层结构」

graph TB
U[更新站点 Update Site<br/>hudson.model.UpdateCenter.xml] -->|下载| J[update-center.json<br/>插件元数据]
J -->|每个插件写死 url 字段| P[按 url 直接下载 .hpi]
style U fill:#e0f0ff
style P fill:#e0ffe0
两个关键事实:
- 元数据和下载地址绑在同一个 JSON 里
。下载地址没有独立配置项,完全由 update-center.json里的url字段决定。 - 官方源会按访问来源做镜像路由
,把下载 302 到清华;而你的环境到清华这条链路被拒(同一文件从别的网络访问是 200,问题在链路/镜像侧,不在文件缺失)。
所以两种故障其实是同一个机制的两面:

graph LR
M[官方 update-center.json] -->|内置镜像含清华| A[下载时挑清华 → 403]
M -->|有人把升级站点改成镜像| B[元数据 404 / 签名不过 → 报错]
style A fill:#ffd0d0
style B fill:#ffd0d0
故障 A:你没动配置,但官方元数据里挑了清华去下载 → 403。 故障 B:你主动把「升级站点(元数据来源)」改成镜像 → 镜像没元数据或签名不过 → 报错。
三、先确认环境:部署形态与关键路径
动手前先确认三件事,后面所有命令都依赖它们。
3.1 部署形态:物理机/虚拟机 与 Docker 两类
Jenkins 常见部署形态就两类,先确认你属于哪一种:
- 物理机 / 虚拟机(包安装)
:用 DEB/RPM 或 WAR 直接装在操作系统上, jenkins_home就是宿主机上的一个目录(通常/var/lib/jenkins)。改文件直接在宿主机操作,最简单。 - Docker 容器
:用官方镜像起容器。Docker 部署的 jenkins_home通常已经 bind mount 到宿主机目录(启动参数-v /宿主机目录:/var/jenkins_home),所以你有两种改法——① 通过docker exec进容器里改(容器内路径/var/jenkins_home);② 直接改宿主机上那个挂载目录(bind mount 同一份文件,容器内立即生效)。本文 4.1 演示形式 ①,7.4 演示形式 ②。
用下面命令判断:
# 是否有 jenkins 容器在跑(命中即为 Docker 部署)
docker ps --filter "name=jenkins" --format '{{.Names}}'
# 是否有包安装的服务(命中即为物理机/虚拟机部署)
systemctl status jenkins 2>/dev/null | head -3 || echo "非包安装"
二者必有其一。容器化是现在的主流,本文以 Docker 为主路径,包安装只在路径上做差异说明。
3.2 JENKINS_HOME 到底在哪
/var/jenkins_home | -v 挂载的目录(如 /data/jenkins_home),可直接在宿主机改 | |
/var/lib/jenkins | ||
~/.jenkins |
后文用变量 JH 表示,Docker 设 JH=/var/jenkins_home,包安装设 JH=/var/lib/jenkins。
3.3 default.json 在哪、什么时候生成
$JH/updates/default.json
它不是安装时自带的,而是 Jenkins 首次成功拉取更新中心元数据后生成的。也就是说:
如果你刚初始化、还没拉过元数据,这个文件可能不存在——这时先去 UI 点一次「检查更新」,或至少装过一个插件,让它生成出来,再回来改。 每次点「Check now」、周期刷新、或安装插件触发刷新, default.json都会被重新生成,清华又会混进来。这就是为什么需要第六章的自愈机制。
3.4 当前升级站点配置在哪看
文件: $JH/hudson.model.UpdateCenter.xmlUI: Manage Jenkins → Plugins → Advanced → Update Site文本框
正常应指向官方:
https://updates.jenkins.io/update-center.json
四、立即可用解法:只改下载地址,不动升级站点
核心原则一句话——升级站点保持官方;只把 default.json 里的下载地址改写成国内云。一条 sed 覆盖全部插件,不动元数据、不触发签名校验。
4.1 Docker 环境完整操作(形式 A:进容器改)
Docker 部署的
jenkins_home一般已挂载到宿主机目录,你也可直接改宿主机挂载文件、免进容器(更快更稳,见 7.4)。本节演示「进容器」的写法(形式 A)。
# 1) 确认容器名(下面统一用 jenkins,按需替换)
docker ps --filter "name=jenkins" --format '{{.Names}}'
# 2) 进容器操作(注意:很多 Jenkins 镜像只有 sh,没有 bash)
docker exec -it jenkins sh
# ===== 以下在容器内执行 =====
cd /var/jenkins_home/updates
ls -l default.json # 若没有,先去 UI 点一次「检查更新」再回来
cp default.json default.json.bak
# 把插件下载地址从官方改写为华为云(已实测 200)
sed -i 's#https://updates.jenkins.io/download/plugins/#https://mirrors.huaweicloud.com/jenkins/plugins/#g' default.json
# 连通性检测从 Google 改国内,避免误报离线
sed -i 's#http://www.google.com#https://www.baidu.com#g' default.json
# 验证:官方地址计数应为 0,华为云应大于 0
grep -c "updates.jenkins.io/download/plugins/" default.json
grep -c "mirrors.huaweicloud.com" default.json
# ============================
exit # 退出容器
如果嫌交互麻烦,也可以一条命令搞定(注意用 sh -c,因为容器里没有 bash):
docker exec jenkins sh -c '
cd /var/jenkins_home/updates
cp -f default.json default.json.bak
sed -i "s#https://updates.jenkins.io/download/plugins/#https://mirrors.huaweicloud.com/jenkins/plugins/#g" default.json
sed -i "s#http://www.google.com#https://www.baidu.com#g" default.json
echo 官方计数: $(grep -c "updates.jenkins.io/download/plugins/" default.json)
echo 华为云计数: $(grep -c "mirrors.huaweicloud.com" default.json)
'
4.2 包安装环境完整操作
把 JH 换成 /var/lib/jenkins,直接在宿主机执行,其余 sed 完全一致:
JH=/var/lib/jenkins
cd "$JH/updates"
cp default.json default.json.bak
sed -i 's#https://updates.jenkins.io/download/plugins/#https://mirrors.huaweicloud.com/jenkins/plugins/#g' default.json
sed -i 's#http://www.google.com#https://www.baidu.com#g' default.json
grep -c "mirrors.huaweicloud.com" default.json # 期望 > 0
systemctl restart jenkins
4.3 故障 B 回滚:把升级站点改回官方
如果你已经把 Update Site 改成了镜像、现在报 404/签名错,两步还原。
方式一:改文件(最稳,不用进 UI)
编辑 $JH/hudson.model.UpdateCenter.xml:
<?xml version='1.1' encoding='UTF-8'?>
<hudson.model.UpdateCenter>
<sites>
<hudson.model.UpdateSite>
<id>default</id>
<url>https://updates.jenkins.io/update-center.json</url>
</hudson.model.UpdateSite>
</sites>
</hudson.model.UpdateCenter>
方式二:UI 改回
Manage Jenkins → Plugins → Advanced → Update Site 文本框填 https://updates.jenkins.io/update-center.json → Submit。
改完后必须重启 Jenkins 让配置生效,报错立刻消失。
顺序提醒:先把升级站点设回官方,再执行 4.1/4.2 改写
default.json,最后重启。这样元数据从官方取(签名有效),下载走国内云,两层都正确。
4.4 重新安装插件的正确姿势
- 初始化向导里失败
:等向导结束(或点「跳过插件安装」),再进 Manage Jenkins → Plugins → Available重新勾选安装。向导里的失败不会自动重试,手动重装最稳。 - 单个插件失败
:进 Installed看状态,Available里重新勾选即可。 改完源、重启后,安装页的下载现在走华为云,不会再 403。
五、清残留:删掉 0 字节的 .jpi.tmp
装失败会在 plugins/ 留下一堆空的临时文件。进容器 ls 一看,四五十个,全 0 字节:
-rwxr-xr-x 1 root root 0 Aug 21 22:42 asm-api.jpi.tmp
-rwxr-xr-x 1 root root 0 Aug 21 22:43 commons-lang3-api.jpi.tmp
-rwxr-xr-x 1 root root 0 Aug 21 22:44 pipeline-graph-view.jpi.tmp
5.1 怎么识别失败残留
.jpi.tmp且大小为 0:下载中断的空临时文件,可删。带真实大小的 .jpi(如bouncycastle-api.jpi9.9MB)和同名目录:已装好的插件,保留。
5.2 预览 + 删除(先预览,确认无误再删)
Docker(在容器内 /var/jenkins_home/plugins 执行):
# 预览
find /var/jenkins_home/plugins -name '*.jpi.tmp' -type f -size 0 -print
# 确认无误后删除
find /var/jenkins_home/plugins -name '*.jpi.tmp' -type f -size 0 -delete -print
包安装(宿主机 /var/lib/jenkins/plugins):
find /var/lib/jenkins/plugins -name '*.jpi.tmp' -type f -size 0 -print
find /var/lib/jenkins/plugins -name '*.jpi.tmp' -type f -size 0 -delete -print
5.3 误删防护
命令里带了 -size 0,只删空文件,不会误伤带真实大小的.jpi。删完用这条确认数量归零: ls /var/jenkins_home/plugins/*.jpi.tmp 2>/dev/null | wc -l。不删也能启动(真正加载的是带大小的 .jpi),但留着会让插件页显示「失败 / 破损」,清掉更干净。
六、验证修复:确认真的好了
别靠「感觉」,按这三步确认:
# 1) default.json 已改写:官方下载地址应为 0,华为云应 > 0
docker exec jenkins sh -c 'cd /var/jenkins_home/updates && \
echo 官方: $(grep -c "updates.jenkins.io/download/plugins/" default.json) ; \
echo 华为云: $(grep -c "mirrors.huaweicloud.com" default.json)'
# 2) 升级站点仍是官方
docker exec jenkins sh -c 'cat /var/jenkins_home/hudson.model.UpdateCenter.xml | grep -o "updates.jenkins.io/update-center.json"'
# 3) 实测拉一次:进 UI 装一个轻量插件(如 git),看 $JH/logs 里无 403
如果第 3 步仍出现 403,回头查 1.3 的自测——多半是华为云在你的网络也被拦,按第七章配代理或换腾讯云/阿里云。
七、根治:让下载源「自愈」,不再复发
default.json 会在你点「Check now」或周期刷新后重新生成,清华又会回来 → 再 403。把改源做成脚本,定时跑即可。
7.1 为什么会复发
触发 default.json 重新生成的动作有三个:
手动点 Manage Jenkins → Plugins → Advanced → Check now;Jenkins 内置的周期性更新中心刷新(默认开启); 安装/升级插件时自动刷新元数据。
只要它重新生成,官方元数据里的清华镜像又会写进下载地址。所以一次性 sed 不够,需要「刷新后自动改回」。
7.2 自愈脚本 jenkins-mirror-fix.sh
#!/bin/sh
# jenkins-mirror-fix.sh —— 刷新更新中心后,自动把下载源改回国内云
JH=/var/jenkins_home
JSON="$JH/updates/default.json"
[ -f "$JSON" ] || exit 0
cp -f "$JSON" "$JSON.bak"
# 官方下载基址 → 华为云(已实测 200;若你的网络不通,把下面域名换成
# mirrors.cloud.tencent.com/jenkins/plugins/ 或 mirrors.aliyun.com/jenkins/plugins/)
sed -i 's#https://updates.jenkins.io/download/plugins/#https://mirrors.huaweicloud.com/jenkins/plugins/#g' "$JSON"
sed -i 's#http://www.google.com#https://www.baidu.com#g' "$JSON"
echo "mirror fixed: $(grep -c 'mirrors.huaweicloud.com' "$JSON") entries"
7.3 把脚本放进去 + 宿主机定时跑
挂载进容器(假设你用 -v /opt/jenkins-data:/var/jenkins_home 起的容器):
# 把脚本放到宿主机挂载目录,容器里即出现在 /var/jenkins_home/ 下
cp jenkins-mirror-fix.sh /opt/jenkins-data/jenkins-mirror-fix.sh
chmod +x /opt/jenkins-data/jenkins-mirror-fix.sh
宿主机 cron 兜底(每 30 分钟跑一次,刷新后也不怕清华回来):
# crontab -e
*/30 * * * * docker exec jenkins /var/jenkins_home/jenkins-mirror-fix.sh >> /var/log/jenkins-mirror.log 2>&1
包安装不用 docker exec,直接让 cron 跑脚本(把 JH 改成 /var/lib/jenkins):
*/30 * * * * /usr/local/bin/jenkins-mirror-fix.sh >> /var/log/jenkins-mirror.log 2>&1
7.4 Docker 挂载目录直改(bind mount 场景,最省事)
Docker 部署时 jenkins_home 通常已 bind mount 到宿主机目录(启动命令里的 -v /宿主机目录:/var/jenkins_home)。这种情况下完全不需要 docker exec 进容器——宿主机上那个目录就是容器内文件的同一份(bind mount 同一 inode)。直接在宿主机改挂载目录里的 default.json 即可,不受容器内有没有 bash、能不能 docker exec 的限制,比 7.3 的 docker exec 写法更稳。
Docker 改
default.json有两种等价形式,选其一即可: - 形式 A(进容器改):docker exec -it jenkins sh进容器,按 4.1 改/var/jenkins_home/updates/default.json; - 形式 B(直改宿主机挂载文件):在宿主机直接改/宿主机目录/updates/default.json(见下)。bind mount 同一 inode,容器内立即生效。
立即修(宿主机执行,把路径换成你实际的挂载目录):
JH=/data/jenkins_home # 换成你 -v 挂载的宿主机目录
JSON="$JH/updates/default.json"
[ -f "$JSON" ] || exit 1
sed -i 's#https://updates.jenkins.io/download/plugins/#https://mirrors.huaweicloud.com/jenkins/plugins/#g' "$JSON"
sed -i 's#https://updates.jenkins.io/download/war/#https://mirrors.huaweicloud.com/jenkins/war/#g' "$JSON"
sed -i 's#http://www.google.com#https://www.baidu.com#g' "$JSON"
echo "华为云计数: $(grep -c 'mirrors.huaweicloud.com' "$JSON") 官方计数: $(grep -c 'updates.jenkins.io/download/plugins/' "$JSON")"
改完重启 Jenkins 容器让配置重新加载:docker restart jenkins。
宿主机 cron 自愈(直接对挂载目录,不依赖 docker exec):
# crontab -e (宿主机上,用真实挂载路径)
*/30 * * * * sed -i 's#https://updates.jenkins.io/download/plugins/#https://mirrors.huaweicloud.com/jenkins/plugins/#g; s#https://updates.jenkins.io/download/war/#https://mirrors.huaweicloud.com/jenkins/war/#g; s#http://www.google.com#https://www.baidu.com#g' /data/jenkins_home/updates/default.json >/dev/null 2>&1
这种写法的好处:不依赖容器名、不需要容器里有 bash、宿主机重启后 cron 仍在(用系统计划任务或写入 /etc/cron.d/ 持久化)。
提醒:cron 只兜底「防复发」,当前这次 403 仍需先手动跑一次上面的立即修命令(或等下一个 cron 周期到来),再重启 Jenkins 重装。
7.5 多重镜像兜底
如果华为云在你的环境也被拦,把 7.2 脚本里的域名换成腾讯云或阿里云即可(均实测 200)。判断用哪个,直接跑 1.3 的自测脚本,挑返回 200 的填进去。
若所有国内云都 403,问题不在镜像选择,而在出口代理/防火墙拦截了 .hpi 下载:
在 Manage Jenkins → Plugins → Advanced → Proxy配置公司代理;或在 Jenkins 启动参数加 -Dhttps.proxyHost=... -Dhttps.proxyPort=...;或让防火墙放通 mirrors.huaweicloud.com/mirrors.cloud.tencent.com/mirrors.aliyun.com。
八、进阶:嫌脚本麻烦?自建改写版更新中心
如果连定时脚本都嫌维护,更彻底的做法是自建更新中心:
元数据每日从官方 update-center.json同步;由于改写 url 会让官方签名失效,需配套关闭下载签名校验(-Dhudson.model.DownloadService.noSignatureCheck=true)或自签 CA;同步后用脚本把里面的下载地址改写成华为云; 用 Nginx 托管这个改写后的 JSON 和代理下载; cron 每日刷新一次。
一次配置永久生效,Web 端装插件再无阻碍。适合多 Jenkins 实例、或需要完全离线/内网可控的场景。
九、常见问题速查
docker exec ... bash -cNo such file or directory | bash,只有 sh | sh -c,或 docker exec -it 容器 sh 进容器跑 |
sedNo such file or directory / default.json 不存在 | ||
default.json 又被刷新覆盖 / 匹配串不符 | grep 官方计数已为 0 | |
.hpi | ||
mirrors.cloud.tencent.com/jenkins/plugins/ | ||
sed -i 报错 | sed -i '' | |
系列文章 · DevOps 实战系列
本系列从基础环境到 CI/CD 平台化落地,建议结合 D0000 总纲按模块阅读。完整大纲见 D0000. DevOps 实战系列。
上一篇:D0203. Gitlab升级与迁移 | 下一篇:D0205. Jenkins 服务迁移