Docker环境下NestJS无法连接MongoDB的问题求助
我有一个基于Prisma和MongoDB的NestJS应用,本地运行NestJS时能正常连接Docker化的MongoDB,但将NestJS部署到Docker容器后,无法连接MongoDB。
相关配置文件
1. docker-compose.yml
version: '3.8' services: # Replica Set (required for Prisma Client) mongo: container_name: mongo build: dockerfile: ./docker/mongo.Dockerfile environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: prisma MONGO_REPLICA_HOST: 127.0.0.1 MONGO_REPLICA_PORT: 27018 env_file: - .env ports: - '27018:27018' web: build: dockerfile: ./docker/app.Dockerfile environment: DATABASE_URL: mongodb://root:prisma@mongo:27018/bookmeon?authSource=admin env_file: - .env ports: - '1337:1337' depends_on: - mongo
2. mongo.Dockerfile
################### # MONGO container - for replication ################### FROM mongo:4 As mongo # we take over the default & start mongo in replica set mode in a background task ENTRYPOINT mongod --port $MONGO_REPLICA_PORT --replSet rs0 --bind_ip 0.0.0.0 & MONGOD_PID=$!; \ # we prepare the replica set with a single node and prepare the root user config INIT_REPL_CMD="rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: '$MONGO_REPLICA_HOST:$MONGO_REPLICA_PORT' }] })"; \ INIT_USER_CMD="db.getUser('$MONGO_INITDB_ROOT_USERNAME') || db.createUser({ user: '$MONGO_INITDB_ROOT_USERNAME', pwd: '$MONGO_INITDB_ROOT_PASSWORD', roles: [ 'root' ] })"; \ # we wait for the replica set to be ready and then submit the commands just above until (mongo admin --port $MONGO_REPLICA_PORT --eval "$INIT_REPL_CMD && $INIT_USER_CMD"); do sleep 1; done; \ # we are done but we keep the container by waiting on signals from the mongo task echo "REPLICA SET ONLINE"; wait $MONGOD_PID;
3. app.Dockerfile
################### # base ################### FROM node:20.5.1-alpine3.18 As base RUN apk add openssl1.1-compat RUN apk add musl RUN npm i -g pnpm ################### # deps install ################### FROM base As deps WORKDIR /my COPY --chown=node:node ./package.json . RUN pnpm i ################### # build ################### FROM base As build WORKDIR /my COPY --chown=node:node --from=deps ./my/. . COPY --chown=node:node ./tsconfig* . COPY --chown=node:node ./webpack.js . COPY --chown=node:node ./prisma ./prisma COPY --chown=node:node ./src ./src COPY --chown=node:node ./aps ./aps ARG DATABASE_URL="mongodb://root:prisma@mongo:27018/bookmeon?authSource=admin" RUN pnpm exec prisma generate RUN pnpm run build USER node CMD [ "node", "dist/main.js" ]
4. .env
DATABASE_URL="mongodb://root:prisma@mongo:27018/bookmeon?authSource=admin" MONGO_INITDB_ROOT_USERNAME=root MONGO_INITDB_ROOT_PASSWORD=prisma MONGO_REPLICA_HOST=127.0.0.1 MONGO_REPLICA_PORT=27018 NODE_ENV=staging FORCE_WWW=0 FORCE_TLS=0
报错信息
book_me_on-web-1 | PrismaClientInitializationError: Raw query failed. Code: `unknown`. Message: `Server selection timeout: No available servers. Topology: { Type: ReplicaSetNoPrimary, Servers: [ { Address: 127.0.0.1:27018, Type: Unknown, Error: Connection refused (os error 111) }, ] }` book_me_on-web-1 | at r (/my/node_modules/.pnpm/@prisma+client@5.0.0_prisma@5.0.0/node_modules/@prisma/client/runtime/library.js:103:2766) book_me_on-web-1 | at async Proxy.onModuleInit (/my/dist/prisma.service.js:14:9) book_me_on-web-1 | at async Promise.all (index 0) book_me_on-web-1 | at async callModuleInitHook (/my/node_modules/.pnpm/@nestjs+core@10.0.0_@nestjs+common@10.0.0_@nestjs+platform-express@10.0.0_reflect-metadata@0.1.13_rxjs@7.8.1/node_modules/@nestjs/core/hooks/on-module-init.hook.js:43:5) book_me_on-web-1 | at async NestApplication.callInitHook (/my/node_modules/.pnpm/@nestjs+core@10.0.0_@nestjs+common@10.0.0_@nestjs+platform-express@10.0.0_reflect-metadata@0.1.13_rxjs@7.8.1/node_modules/@nestjs/core/nest-application-context.js:223:13) book_me_on-web-1 | at async NestApplication.init (/my/node_modules/.pnpm/@nestjs+core@10.0.0_@nestjs+common@10.0.0_@nestjs+platform-express@10.0.0_reflect-metadata@0.1.13_rxjs@7.8.1/node_modules/@nestjs/core/nest-application.js:97:9) book_me_on-web-1 | at async NestApplication.listen (/my/node_modules/.pnpm/@nestjs+core@10.0.0_@nestjs+common@10.0.0_@nestjs+platform-express@10.0.0_reflect-metadata@0.1.13_rxjs@7.8.1/node_modules/@nestjs/core/nest-application.js:166:33) book_me_on-web-1 | at async bootstrap (/my/dist/main.js:21:5) { book_me_on-web-1 | clientVersion: '5.0.0', book_me_on-web-1 | errorCode: 'P2010' book_me_on-web-1 | } book_me_on-web-1 | book_me_on-web-1 | Node.js v20.5.1
排查更新
创建测试容器查看运行时环境变量,手动修改MONGO_REPLICA_HOST为172.19.0.2后,发现NestJS尝试连接172.19.0.2:27018,但通过mongo主机名ping到的MongoDB实际地址是172.19.0.3:27018,说明MongoDB实际运行IP与配置的固定IP不一致,导致连接失败。
解决方案
问题根源
MongoDB副本集初始化时,会将节点的地址返回给客户端,客户端后续会直接使用该地址建立连接。如果初始化时用的是127.0.0.1,NestJS容器内部访问127.0.0.1指向自身容器而非MongoDB容器;如果用固定IP,容器重启后IP可能变化,同样导致连接失败。Docker Compose内部容器可通过服务名(mongo)解析到正确地址,且该地址不会随容器重启变化。
修改步骤
- 更新docker-compose.yml中MongoDB的环境变量
将MONGO_REPLICA_HOST改为服务名mongo:
services: mongo: environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: prisma MONGO_REPLICA_HOST: mongo # 替换原127.0.0.1 MONGO_REPLICA_PORT: 27018
- 更新.env文件中的MONGO_REPLICA_HOST
MONGO_REPLICA_HOST=mongo
保持mongo.Dockerfile不变
该文件中已使用$MONGO_REPLICA_HOST变量,修改环境变量后会自动使用服务名初始化副本集。可选:添加MongoDB健康检查
确保NestJS启动时MongoDB副本集已就绪,在docker-compose.yml的mongo服务中添加健康检查:
services: mongo: # 其他配置... healthcheck: test: echo "rs.status().ok" | mongo admin --port $MONGO_REPLICA_PORT --quiet interval: 5s timeout: 5s retries: 5 web: # 其他配置... depends_on: mongo: condition: service_healthy
验证
重新构建并启动容器:
docker-compose down -v docker-compose up --build
此时NestJS容器将通过服务名mongo连接MongoDB,且副本集返回的节点地址也是mongo:27018,容器内部可正常解析访问。
内容的提问来源于stack exchange,提问作者Rantiev

