服务器部署 fabricjs 踩坑记录

164 阅读5分钟

起源

最近,open-idea 使用 fabricjs 重构了白板功能,同时白板也支持了多人协作,虽然目前还没有做 cursor chat,但是对白板的增删改都能实时同步给其他人。但是当我部署的时候却遇到了问题(踩了坑),下面就来详细说说这个问题(坑),关于为什么选择 fabricjs 以及这个白板功能是如何实现的,我们放到下篇去聊,这篇主要讲部署依赖 fabricjs 的应用如何避免踩坑。

体验地址:www.openbytecode.com/open-idea/s…

白板功能图-1 image.png

白板功能图-2 image.png

服务器环境

先说下我本地开发用的是 windows,而且 node 版本 18 和 20 都用过,都没有问题。 但是当我部署到线上时却遇到了问题,下面是线上(linux)的环境

node:20, 从下面的 DockerFile 即可看出 next: 14.1.0, react: 18.2.0

FROM node:20-alpine AS base  
  
# Install dependencies only when needed  
FROM base AS builder  
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.  
RUN set -eux && sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories  
RUN apk add --no-cache build-base libc6-compat cairo-dev pango-dev jpeg-dev giflib-dev pixman-dev python3 libcairo  
  
# https://pnpm.io/docker  
# https://depot.dev/docs/languages/node-pnpm-dockerfile  
ENV PNPM_HOME="/pnpm"  
ENV PATH="$PNPM_HOME:$PATH"  
  
RUN corepack enable  
RUN corepack prepare pnpm@latest --activate  
  
# Set npm registry explicitly to ensure output  
RUN npm config set registry https://registry.npmmirror.com  
  
WORKDIR /app  
  
# Install dependencies based on the preferred package manager  
COPY .npmrc package.json pnpm-lock.yaml* ./  
  
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install  
  
...

报错信息

报错信息如下,关键信息 Error: Cannot find module '../build/Release/canvas.node'

err: #16 19.19 Creating an optimized production build ...

err: #16 60.42 ✓ Compiled successfully

err: #16 60.42 Collecting page data ...

err: #16 62.77 Error: Cannot find module '../build/Release/canvas.node'

err: #16 62.77 Require stack:

err: #16 62.77 - /app/node_modules/.pnpm/canvas@2.11.2/node_modules/canvas/lib/bindings.js

err: #16 62.77 - /app/node_modules/.pnpm/canvas@2.11.2/node_modules/canvas/lib/canvas.js

err: #16 62.77 - /app/node_modules/.pnpm/canvas@2.11.2/node_modules/canvas/index.js

err: #16 62.77 -

为什么会有这个呢?那我们可以看看 fabricjs 的官方文档有没有说明

image.png

上面这段话翻译过来意思就是说:Fabric.js 依赖于node-canvas来实现画布(HTMLCanvasElement替代)和jsdom来实现window节点上的实现。这意味着您可能会遇到node-canvas限制和错误。 按照这些说明进行操作即可node-canvas开始运行。

如果想进一步了解 fabricjs 是如何依赖 node-canvas 的,那我们可以直接看 fabricjs 的 package.json

image.png

这里你可能会说这里依赖的是 canvas 呀,那 canvas 与 node-canvas 什么关系呀,ok,打开 npm 官网,我们直接搜索 canvas, www.npmjs.com/package/can… ,你会发现 canvas 就是 node-canvas。

image.png

但这些都不是重点,接下来我们点击[说明],进入到 node-canvas 项目。

image.png

他说默认情况下,如果您使用以下平台之一,则会下载预构建的二进制文件:

  • macOS x86/64(Apple 芯片)
  • Linux x86/64(仅限 glibc)
  • Windows x86/64

如果您想从源代码构建,请使用npm install --build-from-source并查看下面的编译部分。所需的 Node.js 最低版本是18.12.0

如果您没有受支持的操作系统或处理器架构,或者您使用--build-from-source,则模块将在您的系统上进行编译。这需要几个依赖项,包括 Cairo 和 Pango。

