即使我在 Dockerfile 中执行 npm install,也无法在 Docker 容器中找到包“drizzle-orm”?

问题描述 投票:0回答:1

我使用下面的

npm ci
在 Dockerfile 中安装所有依赖项。

Dockerfile

FROM node:20-alpine AS base

# 1. Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat

WORKDIR /app

# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
COPY /src/app/db/migrations ./migrations

RUN \
  if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
  elif [ -f package-lock.json ]; then npm ci; \
  elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i; \
  else echo "Lockfile not found." && exit 1; \
  fi


# 2. Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules

COPY . .

# This will do the trick, use the corresponding env file for each environment.
COPY .env.production .env.production
RUN mkdir -p /data

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

# Create /data/users.prod.sqlite using Volume Mount
# RUN npm run db:migrate:prod
RUN npm run build

# 3. Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

COPY --from=builder /app/public ./public

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

# Move the drizzle directory to the runtime image
COPY --from=builder --chown=nextjs:nodejs /app/src/app/db/migrations ./migrations

# Move the run script and litestream config to the runtime image
COPY --from=builder /app/scripts/drizzle-migrate.mjs ./scripts/drizzle-migrate.mjs
COPY --from=builder /app/scripts/init.sh ./init.sh
COPY --from=builder /app/scripts/run.sh ./run.sh
RUN chmod +x init.sh
RUN chmod +x run.sh


USER nextjs

EXPOSE 3000


CMD ["sh", "run.sh"]

运行.sh

#!/bin/bash
set -e

# npm run db:migrate:prod & PID=$!
# Wait for migration to finish
# wait $PID

echo "Starting production server..."
node server.js & PID=$!

wait $PID

当我执行

docker-compose up
时,我收到此错误:

错误 [ERR_MODULE_NOT_FOUND]:找不到从 /app/scripts/drizzle-migrate.mjs 导入的包“drizzle-orm”

我的

package.json
包含这2个脚本:

 "db:migrate:prod": "node --env-file .env.production ./scripts/drizzle-migrate.mjs"
 "start": "next start",

我还检查了

docker run -it easypanel-nextjs npm list
并得到了这个:

