我最近包装了一个Sveltekit应用程序.在我的Dockerfile中有两个主要目标,一个是dev,另一个是prod.该应用程序使用zod(https://github.com/colinhacks/zod)进行验证.它只在我定义的一些DTO类中使用,例如:

import { z } from 'zod';
import {DTO} from "./DTO.js";

const categoryValidationSchema = z.object({
    name: z
       .string()
       .min(1)
       .trim(),
    parent: z
        .coerce
        .number({ required_error: 'Parent is required.' }),
    description: z
        .string()
        .optional()
});

/**
 * @class CategoryDTO
 */
export class CategoryDTO extends DTO {
    constructor() {
        super(categoryValidationSchema);
    }
}

并且这些类在一些Svelte工具包的表单动作(https://kit.svelte.dev/docs/form-actions)中被实例化.

当构建并运行dev个应用程序时,该应用程序会按预期运行.现在,在运行prod时,我收到以下错误:

dmc-web         | Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'zod' imported from /app/server/chunks/23-61add41d.js
dmc-web         |     at packageResolve (node:internal/modules/esm/resolve:844:9)
dmc-web         |     at moduleResolve (node:internal/modules/esm/resolve:901:20)
dmc-web         |     at defaultResolve (node:internal/modules/esm/resolve:1131:11)
dmc-web         |     at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:390:12)
dmc-web         |     at ModuleLoader.resolve (node:internal/modules/esm/loader:359:25)
dmc-web         |     at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:234:38)
dmc-web         |     at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:85:39)
dmc-web         |     at link (node:internal/modules/esm/module_job:84:36) {
dmc-web         |   code: 'ERR_MODULE_NOT_FOUND'
dmc-web         | }

以下是我的Dockerfile条建议:

# base stage
FROM node:21-alpine AS build

# Update and install the latest dependencies on docker base image
# Add non root user to the docker image and set the user
RUN apk update && apk upgrade && adduser -D svelteuser
USER svelteuser

WORKDIR /app

# Copy the sveltkit project content with proper permission for the user svelteuser
COPY --chown=svelteuser:svelteuser . /app

# install all the project npm dependencies and
# build the svelte project to generate the artifacts in build directory
RUN npm install && npm run build

# ----------------------------------------------------------
FROM build AS dev
# Set the Node environment to development to ensure all packages are installed
ENV NODE_ENV development
EXPOSE 3000
EXPOSE 5137

ENTRYPOINT npm run dev

# ---------------------

# We are using multi stage build process to keep the image size as small as possible
FROM node:21-alpine AS prod

# Update and install latest dependencies, add dumb-init package
# add and set non root user
RUN apk update && apk upgrade && apk add dumb-init && adduser -D svelteuser
USER svelteuser

# set work dir as app
WORKDIR /app

# copy the build directory to the /app directory of second stage
COPY --chown=svelteuser:svelteuser --from=build /app/build /app/package.json ./

# expose 8080 on container
EXPOSE 3000

# set app host and port and env as production
ENV HOST=0.0.0.0 PORT=3000 NODE_ENV=production

# start the app with dumb init to spawn the Node.js runtime process
# with signal support
CMD ["dumb-init","node","index.js"]

在做一些谷歌搜索时,我看到有人指出package.json需要"type": "module",但我已经有了.

以下是我的package.json条建议:

