Docker中NestJS应用无法连接MySQL容器的问题求助
问题现象
将基于TypeORM连接MySQL的NestJS应用容器化,本地未安装MySQL,依赖Docker提供的MySQL镜像。单独启动MySQL容器时本地可正常操作数据库,但构建NestJS镜像并运行容器时,出现连接拒绝错误:
[Nest] 29 - 04/02/2023, 7:37:26 PM ERROR [TypeOrmModule] Unable to connect to the database. Retrying (1)... Error: connect ECONNREFUSED 127.0.0.1:3306 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1494:16)
尝试过调整Dockerfile为node:14基础镜像、编写docker-compose同时启动应用与MySQL容器、修改app.module.ts读取环境变量DB_HOST、调整docker-compose配置等操作,甚至使用sudo启动容器,仍无法解决连接问题,期间还遇到过构建上下文权限错误。
相关配置文件
最初的docker-compose.yml
# docker-compose.yml version: '3.3' services: mysql: image: mysql:latest restart: always environment: - MYSQL_USER=myuser - MYSQL_PASSWORD=mypassword - MYSQL_DATABASE=db - MYSQL_ROOT_USER=myuser - MYSQL_ROOT_PASSWORD=root volumes: - mysql:/var/lib/mysql ports: - '3306:3306' volumes: mysql:
最初的Dockerfile
FROM ubuntu:18.04 # Set the working directory to /usr/src/app WORKDIR /usr/src/app # Copy the package.json and package-lock.json files into the container COPY package*.json ./ RUN apt-get update RUN apt-get -y install curl RUN curl -sL https://deb.nodesource.com/setup_16.x | bash RUN apt install -y nodejs # Install the app dependencies RUN npm install # Copy the rest of the app files into the container COPY . . # Set environment variables for the MySQL connection ENV MYSQL_HOST=mysql ENV MYSQL_USER=myuser ENV MYSQL_PASSWORD=mypassword ENV MYSQL_DATABASE=db # Install the MySQL server RUN apt-get update -qq && apt-get install -y mysql-server # Expose the app's port EXPOSE 3000 # Start the app in development mode CMD ["npm", "run", "start:dev"]
修改后的Dockerfile
# Use the official Node.js 14 image as the base image FROM node:14 # Create a working directory for the app WORKDIR /app # Copy the package.json and package-lock.json files into the container COPY package*.json ./ # Install app dependencies RUN npm install # Copy the rest of the app code into the container COPY . . # Expose port 3000 (the port that the app listens on) EXPOSE 3000 # Specify the command to run when the container starts CMD [ "npm", "run", "start:prod" ]
修改后的docker-compose.yml(尝试版本)
services: app: build: . command: npm run start:prod restart: always ports: - '3000:3000' environment: - DB_HOST=mysql - DB_PORT=3306 - DB_USER=myuser - DB_PASSWORD=mypassword - DB_NAME=db depends_on: - mysql mysql: image: mysql:latest restart: always environment: - MYSQL_USER=myuser - MYSQL_PASSWORD=mypassword - MYSQL_DATABASE=db - MYSQL_ROOT_USER=myuser - MYSQL_ROOT_PASSWORD=root volumes: - mysql:/var/lib/mysql volumes: mysql: networks: default: driver: bridge
调整后的app.module.ts
import { Module, OnModuleInit } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { TypeOrmModule } from '@nestjs/typeorm'; import { UsersModule } from './users/users.module'; import { ArtworksModule } from './artworks/artworks.module'; import { User } from './users/entities/user.entity'; import { Artwork } from './artworks/entities/artwork.entity'; import { AuthModule } from './auth/auth.module'; import { ConfigModule } from '@nestjs/config'; import { SeedService } from './users/seedService/seedService'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: process.env.DB_HOST, port: 3306, username: 'myuser', password: 'mypassword', database: 'db', entities: [User, Artwork], synchronize: false, autoLoadEntities: true, logging: true, }), ConfigModule.forRoot({ isGlobal: true, }), UsersModule, ArtworksModule, AuthModule, ], controllers: [AppController], providers: [AppService, SeedService], }) export class AppModule implements OnModuleInit { constructor(private seedService: SeedService) {} async onModuleInit() { await this.seedService.seedUsers(); } }
解决方案
1. 修复ConfigModule加载顺序问题
ConfigModule.forRoot必须在TypeOrmModule.forRoot之前加载,否则环境变量还未被注入,process.env.DB_HOST会是undefined,TypeORM会默认尝试连接127.0.0.1:3306,导致连接拒绝。调整app.module.ts的导入顺序:
@Module({ imports: [ // 优先加载ConfigModule,确保环境变量全局可用 ConfigModule.forRoot({ isGlobal: true, }), TypeOrmModule.forRoot({ type: 'mysql', host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT, 10) || 3306, username: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, entities: [User, Artwork], synchronize: false, autoLoadEntities: true, logging: true, }), UsersModule, ArtworksModule, AuthModule, ], // ... 其余代码不变 })
2. 确保MySQL服务就绪后再启动应用
depends_on仅保证容器启动顺序,不保证MySQL服务已经完全就绪。给MySQL服务添加健康检查,并让应用等待MySQL健康状态:
修改docker-compose.yml:
services: app: build: . command: npm run start:prod restart: always ports: - '3000:3000' environment: - DB_HOST=mysql - DB_PORT=3306 - DB_USER=myuser - DB_PASSWORD=mypassword - DB_NAME=db depends_on: mysql: condition: service_healthy # 等待MySQL服务健康 mysql: image: mysql:latest restart: always environment: - MYSQL_USER=myuser - MYSQL_PASSWORD=mypassword - MYSQL_DATABASE=db - MYSQL_ROOT_PASSWORD=root # 移除无效的MYSQL_ROOT_USER,官方镜像默认root用户为root volumes: - mysql:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot"] interval: 5s timeout: 5s retries: 5 volumes: mysql:
3. 修正MySQL环境变量错误
官方MySQL镜像没有MYSQL_ROOT_USER这个配置项,默认root用户就是root,只需设置MYSQL_ROOT_PASSWORD即可,之前的MYSQL_ROOT_USER=myuser属于无效配置,可能导致权限异常,直接移除该变量。
4. 清理Dockerfile冗余配置
最初的Dockerfile中安装mysql-server完全没必要,NestJS应用通过mysql2包连接MySQL,不需要在容器内安装MySQL服务,使用修改后的node:14基础镜像即可。
验证步骤
- 删除旧容器和镜像:
docker-compose down -v docker rmi $(docker images -q your-app-image-name)
- 重新构建并启动:
docker-compose up --build
内容的提问来源于stack exchange,提问作者CodingSirius