npm ERR! code ELSPROBLEMS
npm ERR! missing: @epic-web/remember@^1.0.2, required by [email protected]
npm ERR! missing: @t3-oss/env-nextjs@^0.9.2, required by [email protected]
npm ERR! missing: drizzle-orm@^0.29.3, required by [email protected]
npm ERR! missing: jiti@^1.21.0, required by [email protected]
npm ERR! missing: std-env@^3.7.0, required by [email protected]
npm ERR! missing: tsx@^4.7.1, required by [email protected]
npm ERR! missing: zod@^3.22.4, required by [email protected]
[email protected] /app
+-- UNMET DEPENDENCY @epic-web/remember@^1.0.2
+-- UNMET DEPENDENCY @t3-oss/env-nextjs@^0.9.2
+-- [email protected] -> ./node_modules/.pnpm/[email protected]/node_modules/better-sqlite3
+-- UNMET DEPENDENCY drizzle-orm@^0.29.3
+-- UNMET DEPENDENCY jiti@^1.21.0
+-- [email protected] -> ./node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/next
+-- [email protected] -> ./node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom
+-- [email protected] -> ./node_modules/.pnpm/[email protected]/node_modules/react
+-- UNMET DEPENDENCY std-env@^3.7.0
+-- UNMET DEPENDENCY tsx@^4.7.1
`-- UNMET DEPENDENCY zod@^3.22.4

如何解决这个错误?

我什至尝试使用匿名模块但那些也不起作用。

docker-compose.yml

version: '3.8'

services:
  web:
    build:
      context: .
      dockerfile: docker/web/Dockerfile
    # depends_on:
    #   db:
    #     condition: service_healthy
    #   redis:
    #     condition: service_started
    image: easypanel-nextjs
    container_name: nextjs-sqlite
    env_file:
      - .env.production
    ports:
      - 3000:3000
    volumes:
      - ./data:/data
      - /app/node_modules
  # migration:
  #   build:
  #     context: .
  #     dockerfile: docker/migrations/Dockerfile
  #   image: easypanel-nextjs
  #   depends_on:
  #     web:
  #       condition: service_completed_successfully

我的容器因此错误而停止:

> [email protected] db:migrate:prod
> node --env-file .env.production ./scripts/drizzle-migrate.mjs

node:internal/modules/esm/resolve:853
  throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base), null);
        ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'drizzle-orm' imported from /app/scripts/drizzle-migrate.mjs
    at packageResolve (node:internal/modules/esm/resolve:853:9)
    at moduleResolve (node:internal/modules/esm/resolve:910:20)
    at defaultResolve (node:internal/modules/esm/resolve:1130:11)
    at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:396:12)
    at ModuleLoader.resolve (node:internal/modules/esm/loader:365:25)
    at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:240:38)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:85:39)
    at link (node:internal/modules/esm/module_job:84:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

Node.js v20.11.1

当我尝试使用

docker run -it easypanel-nextjs sh
进入停止的容器并执行
ls node_modules
时,然后我得到:

/app $ ls node_modules/
better-sqlite3  next            react           react-dom

只有 4 个依赖项。我的

package.json
有很多依赖项:

{
  "name": "easypanel-nextjs-sqlite",
  "type": "module",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "turbo": "next dev --turbo",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "knip": "knip",
    "clean": "rimraf .next",
    "db:push": "drizzle-kit push:sqlite",
    "db:generate": "drizzle-kit generate:sqlite",
    "db:migrate": "node --env-file .env.development ./scripts/drizzle-migrate.mjs",
    "db:seed": "node --import tsx --env-file .env.development ./scripts/seed/insert.ts",
    "db:delete": "node --import tsx --env-file .env.development ./scripts/seed/delete.ts",
    "db:migrate:prod": "node --env-file .env.production ./scripts/drizzle-migrate.mjs"
  },
  "dependencies": {
    "@epic-web/remember": "^1.0.2",
    "@t3-oss/env-nextjs": "^0.9.2",
    "better-sqlite3": "^9.4.1",
    "drizzle-orm": "^0.29.3",
    "jiti": "^1.21.0",
    "next": "14.1.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "std-env": "^3.7.0",
    "tsx": "^4.7.1",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@types/better-sqlite3": "^7.6.9",
    "@types/node": "^20.11.19",
    "@types/react": "^18.2.56",
    "@types/react-dom": "^18.2.19",
    "drizzle-kit": "^0.20.14",
    "knip": "^5.0.1",
    "rimraf": "^5.0.5",
    "typescript": "^5.3.3"
  }
}

问题是什么?

我有完整的复制品 -> https://github.com/deadcoder0904/easypanel-nextjs-sqlite

node.js bash docker sqlite npm
1个回答
0
投票

新答案

我从

pnpm install
中删除了
run.sh
,它启动容器 (
docker compose up
) 的速度很慢。于是1分钟的等待就消失了。我在最后阶段复制了带有生产依赖项的
node_modules
,这使我的图像从 198mb 变为 611mb,但这对我来说没关系。

然后我尝试添加 SQLite WAL 模式,结果由于网络问题导致数据丢失。我不知道为什么它不能正常工作,但这既不是 Docker 的错,也不是我的 Volume 语法错误。所以我注释掉了 WAL 模式,现在一切正常了。

新解决方案通过使用非特权用户来使用 Node.js 中的安全最佳实践。而且该解决方案更加清晰且易于理解。

docker-compose.yml

version: '3.8'

services:
  web:
    image: easypanel-nextjs:0.0.1
    build:
      context: .
      dockerfile: Dockerfile
    container_name: nextjs-sqlite
    env_file:
      - .env.production
    ports:
      - 3000:3000
    volumes:
      - ./data:/data

Dockerfile

FROM node:20-alpine AS base

# mostly inspired from https://github.com/BretFisher/node-docker-good-defaults/blob/main/Dockerfile & https://github.com/remix-run/example-trellix/blob/main/Dockerfile

# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
RUN corepack enable && corepack prepare [email protected] --activate 
# set the store dir to a folder that is not in the project
RUN pnpm config set store-dir ~/.pnpm-store
RUN pnpm fetch

# 1. Install all dependencies including dev dependencies
FROM base AS deps
# Root user is implicit so you don't have to actually specify it. From https://stackoverflow.com/a/45553149/6141587
# USER root
USER node
# WORKDIR now sets correct permissions if you set USER first so `USER node` has permissions on `/app` directory
WORKDIR /app

# Install dependencies based on the preferred package manager
COPY --chown=node:node package.json pnpm-lock.yaml* ./
COPY --chown=node:node /src/app/db/migrations ./migrations

USER root
RUN pnpm install

# 2. Setup production node_modules
FROM base as production-deps
WORKDIR /app

COPY --from=deps --chown=node:node /app/node_modules ./node_modules
COPY --chown=node:node package.json pnpm-lock.yaml* ./
RUN pnpm prune --prod

# 3. Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps --chown=node:node /app/node_modules ./node_modules

COPY --chown=node:node . .

# This will do the trick, use the corresponding env file for each environment.
COPY --chown=node:node .env.production .env.production

# Copied from https://stackoverflow.com/a/69867550/6141587
USER root
# Give /data directory correct permissions otherwise WAL mode won't work. It means you can't have 2 users writing to the database at the same time without this line as *.sqlite-wal & *.sqlite-shm are automatically created & deleted when *.sqlite is busy.
RUN mkdir -p /data && chown -R node:node /data

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

RUN pnpm build

# 3. Production image, copy all the files and run next
FROM base AS runner
USER node
WORKDIR /app

EXPOSE 3000

ENV PORT 3000
ENV HOSTNAME '0.0.0.0'
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

COPY --from=builder --chown=node:node /app/public ./public
COPY --from=production-deps --chown=node:node /app/node_modules ./node_modules

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=node:node /app/.next/standalone ./
COPY --from=builder --chown=node:node /app/.next/static ./.next/static

# Move the drizzle directory to the runtime image
COPY --from=builder --chown=node:node /app/src/app/db/migrations ./migrations

# Move the run script and litestream config to the runtime image
COPY --from=builder --chown=node:node /app/scripts/drizzle-migrate.mjs ./scripts/drizzle-migrate.mjs
COPY --from=builder --chown=node:node /app/scripts/run.sh ./run.sh
RUN chmod +x run.sh

CMD ["sh", "run.sh"]

运行.sh

#!/bin/bash
set -e

echo "Creating '/data/users.prod.sqlite' using bind volume mount"
pnpm run db:migrate:prod & PID=$!
# Wait for migration to finish
wait $PID

echo "Starting production server..."
node server.js & PID=$!

wait $PID

原始答案(不要使用这个)

原因是当我进入

node_modules
时,我的
run.sh
不存在,或者至少它只有我上面提到的几个文件夹,所以我想我应该
npm install
它们在
run.sh
中,这就是我的做到了。

我实际上更改为

pnpm
,因为我认为它会很快。我不知道它在我的 docker 上是否真的很快,但在本地肯定很快。

我还使用了 chown 等权限,因为我了解到这是 Node.js 的最佳实践。

最后,我使用

node_modules
作为匿名卷,由 https://michalzalecki.com/docker-compose-node/

提供

docker-compose.yml

version: '3.8'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    image: easypanel-nextjs
    container_name: nextjs-sqlite
    env_file:
      - .env.production
    ports:
      - 3000:3000
    volumes:
      - ./data:/data
      - /app/node_modules

Dockerfile

FROM node:20-alpine AS base

# mostly inspired from https://github.com/BretFisher/node-docker-good-defaults/blob/main/Dockerfile & https://github.com/remix-run/example-trellix/blob/main/Dockerfile

# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
RUN corepack enable && corepack prepare [email protected] --activate 
# set the store dir to a folder that is not in the project
RUN pnpm config set store-dir ~/.pnpm-store
RUN pnpm fetch

# 1. Install all dependencies including dev dependencies
FROM base AS deps

# Root user is implicit so you don't have to actually specify it. From https://stackoverflow.com/a/45553149/6141587
# USER root
USER node
# WORKDIR now sets correct permissions if you set USER first so `USER node` has permissions on `/app` directory
WORKDIR /app

# Install dependencies based on the preferred package manager
COPY --chown=node:node package.json pnpm-lock.yaml* ./
COPY --chown=node:node /src/app/db/migrations ./migrations

USER root
RUN pnpm install
USER node

# 2. Setup production node_modules
FROM base as production-deps
WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY --chown=node:node package.json pnpm-lock.yaml* ./
RUN pnpm prune --prod

# 3. Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps --chown=node:node /app/node_modules ./node_modules

COPY --chown=node:node . .

# This will do the trick, use the corresponding env file for each environment.
COPY --chown=node:node .env.production .env.production
RUN mkdir -p /data

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

RUN pnpm build

# 3. Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

COPY --from=builder --chown=node:node /app/public ./public
COPY --from=production-deps --chown=node:node /app/node_modules ./node_modules

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=node:node /app/.next/standalone ./
COPY --from=builder --chown=node:node /app/.next/static ./.next/static

# Move the drizzle directory to the runtime image
COPY --from=builder --chown=node:node /app/src/app/db/migrations ./migrations

# Move the run script and litestream config to the runtime image
COPY --from=builder --chown=node:node /app/scripts/drizzle-migrate.mjs ./scripts/drizzle-migrate.mjs
COPY --from=builder --chown=node:node /app/scripts/run.sh ./run.sh
RUN chmod +x run.sh

EXPOSE 3000

CMD ["sh", "run.sh"]

运行.sh

#!/bin/bash
set -e

echo "Installing dependencies using pnpm..."
pnpm install & PID=$!
wait $PID

echo "Creating 'data/users.prod.sqlite' using bind volume mount"
pnpm run db:migrate:prod & PID=$!
# Wait for migration to finish
wait $PID

echo "Starting production server..."
node server.js & PID=$!

wait $PID

完整的工作版本位于 https://github.com/deadcoder0904/easypanel-nextjs-sqlite/

现在唯一需要注意的是,当我启动容器时,需要时间,因为它正在做

pnpm install

我很想弄清楚如何制作它,这样我就不必每次在项目中做

pnpm install
时都
make start-production
。我使用
Makefile
,您可以在链接的存储库中查看。

© www.soinside.com 2019 - 2024. All rights reserved.