mirrorOf= 把私服请求拦去了公网镜像:本地能跑,CI 上内部包 404 排查

69 阅读6分钟

老炮踩坑录 · 番外篇 · 翻车现场系列

基于「基础企业档案」真实项目代码,复盘一次"本地构建正常、CI 上内部包 404"的排查

关键词:mirrorOf · settings.xml · 镜像 · !排除写法 · effective-settings

专业术语:CI(持续集成)

👋 欢迎阅读 f18-csdn.jpg 🏠个人主页:知守观
📘我的专栏: 老炮踩坑录
💻当前内容:Maven 镜像拦截规则

前言

上一篇《# 同事的 SNAPSHOT 更新了,我三天后才拉到》讲了 _remote.repositories 的来源校验。那篇的坑出在仓库 id 对不上,这篇的坑还是出在仓库 id 上——在 settings.xml 文件里一行 mirror 配置,匹配的对象就是仓库 id。

时间线接着上一篇:私服迁移完成,old-nexus 的账清干净了,新私服用了两周,一切正常。然后 CI 时却挂了。

现象:本地能跑,CI 上 404

周五下午,我提交了一版代码,CI 构建失败,报错信息如下:

[ERROR] Failed to execute goal on project base-archives-web:
Could not resolve dependencies for project cn.xiaomayi:base-archives-web:jar:0.0.1-SNAPSHOT:
Could not find artifact cn.xiaomayi:base-archives-common:jar:0.0.1-SNAPSHOT
in alimaven (https://maven.aliyun.com/repository/public) -> [Help 1]

base-archives-common 是内部模块,就在公司私服上,上午同事还 deploy 过。

我在本地把同样的代码拉下来,同样的命令构建了一遍,结果是 BUILD SUCCESS。

同一份代码,同一份 pom,本地能下到的包,CI 上却报 404错误。

排查:先怀疑私服,再怀疑 pom

第一个怀疑对象是私服本身。于是直接用浏览器访问了私服上 common 的目录,jar 在,时间戳是今天上午的,说明私服没问题。

第二个怀疑对象是 CI 的网络。让运维同事看了下,CI 服务器网络也正常。

然后我回头看报错。in alimaven (https://maven.aliyun.com/repository/public) 这一段引起了我的注意:alimaven 这个 id,我们 pom 里没配过,仓库地址是阿里云的公网镜像。

一个公司内部的 groupId,出现在公网镜像的报错里。

公司内部包只存在于私服,去阿里云找必然 404。问题变了:CI 上的 Maven 为什么拿着内部包的坐标,去公网镜像下载依赖?

让运维把 CI 服务器上的 settings.xml 贴出来:

<mirrors>
    <mirror>
        <id>alimaven</id>
        <name>aliyun maven</name>
        <url>https://maven.aliyun.com/repository/public</url>
        <mirrorOf>*</mirrorOf>
    </mirror>
</mirrors>

报错里的 alimaven,就是它。

原因:mirrorOf 匹配的是仓库 id,* 匹配所有

mirror 的工作方式:Maven 发起仓库请求之前,先过一遍 settings.xml 里的 mirror 列表,看要访问的仓库 id 命中哪个 mirror 的 <mirrorOf> 规则。命中了,这个请求就交给镜像处理——URL 换成镜像的地址,id 换成镜像的 id,原仓库不再出现。

mirrorOf=* 的意思是接管所有仓库。私服 id 是 nexus,命中 *,于是下载 cn.xiaomayi:base-archives-common 的请求被换到了阿里云的地址。阿里云上没有这个包,所以报 404错误。

这份 settings.xml 是运维前一天换的。旧服务器的 settings.xml 好久没人维护了,他找了篇博客抄了一份新的,博客作者配阿里云镜像是为了加速,用的 *。抄过来的时候,没人想到公司里还有个私服。

三个关键事实,分开讲。

  1. mirrorOf 匹配的是仓库 id,不是 URL。 很多人以为 * 匹配的是"所有地址",以为写个私服的 URL 就能绕开。不行,匹配的是 pom 和 settings 里 <repository> 的 id。想排除私服,得在 mirrorOf 里写它的 id:
<mirrorOf>*,!nexus</mirrorOf>

! 是排除写法,意思是"除了 id 为 nexus 的仓库,其他全部用镜像"。

  1. mirror 管不到 deploy。 同事的 deploy 走的是 distributionManagement,和 mirror 是两套配置,互不相干。所以这次事故里 deploy 一直成功、只有拉取 404,两边看起来矛盾,实际上各走各的路。

  2. 报错里的 id 和地址是镜像的。 请求被镜像接管之后,日志和报错里出现的就是镜像的 id 和 URL,原仓库的名字不会出现。排查时看到陌生的仓库 id,先别急着翻 pom,去 settings.xml 的 mirrors 里找。 mirrorOf 的完整写法,四种:

<mirrorOf>*</mirrorOf>                  <!-- 所有仓库 -->
<mirrorOf>central</mirrorOf>            <!-- 只接管 central -->
<mirrorOf>*,!nexus,!snapshots</mirrorOf> <!-- 全部,但排除 nexus 和 snapshots -->
<mirrorOf>external:*</mirrorOf>         <!-- 所有非本机的仓库(排除 localhost 和 file://) -->

修复

把 CI 的 settings.xml 改成显式排除私服:

<mirror>
    <id>alimaven</id>
    <name>aliyun maven</name>
    <url>https://maven.aliyun.com/repository/public</url>
    <mirrorOf>*,!nexus</mirrorOf>
</mirror>

改完重新构建,BUILD SUCCESS。

我倾向更保守的写法:只镜像 central,不用 *:

<mirrorOf>central</mirrorOf>

中央仓库走阿里云加速,其他仓库(私服、jitpack、各种第三方仓库)原样直连。镜像的范围越小,意外接管的面就越小。

还有一个连环坑也要提醒。如果 CI 的本地仓库是持久化的,这次事故期间下载过的所有第三方依赖,_remote.repositories 里登记的都是 alimaven。哪天把 mirror 整个撤了,仓库 id 又对不上了——上一篇 F08 的坑会原样重现。撤 mirror 的时候,要么保持镜像 id 不变,要么清一次本地仓库。

这个坑为什么难排查

报错指向是歪的。看到 Could not find artifact ... in alimaven,第一反应是"pom 里什么时候配了个阿里云",翻完 pom 翻父 pom,什么都找不到。镜像接管这个环节在日志里不露痕迹,INFO 级别的输出里看不到"请求被换地址"这件事。

本地和 CI 的配置差异藏得深。settings.xml 不在代码仓库里,本地一份、CI 一份、每个同事一份,改任何一份都没有记录可查。这次的事故现场(CI 服务器)和问题源头(运维抄的配置)之间还隔着一个人,信息又多了一道衰减。

* 号这个写法流传太广。搜"Maven 阿里云镜像",前排文章清一色 mirrorOf=*,抄的人默认它是标准配置。对纯开源依赖的项目它确实没问题,一旦公司有私服,它就是定时炸弹。

自查清单

自查清单.png

老炮点评

settings.xml 这东西,入职配一次,抄来的,之后几年不碰。它的内容很少有人逐行读过,可它对构建的控制权比 pom 还大——pom 决定"要什么",settings 决定"去哪拿",后者改一行,前者的所有配置都可能落空。

"本地能跑,CI 却挂"这类问题,先 diff 环境,再怀疑代码。这次我从 404 错误到定位,绕的弯路全在"怀疑私服、怀疑 pom"上,方向反了。


延伸阅读: 前两篇番外篇讲了多模块没install和SNAPSHOT拉不到,这篇讲mirrorOf把私服拦去公网。三篇构成了Maven仓库三部曲,建议一起看


下期预告:《IDEA 里藏着的第二份 settings.xml》

settings.xml 明明改对了,命令行构建也正常了,IDEA 里的依赖还是红的。翻遍设置才发现,IDEA 的 Maven 配置里 override 了另一份 settings.xml,路径指向一个两年前就不存在的目录。

下期讲 IDEA 和命令行各用各的配置时,怎么让它们说同一种话。

如果本文对你有帮助,欢迎:

👍 点赞 | ⭐ 收藏 | 👤 关注| 💬 留言

我是老炮,Java 老兵,仍在一线。关注「Java老炮踩坑录」,看真实案例,少踩坑。