Docker容器中NodeJS连接MySQL报错ECONNREFUSED求助
使用Docker Compose部署NodeJS应用和MySQL服务后,执行docker compose up,NodeJS应用抛出连接拒绝错误,目标端口为3306,但Adminer可以正常连接MySQL。相关配置及代码如下:
Docker Compose配置
services: app-node: build: . ports: - "3000:3000" depends_on: mysql2: condition: service_healthy restart: true environment: DB_HOST: mysql2 DB_USER: lucasfer DB_PASSWORD: contra31 DB_NAME: test-docker DB_PORT: "3308" mysql2: image: mysql:5.7.27 healthcheck: test: "exit 0" command: --default-authentication-plugin=mysql_native_password restart: always ports: - "3308:3308" environment: MYSQL_ROOT_PASSWORD: contra31 MYSQL_DATABASE: test-docker MYSQL_USER: lucasfer MYSQL_PASSWORD: contra31 adminer: image: adminer restart: always ports: - 8080:8080
NodeJS Dockerfile
FROM node:18-alpine3.18 WORKDIR /app COPY . . RUN npm install RUN npm run build EXPOSE 3000 ENTRYPOINT ["node", "./dist/index.js"]
NodeJS应用代码
import express, { Request, Response } from 'express' import mysql from 'mysql' import { createUserTable, insertInitialUsers } from './src/DatabaseService.js'; const pool = mysql.createPool({ connectionLimit : 10, host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, port: Number(process.env.DB_PORT) }); const app = express() const PORT = 3000; createUserTable(pool) insertInitialUsers(pool) app.get('/', async (req: Request, res: Response) => { pool.query('SELECT * FROM users', (err: any, results: any) => { if (err) { console.error(err) return res.status(500).json({ error: 'Database error' }) } res.json(results) }) }) app.listen(PORT, () => { console.log(`Servidor NodeJS corriendo en puerto ${PORT}`) })
数据库服务文件
import { Pool } from "mysql"; export function createUserTable (pool: Pool) { pool.query(` CREATE TABLE IF NOT EXISTS users( id INT AUTO_INCREMENT PRIMARY KEY, firstName VARCHAR(255), lastName VARCHAR(255), email VARCHAR(255) ) ` , (err, result) => { if (err) { console.error(err) return } console.log('Tabla users creada exitosamente') }) } export function insertInitialUsers (pool: Pool) { return pool.query(` INSERT INTO users(firstName, lastName, email) VALUES ('Lucas', 'Ferrero', 'lucasdavidferrero@gmail.com'), ('Juan', 'Perez', 'juanperez@gmail.com'), ('Pedro', 'PicaPiedra', 'pedrito@gmail.com') `, (err, result) => { if (err) { console.error(err) return } console.log('usuarios insertados correctamente') }) }
报错信息
Error: connect ECONNREFUSED 172.21.0.3:3306 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16) -------------------- at Protocol._enqueue (/app/node_modules/mysql/lib/protocol/Protocol.js:144:48) at Protocol.handshake (/app/node_modules/mysql/lib/protocol/Protocol.js:51:23) at Connection.connect (/app/node_modules/mysql/lib/Connection.js:116:18) at file:///app/dist/index.js:22:5 at ModuleJob.run (node:internal/modules/esm/module_job:195:25) at async ModuleLoader.import (node:internal/modules/esm/loader:337:24) at async loadESM (node:internal/process/esm_loader:34:7) at async handleMainPromise (node:internal/run_main:106:12) { errno: -111, code: 'ECONNREFUSED', syscall: 'connect', address: '172.21.0.3', port: 3306, fatal: true }
错误原因分析
MySQL容器端口配置错误:
MySQL镜像默认在容器内部监听3306端口,但你的mysql2服务端口映射写的是3308:3308,这意味着试图让容器内部MySQL用3308端口启动,但MySQL未修改默认端口配置,实际仍运行在3306端口。NodeJS连接端口配置错误:
NodeJS应用的环境变量DB_PORT设为3308,但Docker容器间通信需访问容器内部端口,而非宿主映射的端口。即使修正MySQL端口映射,NodeJS也应连接容器内的3306端口。健康检查无效:
mysql2服务的健康检查用test: "exit 0",这是一个始终成功的空命令,无法真正检测MySQL服务是否就绪,可能导致NodeJS在MySQL未完全启动时尝试连接,但本次报错核心问题还是端口配置。
解决方案
1. 修正MySQL服务的端口映射
将mysql2服务的ports改为3308:3306,宿主3308端口映射到容器内MySQL默认的3306端口,既不影响外部工具(如Adminer)连接,也保证容器内部服务正常访问。
2. 修改NodeJS的数据库连接端口
将app-node服务的DB_PORT环境变量改为3306,容器内部访问MySQL需用容器内的默认端口。
3. 修复MySQL健康检查
使用真实检测命令确保MySQL服务就绪,替换healthcheck配置:
healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"] timeout: 20s retries: 10
修改后的Docker Compose配置
services: app-node: build: . ports: - "3000:3000" depends_on: mysql2: condition: service_healthy restart: true environment: DB_HOST: mysql2 DB_USER: lucasfer DB_PASSWORD: contra31 DB_NAME: test-docker DB_PORT: "3306" # 修改为容器内MySQL默认端口 mysql2: image: mysql:5.7.27 healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"] timeout: 20s retries: 10 command: --default-authentication-plugin=mysql_native_password restart: always ports: - "3308:3306" # 修正端口映射,宿主3308→容器3306 environment: MYSQL_ROOT_PASSWORD: contra31 MYSQL_DATABASE: test-docker MYSQL_USER: lucasfer MYSQL_PASSWORD: contra31 adminer: image: adminer restart: always ports: - 8080:8080
4. 重启服务
执行以下命令重新部署:
docker compose down docker compose up --build
内容的提问来源于stack exchange,提问作者Lucas David Ferrero