有关详细安装信息,请参阅wiki。常见操作系统的单行安装说明如下。请注意,libgif/giflib、librsvg 和 libjpeg 是可选的,仅在您需要 GIF、SVG 和 JPEG 支持时才需要。需要 Cairo v1.10.0 或更高版本。

接下来再看看我们的 DockerFile 文件,我们使用的 node 是 node:20-alpinenode:20-alpine 提供轻量级的 Node 环境,而且 Alpine 使用 musl 而不是 glibc注意了,都注意了哈,这里就是我们的问题(坑)所在

因此如果如果继续使用 node:20-alpine,那就需要执行源代码构建,并且需要 在 node:20-alpine 中安装 node-canvas 依赖的库,比如 cairopangojpeggiflib 等,更新你的 Dockerfile,添加这些依赖:

FROM node:20-alpine AS base

# 安装系统依赖
RUN apk add --no-cache build-base \
    cairo-dev \ 
    pango-dev \ 
    jpeg-dev \ 
    giflib-dev \ 
    pixman-dev \ 
    python3

我试了这种方法,虽然 build 通过了,但是启动应用页面直接打不开了,查看日志之后发现还是报错,报错信息如下:

⨯ Error: Error loading shared library libcairo.so.2: No such file or directory (needed by /app/node_modules/.pnpm/canvas@2.11.2/node_modules/canvas/build/Release/canvas.node) at Module._extensions..node (node:internal/modules/cjs/loader:1586:18) at Module.load (node:internal/modules/cjs/loader:1288:32) at Module._load (node:internal/modules/cjs/loader:1104:12) at Module.require (node:internal/modules/cjs/loader:1311:19) at mod.require (/app/node_modules/.pnpm/next@14.1.0_react-dom@18.2.0_react@18.2.0__react@18.2.0/node_modules/next/dist/server/require-hook.js:65:28) at require (node:internal/modules/helpers:179:18) at Object.<anonymous> (/app/node_modules/.pnpm/canvas@2.11.2/node_modules/canvas/lib/bindings.js:3:18) at Module._compile (node:internal/modules/cjs/loader:1469:14) at Module._extensions..js (node:internal/modules/cjs/loader:1548:10) at Module.load (node:internal/modules/cjs/loader:1288:32) { code: 'ERR_DLOPEN_FAILED' } ⨯ Error: Error loading shared library libcairo.so.2: No such file or directory (needed by /app/node_modules/.pnpm/canvas@2.11.2/node_modules/canvas/build/Release/canvas.node) at Module._extensions..node (node:internal/modules/cjs/loader:1586:18) at Module.load (node:internal/modules/cjs/loader:1288:32) at Module._load (node:internal/modules/cjs/loader:1104:12) at Module.require (node:internal/modules/cjs/loader:1311:19) at mod.require (/app/node_modules/.pnpm/next@14.1.0_react-dom@18.2.0_react@18.2.0__react@18.2.0/node_modules/next/dist/server/require-hook.js:65:28) at require (node:internal/modules/helpers:179:18) at Object.<anonymous> (/app/node_modules/.pnpm/canvas@2.11.2/node_modules/canvas/lib/bindings.js:3:18) at Module._compile (node:internal/modules/cjs/loader:1469:14) at Module._extensions..js (node:internal/modules/cjs/loader:1548:10) at Module.load (node:internal/modules/cjs/loader:1288:32) { code: 'ERR_DLOPEN_FAILED' }

后面我直接使用标准的 Node 镜像(如 node:20),这个 Node镜像通常会包含 canvas 所需的依赖库,因此无需手动安装 cairopango 等系统库。这次终于成功了,虽然这样会增加镜像大小,但会让依赖管理更简单。下面是更新后的 Dockerfile。如果你也遇到了这个问题欢迎一起交流。

FROM node:20 AS base  
  
# Install dependencies only when needed  
FROM base AS builder