Docker化Node.js应用如何确保MongoDB迁移在数据库就绪后执行
我是Docker新手,想在Docker化的Node.js应用里执行MongoDB迁移。后端Dockerfile在构建阶段执行npm run db:up(实际是调用migrate-mongo up做迁移),用docker-compose部署前端、后端和MongoDB容器。
现有配置
后端Dockerfile
FROM node:14.16.0-alpine3.13 RUN addgroup app && adduser -S -G app app USER app WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3001 RUN npm run db:up CMD ["npm", "start"]
docker-compose.yml
services: web: depends_on: - api build: ./frontend ports: - 3000:3000 # environment: # - WATCHPACK_POLLING=true develop: watch: - path: ./frontend/package.json action: rebuild - path: ./frontend/package-lock.json action: rebuild - path: ./frontend target: /app action: sync api: depends_on: db: condition: service_healthy restart: true build: ./backend ports: - 3001:3001 environment: DB_URL: mongodb://db/vidly develop: watch: - path: ./backend/package.json action: rebuild - path: ./backend/package-lock.json action: rebuild - path: ./backend target: /app action: sync command: ./docker-entrypoint.sh db: image: mongo:latest ports: - 27017:27017 volumes: - vidly:/data/db healthcheck: test: echo 'db.runCommand("ping").ok' | mongosh mongo:27017/test --quiet 1 interval: 10s timeout: 5s retries: 10 volumes: vidly:
执行错误
运行docker-compose up时出现以下错误:
=> ERROR [api 7/7] RUN npm run db:up 33.0s ------ > [api 7/7] RUN npm run db:up: 0.889 0.889 > vidly-backend@1.0.0 db:up /app 0.889 > migrate-mongo up 0.889 32.90 ERROR: connect ECONNREFUSED 127.0.0.1:27017 MongoServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017 32.90 at Timeout._onTimeout (/app/node_modules/migrate-mongo/node_modules/mongodb/lib/sdam/topology.js:292:38) 32.90 at listOnTimeout (internal/timers.js:554:17) 32.90 at processTimers (internal/timers.js:497:7) 32.94 npm ERR! code ELIFECYCLE 32.94 npm ERR! errno 1 32.95 npm ERR! vidly-backend@1.0.0 db:up: `migrate-mongo up` 32.95 npm ERR! Exit status 1 32.95 npm ERR! 32.95 npm ERR! Failed at the vidly-backend@1.0.0 db:up script. 32.95 npm ERR! This is probably not a problem with npm. There is likely additional logging output above. 32.96 32.96 npm ERR! A complete log of this run can be found in: 32.96 npm ERR! /home/app/.npm/_logs/2024-06-17T16_51_27_332Z-debug.log ------
补充说明
- 本地MongoDB连接字符串是
mongodb://localhost:27017,容器中用DB_URL: mongodb://db/vidly - 已经给db服务加了healthcheck,但问题依旧
我的疑问是:怎么才能让MongoDB迁移在mongod完全启动就绪之后再执行?
核心问题:构建阶段跑迁移完全不对
你现在的Dockerfile里用RUN npm run db:up是在镜像构建阶段执行的,这时候db容器还没启动——构建阶段根本不在docker-compose的服务网络里,连db服务都访问不到,自然会连127.0.0.1失败(因为构建时没有加载DB_URL环境变量,migrate-mongo默认连本地)。
正确做法:把迁移放到容器启动阶段
迁移必须在容器启动、db服务就绪之后执行,不能在构建镜像的时候跑。
步骤1:修改Dockerfile,删除构建阶段的迁移命令
把RUN npm run db:up这一行删掉,修改后的Dockerfile:
FROM node:14.16.0-alpine3.13 RUN addgroup app && adduser -S -G app app USER app WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3001 CMD ["npm", "start"]
步骤2:用entrypoint脚本控制启动流程
你已经在docker-compose里指定了command: ./docker-entrypoint.sh,在后端项目根目录创建这个脚本(记得给脚本加执行权限:chmod +x docker-entrypoint.sh):
#!/bin/sh # 等待MongoDB就绪 wait-for-mongodb() { until mongosh "$DB_URL" --eval "db.runCommand('ping')" >/dev/null 2>&1; do echo "等待MongoDB就绪..." sleep 2 done } # 先等DB就绪 wait-for-mongodb # 执行迁移 echo "开始执行数据库迁移..." npm run db:up # 启动应用 echo "启动Node.js应用..." exec npm start
如果容器里没有mongosh,可以在Dockerfile里添加安装命令(切换到root用户安装,再切回app用户):
# 在USER app之前添加: RUN apk add --no-cache mongodb-tools
步骤3:确保migrate-mongo配置正确
检查migrate-mongo-config.js,要读取环境变量里的DB_URL,不要硬编码localhost:
require('dotenv').config(); module.exports = { mongodb: { url: process.env.DB_URL || 'mongodb://localhost:27017/vidly', options: { useNewUrlParser: true, useUnifiedTopology: true } }, migrationsDir: 'migrations', changelogCollectionName: 'changelog' };
步骤4:验证docker-compose的depends_on配置
你已经加了depends_on的healthcheck条件,这个配置是对的,能保证api容器在db健康检查通过后才启动。加上entrypoint里的等待逻辑,双重保障db完全就绪后再执行迁移。
为什么之前的healthcheck没用?
因为你的错误是在镜像构建阶段发生的,docker-compose的depends_on只控制容器启动顺序,管不了镜像构建过程。构建api镜像的时候,db容器还没创建,所以healthcheck对构建阶段的命令完全无效。
内容的提问来源于stack exchange,提问作者Ashraf Lobo

