夜雨聆风学习资料网

ARTICLE · 1040468

D0204. Jenkins 插件装不上?403 与改升级源报错的实战修复手册

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

两个关键事实:

  1. 元数据和下载地址绑在同一个 JSON 里
    。下载地址没有独立配置项,完全由 update-center.json 里的 url 字段决定。
  2. 官方源会按访问来源做镜像路由
    ,把下载 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 到底在哪

部署方式
JENKINS_HOME
说明
Docker(官方镜像默认)
/var/jenkins_home
容器内路径;宿主机一侧是 -v 挂载的目录(如 /data/jenkins_home),可直接在宿主机改
包安装(DEB/RPM)
/var/lib/jenkins
直接在宿主机文件系统
WAR 包随意起
~/.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.xml
  • UI: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.jpi 9.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 重新生成的动作有三个:

  1. 手动点 Manage Jenkins → Plugins → Advanced → Check now
  2. Jenkins 内置的周期性更新中心刷新(默认开启);
  3. 安装/升级插件时自动刷新元数据。

只要它重新生成,官方元数据里的清华镜像又会写进下载地址。所以一次性 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

八、进阶:嫌脚本麻烦?自建改写版更新中心

如果连定时脚本都嫌维护,更彻底的做法是自建更新中心

  1. 元数据每日从官方 update-center.json 同步;由于改写 url 会让官方签名失效,需配套关闭下载签名校验(-Dhudson.model.DownloadService.noSignatureCheck=true)或自签 CA;
  2. 同步后用脚本把里面的下载地址改写成华为云;
  3. 用 Nginx 托管这个改写后的 JSON 和代理下载;
  4. cron 每日刷新一次。

一次配置永久生效,Web 端装插件再无阻碍。适合多 Jenkins 实例、或需要完全离线/内网可控的场景。

九、常见问题速查

现象
根因
处理
docker exec ... bash -c
 报 No such file or directory
容器里没有 bash,只有 sh
换成 sh -c,或 docker exec -it 容器 sh 进容器跑
sed
 报 No such file or directory / default.json 不存在
还没生成过元数据
先去 UI 点一次「检查更新」或装一个插件,让它生成
改完还是 403
没重启 / default.json 又被刷新覆盖 / 匹配串不符
重启;跑自愈脚本;确认 grep 官方计数已为 0
华为云也 403
出口代理/防火墙拦了 .hpi
配代理或换腾讯云/阿里云,或放通域名
想用腾讯云
华为云不通
把脚本里域名换成 mirrors.cloud.tencent.com/jenkins/plugins/
macOS 本地跑 sed -i 报错
BSD sed 要求备份扩展
服务器是 Linux(GNU sed)无此问题;本地测试加 sed -i ''
改了升级源后列表空白/签名错
镜像元数据 404 或签名不过
回滚升级站点到官方(4.3 节)

系列文章 · DevOps 实战系列

本系列从基础环境到 CI/CD 平台化落地,建议结合 D0000 总纲按模块阅读。完整大纲见 D0000. DevOps 实战系列。

编号
主题
状态
D0000
DevOps 实战系列(整体方案与大纲)
已发布
D0001
操作系统初始化(CentOS 7,扩展 Ubuntu/AliOS)
已发布
D0002
防火墙策略
已发布
D0101
OpenLDAP 统一认证
已发布
D0102
JumpServer 运维审计
已发布
D0103
OpenVPN 远程接入
已发布
D0104
NginxWebUI 反向代理
已发布
D0105
内网导航
待发布
D0201
GitLab 安装配置
已发布
D0202
GitLab 备份与恢复
已发布
D0203
Gitlab升级与迁移
已发布
D0204
Jenkins 插件安装
本文
D0205
Jenkins 服务迁移
待发布
D0206
Nexus 安装配置
待发布
D0207
Nexus 迁移
待发布

上一篇:D0203. Gitlab升级与迁移 | 下一篇:D0205. Jenkins 服务迁移

相关学习资料