惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
About on SuperTechFans
Cloudbric
Cloudbric
C
CERT Recently Published Vulnerability Notes
G
GRAHAM CLULEY
V
Vulnerabilities – Threatpost
C
Cisco Blogs
T
Tenable Blog
P
Privacy International News Feed
T
The Exploit Database - CXSecurity.com
I
Intezer
AWS News Blog
AWS News Blog
IT之家
IT之家
博客园 - 司徒正美
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 【当耐特】
The Hacker News
The Hacker News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Spread Privacy
Spread Privacy
S
SegmentFault 最新的问题
博客园 - Franky
人人都是产品经理
人人都是产品经理
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
V
Visual Studio Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
H
Hacker News: Front Page
Latest news
Latest news
Scott Helme
Scott Helme
腾讯CDC
宝玉的分享
宝玉的分享
大猫的无限游戏
大猫的无限游戏
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
A
Arctic Wolf
S
Securelist
雷峰网
雷峰网
The GitHub Blog
The GitHub Blog
Project Zero
Project Zero
Google DeepMind News
Google DeepMind News
P
Palo Alto Networks Blog
F
Fortinet All Blogs
Schneier on Security
Schneier on Security
云风的 BLOG
云风的 BLOG
Security Archives - TechRepublic
Security Archives - TechRepublic
The Last Watchdog
The Last Watchdog
WordPress大学
WordPress大学
MongoDB | Blog
MongoDB | Blog
L
LINUX DO - 最新话题
S
Schneier on Security
NISL@THU
NISL@THU
Jina AI
Jina AI
M
MIT News - Artificial intelligence

程沛权 - 养了三只猫

声画共生:黄贯中的摇滚美学宇宙 适用于 Oxlint 与 Oxfmt 的 Oxc 优先工作流 Vue Picture Cropper 发布 1.x 版本 讲一讲背后的设计理念 年终总结:2025 年的一些回顾和 2026 年的一些小规划 Obsidian x 飞牛 NAS:打造免费的跨平台笔记同步与备份方案 适用于 ESLint V9 的现代化扁平化配置 本色十年 记录一次 ERR_INCOMPLETE_CHUNKED_ENCODING 的问题排查 使用 remark-directive 为 Unifiedjs 提供 Markdown 视频语法的解析 干净的 TypeScript 项目在编译时报错 Cannot find module 'undici-types' 的原因和解决 macOS 基于 Android Studio 修改模拟器 Hosts 给猫儿子的新年礼物 它很喜欢! 年终总结:2023 年的一些回顾和 2024 年的一些小规划 千元预算组装入门 NAS 设备 分享 NAS 的硬件基础知识 我写了一本书《前端工程化:基于 Vue.js 3.0 的设计与实践》 想分享一下它背后的故事 Git的选择性合并操作笔记:合并某个版本或某个提交 年终总结:2022 年的一些回顾和 2023 年的一些小规划 知乎收藏夹助手:自动化将专栏的文章添加到收藏夹 用Vite更简单的解决Vue3项目的预渲染问题 Pinia怎么用?Vue3全局状态的管理工具Pinia教程 每天吃什么?上班带什么饭?程序员教你做菜啦!
解决 better-sqlite3 连接 SQLite 时报错 Could not locate the bindings file
chengpeiquan · 2025-02-15 · via 程沛权 - 养了三只猫

五年前买的阿里云 ECS 这个月底到期,前天准备续费的时候发现买个新的更划算,不仅价格差不多,还多了 1GB 内存,那还续个屌…… 服务器上要迁移的东西不多,影响不大,所以就直接买个新的了。

简单的迁移工作

由于老的服务器上部署的大多是前端项目(数据是连 Serverless 的 API 操作的,不在这台机器上),并且基本都是用 Docker 部署的,所以迁移工作都比较简单,在源码仓库上修改 Workflow 的目标机器 IP 和 SSH Key ,重新运行一次 CI 打包,就可以把新的镜像推送到新的服务器上了。

其他的像 SSL 证书, Nginx 配置,都是拷贝过去后重启一下 Nginx 就搞定,等服务都起来了,去 DNS 解析那里把域名指向新机器的 IP 就迁移完了,都问题不大。

除了有一个 Nest 服务,因为连了 SQLite ,迁移后出现了一点问题。

部署后运行报错

问题倒不是出在 SQLite 上,用 Docker 连接这种嵌入式数据库,都是通过 Volumes 挂载到宿主机器上的,所以访问的数据库文件路径是宿主机器上的路径,知道了这一点,把 SQLite 数据迁移到新服务器上也很简单,并且在新机器上直接用 SQLite 查询数据,也都没问题。