{
    "name": "dundermifflin-ui",
    "version": "0.0.1",
    "private": true,
    "scripts": {
        "dev": "NODE_ENV=development vite dev --host",
        "build": "vite build",
        "preview": "vite preview",
        "test": "npm run test:integration && npm run test:unit",
        "lint": "prettier --plugin-search-dir . --check . && eslint .",
        "format": "prettier --plugin-search-dir . --write .",
        "test:integration": "playwright test",
        "test:unit": "vitest"
    },
    "devDependencies": {
        "@playwright/test": "^1.28.1",
        "@sveltejs/adapter-auto": "^2.0.0",
        "@sveltejs/kit": "^1.20.4",
        "autoprefixer": "^10.4.14",
        "daisyui": "^3.1.7",
        "eslint": "^8.28.0",
        "eslint-config-prettier": "^8.5.0",
        "eslint-plugin-svelte": "^2.30.0",
        "postcss": "^8.4.24",
        "prettier": "^2.8.0",
        "prettier-plugin-svelte": "^2.10.1",
        "svelte": "^4.0.0",
        "tailwindcss": "^3.3.2",
        "vite": "^4.3.6",
        "vitest": "^0.32.2"
    },
    "type": "module",
    "dependencies": {
        "@sveltejs/adapter-node": "^1.3.1",
        "html5-qrcode": "^2.3.8",
        "jsbarcode": "^3.11.6",
        "process": "^0.11.10",
        "svelte-select": "^5.6.1",
        "zod": "^3.21.4"
    }
}

同样,这个问题只发生在prod版本中.

你知道会发生什么事吗?

推荐答案

在你的Dockerfile中,在prod构建阶段,你只需要复制build目录和package.json文件.但是,您不会复制node_modules directory或重新安装生产依赖项.由于zod是一个运行时依赖项(在package.json中的dependencies下列出),因此它需要存在于生产环境中.

# previous stages remain the same 

# Production Stage
FROM node:21-alpine AS prod

# Update and install latest dependencies, add dumb-init package
# add and set non root user
RUN apk update && apk upgrade && apk add dumb-init && adduser -D svelteuser
USER svelteuser

WORKDIR /app

# Copy the package.json and package-lock.json (if exists)
COPY --chown=svelteuser:svelteuser package*.json ./


# Install only production dependencies   <===================
RUN npm install --only=production

# Copy the build directory from the build stage
COPY --chown=svelteuser:svelteuser --from=build /app/build ./build

EXPOSE 3000

ENV HOST=0.0.0.0 PORT=3000 NODE_ENV=production

CMD ["dumb-init","node","index.js"]

在对Dockerfile的建议修改中,没有显式复制node_modules目录:从开发环境复制node_modules可能会引入不必要的或特定于平台的文件,从而导致Docker映像inflating .

当您运行npm install --only=production时,NPM会查看您的package.json(如果有的话,还有package-lock.json),并安装生产所需的内容.该步骤会在容器中固有地创建node_modules目录和所有生产依赖项.通过在容器中安装依赖项,您可以确保生产环境与任何本地开发设置隔离.

Docker可以在构建图像时缓存层.如果package.json不经常更改,Docker将使用已安装node_modules的缓存层,从而加快构建过程.

参见Terence Tan中的"Dev Ops: Creating a Multi Stage Docker Container for Production".

Node.js相关问答推荐

使用NodeJS在S3上传文件时的格式问题

在Nest.Js中,如何发送带有表单数据的正文请求并应用正文请求验证.附加的文件是可选的

Mongoose抱怨说,整数是数字,而不是整数

未显示NPM版本

FiRestore UPDATE方法引发错误:&Quot;错误:13内部:收到代码为1&Quot;的RST_STREAM

安装样式组件时出现react 错误

Prisma,只有一个用户的行可以有真值,@@unique(userId, isActive)

yarn 安装失败,因为 node-gyp 正在寻找过时的 node 版本标头

错误:0308010C:try 通过 Github 推送部署到 firebase 托管时出现数字

未捕获的错误: 只能用作 元素的子元素,永远不会直接呈现.请将您的 包裹在

我应该如何解决这个 Angular node 包模块依赖冲突?

node Axios 创建全局令牌变量以在单独的变量头中使用

如何在 NestJS 中使用外部生成的 swagger.json?

为什么我的 Heroku Express API 数据是持久的,即使它只是来自一个变量

如何刷新 youtube-data-api v3 的访问令牌

Puppeteer 错误:未下载 Chromium 修订版

socket.io 发出回调合适吗?

代理(如提琴手)可以与 Node.js 的 ClientRequest 一起使用吗

PhoneGap/Cordova Android 开发

Node.js `--nolazy` 标志是什么意思?