在 Vercel Workflow DevKit 架構中使用 Redis PUB/SUB 模組時遇到 Cannot find module '@workflow-worlds/redis' 錯誤?本文分享如何在 Nitro 與 Docker 環境下,透過調整 WORKDIR 解決 module not found 問題,含完整 Dockerfile 範例與教學。
Written by: Chia1104 CC BY-NC-SA 4.0
這一陣子在用 Vercel 的 Workflow DevKit 處理文章生成 embeddings 的 task queue 架構,當中使用到 Redis PUB/SUB 架構的 @workflow-worlds/redis 這模組。
不過在部署環境遇到 module notfound 的問題:
Cannot find module '@workflow-worlds/redis'由於自己是用 Monorepo 的架構,並且自己的後端是用 Nitro 在 serve 的,原本的專案結構這樣:
Nitro build 完後會有一個 .output 的 folder 跟已 transpile 過的 node_modules 結構:
那時候建置時的 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'));這裡會透過我們運行 node 指令的位置來載入 @workflow-worlds/redis,這意味著我們起 server 的 entrypoint 指令檔需要跟含有 @workflow-worlds/redis 的 node_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" ]