如何在Docker Compose中通过npm包自动创建PostgreSQL数据库表?
我有一个使用Prisma和PostgreSQL进行数据库管理的项目,为实现集中管理并供多个项目复用,将其封装为npm包project-db,通过npm i <project-db>安装。目前需要配置docker-compose文件,在部署依赖该数据库的新项目(如Project A)的全新实例时自动创建所需数据表,但由于数据库/Prisma均由npm包间接管理,不确定如何实现运行docker-compose时执行project-db中的脚本创建表。当前Project A的Docker镜像构建时因缺少数据库表报错,相关配置如下:
结构示意图
Project A(Node项目,配置为Docker服务) - package.json中包含`project-db`作为依赖 project-db(发布至npm registry的独立仓库) - 包含prisma schema - 拥有用于初始化Prisma和部署数据库的安装脚本 postgres(配置为Docker服务) - Prisma使用的数据库服务
Dockerfile(Project A)
ARG OWNER FROM node:20-buster-slim ENV NODE_ENV 'development' ENV OWNER "username" RUN apt-get update && apt-get install libssl-dev ca-certificates -y # Create app directory WORKDIR /usr/src/app # Copy package.json and package-lock.json COPY package*.json . # Setup npmrc RUN --mount=type=secret,id=TOKEN \ TOKEN=$(cat /run/secrets/TOKEN) && \ echo //npm.pkg.github.com/:_authToken=${TOKEN} >> ~/.npmrc RUN echo @${OWNER}:registry=https://npm.pkg.github.com/ >> ~/.npmrc # Copy src files COPY src/ src/ # Install packages RUN npm ci # ^ installs "project-db" as a dependency # remove github token from npmrc RUN echo > ~/.npmrc # Build the project RUN npm run build # Run the start script in package.json CMD ["npm", "run", "start"]
docker-compose脚本
services: postgres: image: postgres:16.2 container_name: postgres hostname: postgres environment: POSTGRES_USER: ${PSQL_USER} POSTGRES_PASSWORD: ${PSQL_PASS} POSTGRES_DB: ${PSQL_DB_NAME} ports: - ${PSQL_PORT}:${PSQL_PORT} restart: 'unless-stopped' volumes: - 'pg-data:/var/lib/postgresql/data' healthcheck: test: ['CMD-SHELL', 'pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER'] interval: 10s timeout: 5s retries: 5 projectA: build: context: . secrets: - TOKEN container_name: projectA depends_on: postgres: condition: service_healthy tty: true env_file: - src/.env volumes: pg-data: driver: local secrets: TOKEN: environment: "TOKEN"
核心思路
构建阶段报错的原因是:镜像构建时PostgreSQL服务尚未启动,若构建流程中包含访问数据库的操作(如运行数据库查询、执行迁移)必然失败。因此需要将数据库初始化操作从构建阶段移至容器启动阶段,或通过独立服务完成初始化后再启动主应用。
方案1:修改Project A启动流程,前置数据库初始化
修改Project A的package.json启动脚本,在启动应用前执行project-db提供的数据库初始化/迁移脚本:
// Project A package.json { "scripts": { "start": "npx project-db-run-migrations && node dist/index.js" } }
若project-db未提供封装好的脚本,可直接调用Prisma命令(需指定schema路径):
"start": "npx prisma migrate deploy --schema=node_modules/project-db/prisma/schema.prisma && node dist/index.js"
也可直接修改Dockerfile的CMD命令:
CMD ["sh", "-c", "npx prisma migrate deploy --schema=node_modules/project-db/prisma/schema.prisma && npm run start"]
方案2:新增独立数据库初始化服务(推荐,解耦架构)
在docker-compose中添加一个一次性的db-init服务,专门执行数据库迁移,完成后自动退出。Project A等待该服务执行成功后再启动:
services: postgres: # 保持原有配置不变 db-init: build: context: . secrets: - TOKEN container_name: db-init depends_on: postgres: condition: service_healthy env_file: - src/.env # 执行project-db的迁移脚本,或直接调用Prisma命令 command: ["npx", "prisma", "migrate", "deploy", "--schema=node_modules/project-db/prisma/schema.prisma"] projectA: build: context: . secrets: - TOKEN container_name: projectA depends_on: db-init: condition: service_completed_successfully postgres: condition: service_healthy # 保持原有配置不变
该方案的优势是将数据库初始化与应用启动完全解耦,避免主应用容器因初始化失败反复重启。
补充:修复构建阶段报错
若构建阶段(npm run build)包含生成Prisma客户端的操作,需确保仅生成客户端而不访问数据库:
# 在npm ci之后添加以下命令 RUN npx prisma generate --schema=node_modules/project-db/prisma/schema.prisma
此命令仅生成Prisma客户端代码,不会连接数据库,可安全在构建阶段执行。
内容的提问来源于stack exchange,提问作者secondubly