至于在 Docker 里使用 SQLite 本身,只需要在 Dockerfile 里,在安装 libc6-compat 的时候记得一起安装 sqlite 就可以。

但是 Docker 容器运行后,访问接口却挂了,通过 docker logs 查询容器的日志,发现启动后这里有个报错:

错误日志分析

在 Node 服务端程序连接 SQLite 是用了 better-sqlite3 这个库,它是 Node.js 中速度最快、最简单的 SQLite 库,在 Nestjs 里也是支持用 TypeORM 来基于这个库操作 SQLite 。

better-sqlite3 的产物

和普通的依赖包直接引入 dist 产物开箱即用不一样,它还需要编译一次原生绑定文件,默认情况下,它会尝试在安装时自动构建原生模块,对比本地在 node_modules 里的目录文件,和 npmjs 上的发布文件列表,会发现线上的发布版少了 better_sqlite3.node 这个文件,提示的报错信息也是少了这个文件。

本地在 node_modules 里的 better-sqlite3 目录文件
本地在 node_modules 里的 better-sqlite3 目录文件
npmjs 上的 better-sqlite3 发布文件列表
npmjs 上的 better-sqlite3 发布文件列表

构建脚本解析

在 better-sqlite3 的 package.json 里,可以看到 install 脚本就是执行这个安装后编译 Node.js 模块的操作。

这个 npm install 脚本,是 npm 的 生命周期 之一,当执行 npm install 时触发(其它包管理器如 pnpm install 也会触发)。如果 npm 包的根目录下有一个名为 binding.gyp 的文件,当没有自定义 install 或 preinstall 脚本时, npm 将默认使用 node-gyp rebuild 命令对 binding.gyp 进行编译。

node-gyp 是 Node.js 官方提供的跨平台命令行工具,用于编译 Node.js 的原生插件模块。

可以看到 better-sqlite3 的根目录下,也是存在一个 binding.gyp 文件,所以在 install 依赖的时候,better-sqlite3 会尝试使用 prebuild-install 来下载已编译好的二进制文件,如果没有找到匹配的文件,则会退回使用 node-gyp rebuild --release 来手动编译源代码。

better-sqlite3 这行 install 命令的意思是:

  1. prebuild-install:

    • 这个命令会首先检查是否有已经构建好的二进制文件(预编译的二进制文件)。 它是由 prebuild-install 这个工具提供的,用来自动下载已编译好的二进制文件,避免在安装时重新编译原生模块,这个步骤是为了加速安装过程,并且能够在大多数情况下避免编译原生代码,尤其是针对不同平台的预编译文件。
    • prebuild-install 会检查与当前 Node.js 版本和操作系统匹配的预编译包,如果找到了,则直接使用。
  2. node-gyp rebuild --release:

    • 如果没有找到匹配的预编译文件(比如第一次安装时或没有适用的二进制文件),则会执行 node-gyp rebuild,这会尝试从源代码编译原生模块。
    • node-gyp 是一个用于编译 Node.js 本地模块的工具,它会根据模块中的 C++ 源代码生成并编译 .node 文件。
    • --release 参数表示以发布模式编译(优化后的版本),而不是调试模式。

但以上虽然是理论上的预期方案和兜底方案,但不知道为什么在 CI 机器上居然都没有执行成功,导致最后缺少了这个编译好的二进制文件。

解决思路

既然确认问题就是因为少了这个编译的 Node.js 模块,那么可以尝试手动 build 一下,主动生成 better_sqlite3.node 文件。

因此在 Dockerfile 里,通过 pnpm i 之类安装项目依赖这一步的后面,添加如下代码:

手动安装编译需要的依赖,并手动执行 better-sqlite3 的构建脚本,主动生成运行程序需要的 better_sqlite3.node 文件,然后就一切恢复正常了!

感慨一下

之前 CI 构建没问题的时候是基于 Ubuntu 22.04.5 ,现在构建失败的时候是基于 Ubuntu 24.04.1 ,只能说 CI 机器的新系统环境少了一些预装的依赖,导致 node-gyp build 没有执行成功。

这次自己的主服务器迁移是从旧机器的 CentOS 7 迁移到新机器的 Debian 12 ,在选择 Debian 之前,还一度先装了 Ubuntu 24 ,然后看到一些建议说作为主服务器还是稳定优先,建议用 Debian ,所以就重新装了系统。

来自大佬的踩坑经验建议
来自大佬的踩坑经验建议

没想到话音刚落就在 CI 机器踩了 Ubuntu 的坑,害,对 Linux 这些系统版本不太熟,还是得多多学习呀。