Vercel Workflow DevKit 部署錯誤:解決 Nitro Docker Module Not Found 問題

在 Vercel Workflow DevKit 架構中使用 Redis PUB/SUB 模組時遇到 Cannot find module '@workflow-worlds/redis' 錯誤?本文分享如何在 Nitro 與 Docker 環境下,透過調整 WORKDIR 解決 module not found 問題,含完整 Dockerfile 範例與教學。

CChia1104
筆記3 分鐘閱讀

Module Notfound 問題

這一陣子在用 Vercel 的 Workflow DevKit 處理文章生成 embeddings 的 task queue 架構,當中使用到 Redis PUB/SUB 架構的 @workflow-worlds/redis 這模組。

不過在部署環境遇到 module notfound 的問題:

Cannot find module '@workflow-worlds/redis'

由於自己是用 Monorepo 的架構,並且自己的後端是用 Nitro 在 serve 的,原本的專案結構這樣:

package.json

Nitro build 完後會有一個 .output 的 folder 跟已 transpile 過的 node_modules 結構:

index.mjs
package.json
package.json
package.json
package.json

那時候建置時的 Dockerfile 是這樣寫的:

ARG NODE_VERSION=24

FROM node:${NODE_VERSION}-alpine AS base

ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
ENV TURBO_TELEMETRY_DISABLED=1

WORKDIR /app

RUN apk update && \
    apk add --no-cache \
    python3 \
    build-base \
    gcc \
    libc6-compat && \
    npm install -g corepack@latest && \
    corepack enable pnpm && \
    pnpm add -g turbo && \
    ln -sf /usr/bin/python3 /usr/bin/python

FROM base AS pre-builder

COPY . .

RUN turbo prune --scope=service --docker

FROM base AS builder

COPY --from=pre-builder /app/out/json/ .
COPY --from=pre-builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=pre-builder /app/out/full/ .
COPY turbo.json turbo.json

ARG TURBO_TEAM \
    TURBO_TOKEN

ENV TURBO_TEAM=$TURBO_TEAM \
    TURBO_TOKEN=$TURBO_TOKEN

RUN pnpm i

RUN pnpm turbo run build --filter=service...

FROM base AS runner

COPY --from=builder /app/apps/service/.output/server ./apps/service

ENV NODE_ENV=production \
    PORT=8080

EXPOSE 8080

CMD [ "node", "app/apps/service/index.mjs" ]

所有 layer 的 workdir 統一都在 app,由於 workflow 是透過我們指定 WORKFLOW_TARGET_WORLD 這環境變數後在內部 @workflow/core/runtime 動態載入對應的 module

const require = createRequire(join(process.cwd(), 'index.js'));

@workflow/core/runtime

這裡會透過我們運行 node 指令的位置來載入 @workflow-worlds/redis,這意味著我們起 server 的 entrypoint 指令檔需要跟含有 @workflow-worlds/redisnode_modules 同一個層級

解法

重新指定 WORKDIR

ARG NODE_VERSION=24

FROM node:${NODE_VERSION}-alpine AS base

ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
ENV TURBO_TELEMETRY_DISABLED=1

WORKDIR /app

RUN apk update && \
    apk add --no-cache \
    python3 \
    build-base \
    gcc \
    libc6-compat && \
    npm install -g corepack@latest && \
    corepack enable pnpm && \
    pnpm add -g turbo && \
    ln -sf /usr/bin/python3 /usr/bin/python

FROM base AS pre-builder

COPY . .

RUN turbo prune --scope=service --docker

FROM base AS builder

COPY --from=pre-builder /app/out/json/ .
COPY --from=pre-builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=pre-builder /app/out/full/ .
COPY turbo.json turbo.json

ARG TURBO_TEAM \
    TURBO_TOKEN

ENV TURBO_TEAM=$TURBO_TEAM \
    TURBO_TOKEN=$TURBO_TOKEN

RUN pnpm i

RUN pnpm turbo run build --filter=service...

FROM base AS runner

COPY --from=builder /app/apps/service/.output/server ./apps/service

ENV NODE_ENV=production \
    PORT=8080

WORKDIR /app/apps/service

EXPOSE 8080

CMD [ "node", "index.mjs" ]

Written by: Chia1104 CC BY-NC-SA 4.0

Chia1104
©
Chia1104