You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker中NestJS应用无法连接MySQL容器的问题求助

解决NestJS + TypeORM + Docker 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基础镜像即可。

验证步骤

  1. 删除旧容器和镜像:
docker-compose down -v
docker rmi $(docker images -q your-app-image-name)
  1. 重新构建并启动:
docker-compose up --build

内容的提问来源于stack exchange,提问作者CodingSirius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 22:03:11