CI 里 npm ci 比本地 npm install 还慢,偶尔还报 EINTEGRITY

举报
yd_271851511 发表于 2026/11/06 10:22:31 2026/11/06
【摘要】 同样的项目,本地 npm install 一分半,流水线里 npm ci 要四分半,还时不时因为 EINTEGRITY 直接失败。问题不在 npm ci 本身,而在缓存目录、lockfile 的 resolved 地址和镜像源同步延迟。三个坑逐个说明,照着改能把安装时间压回 45 秒。

团队把流水线从「每次 npm install」换成「npm ci」之后,构建时间不降反升:本地跑 `npm install` 一分半,流水线里 `npm ci` 要四分半,而且两周内失败了三次,报错都是 `EINTEGRITY`。花了一个下午查清楚,是三个独立的问题叠在一起。


一、npm ci 到底做了什么


先把「为什么 npm ci 更该在 CI 里用」说清楚,再说它为什么在这里变慢。


`npm ci` 的行为是固定的:


1. 如果 `package-lock.json` 和 `package.json` 不一致,直接报错退出(这其实是好事,能挡住「本地装完忘了提交 lock」);

2. **删掉整个 `node_modules`**,然后完全按 lockfile 里的 `resolved` 地址和版本重新装一遍;

3. 不做版本求解,所以理论上比 `npm install` 快得多。


它慢只有一种可能:**每次都在从零下载**。这也是第一个坑。


二、坑一:缓存的目录错了


很多流水线模板缓存的是 `node_modules` 目录。这个做法对 `npm ci` 完全无效——它第一步就把 `node_modules` 删了,缓存恢复出来的东西一行都用不上,反而多花几十秒解压。


真正该缓存的是 npm 的全局缓存目录:


```bash

npm config get cache # 一般是 ~/.npm

```


流水线里对应地缓存 `~/.npm`(Windows 上是 `%LocalAppData%\npm-cache`),缓存 key 用 lockfile 的哈希:


```yaml

cache:

  key: npm-${{ hashFiles('package-lock.json') }}

  paths:

    - ~/.npm

```


配上之后,第二次构建就从本地缓存装,`npm ci` 会自己优先用缓存里的 tarball。实测从 4 分半降到 1 分 10 秒。


再加两个参数,能省掉一堆没必要的网络请求:


```bash

npm ci --prefer-offline --no-audit --no-fund

```


`--prefer-offline` 是「缓存里有就直接用,不再校验远端」;`--no-audit` 关掉安装后的漏洞扫描(它会额外发一次 bulk advisory 请求,国内网络下经常超时重试);`--no-fund` 只是少打印点东西。三项加起来又能省 20 秒左右。


三、坑二:lockfile 里的 resolved 指向了另一个源


这个问题更隐蔽。`package-lock.json` 里每个包都有 `resolved` 字段,写着 tarball 的完整下载地址:


```json

"node_modules/lodash": {

  "version": "4.17.21",

  "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",

  "integrity": "sha512-..."

}

```


如果流水线用 `.npmrc` 把 registry 改成了内网镜像,npm 会怎么处理?答案是**看版本**:


- npm 7 及以上:如果 `resolved` 的 host 和当前 registry 不一致,会**改写成当前 registry** 再下载;

- 更早的版本(以及某些 npm 兼容实现):直接按 `resolved` 下载,`.npmrc` 里的 registry 形同虚设。


我们踩的是第二种。流水线为了能访问,配了内网镜像,但 lockfile 是开发者在本机用官方源生成的,结果安装阶段仍然在往 `registry.npmjs.org` 发请求,走代理超时重试,这才是四分半的主要来源。


判断方法很简单,看安装日志里的下载地址:


```bash

npm ci --loglevel=http 2>&1 | grep -o 'https://[^ ]*tgz' | sort -u | head

```


如果打印出来的 host 不是你配的那个源,就是这个问题。


修法有两个,选一个即可:


1. 在流水线里统一执行一次 `npm install --package-lock-only`,让 lockfile 的 `resolved` 改写成内网地址,然后提交(缺点是开发者的本地环境也得用同一个源);

2. 更推荐:**保持 lockfile 用官方源地址**,只把 npm 的 registry 换掉,并确认流水线用的 npm 版本在 7 以上。这样 lockfile 是源无关的,谁都能用。


顺带说一句,`resolved` 里如果写的是 `http://` 而不是 `https://`,某些新版 npm 会直接拒绝,报的错和源不可达很像,别被误导。


四、坑三:EINTEGRITY 不是文件坏了,是镜像没同步完


三次失败都是这个:


```

npm error code EINTEGRITY

npm error sha512-xxxxx integrity checksum failed when using sha512

```


`integrity` 是 tarball 内容的 sha512,和从哪个源下载无关。所以这个错误的含义只有一个:**下载到的内容和 lockfile 里记录的不一致**。


常见原因按概率排:


1. **镜像源同步延迟或同步出错**。上游发了新版本,镜像抓了一半,或者抓的是重新打包过的 tarball(有些私有代理会重打包,字节变了哈希也变了)。表现是「同一个包,昨天能装今天不能装」。

2. **缓存里存了半个文件**。上一次构建被中断,tarball 只写了一部分但缓存认为有效。清缓存即可:`npm cache verify`,必要时 `npm cache clean --force`。

3. **私有包的版本被覆盖发布**。npm 允许 `--force` 重发同一版本,哈希就变了,而 lockfile 里还是旧哈希。这种情况只能重新生成 lockfile。


排查顺序:先 `npm cache verify`(它会校验缓存并清掉坏条目),不行就换个源重试,再不行才怀疑 lockfile。


五、最终效果和一条经验


改完之后流水线是:缓存 `~/.npm` + `npm ci --prefer-offline --no-audit --no-fund` + 固定 npm 版本(`corepack` 或 `packageManager` 字段)。安装阶段从 4 分 30 秒降到 45 秒,两个月没再出现 EINTEGRITY。


一条经验值得单独记下来:**`npm ci` 的价值是「可复现」,不是「快」**。它快是因为不用求解版本,但如果缓存没配对,它比 `npm install` 更慢——因为它一定会从零开始。所以换 `npm ci` 的同时必须把缓存目录改对,这两件事是一套的。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。