在GitHub Actions和Docker中使用Sequelize时遇ECONNREFUSED错误
GitHub Actions中Sequelize连接MySQL容器出现ECONNREFUSED错误
问题描述
推送PR或提交到master分支时,GitHub Actions运行的集成测试无法通过Sequelize连接Docker启动的MySQL数据库,报错ECONNREFUSED 127.0.0.1:3305。本地运行测试完全正常,且两周前该CI流程无异常,期间未修改相关代码。
具体错误信息
connect ECONNREFUSED 127.0.0.1:3305 ConnectionRefusedError [SequelizeConnectionRefusedError]: connect ECONNREFUSED 127.0.0.1:3305 ... parent: Error: connect ECONNREFUSED 127.0.0.1:3305 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1605:16) { errno: -111, code: 'ECONNREFUSED', syscall: 'connect', address: '127.0.0.1', port: 3305, fatal: true }
相关配置与代码
GitHub Actions配置文件
name: Node.js CI on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: name: "Building" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Use Node.js uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npm test // this is the relevant test that is failing
Docker Compose测试配置文件
version: "3.3" services: db: image: mysql command: --default-authentication-plugin=mysql_native_password restart: always environment: MYSQL_ROOT_PASSWORD: root volumes: - ./init_db:/docker-entrypoint-initdb.d ports: - 3305:3306 extra_hosts: - "host.docker.internal:host-gateway"
测试代码相关部分
async function setUpMySQLServer() { // Stop any existing MySQL container. child_process.spawnSync("docker-compose", ["-f", __dirname + "/docker-compose.test.yaml", "down"]); // Start a container for MySQL server. const { status, stderr, error } = child_process.spawnSync("docker-compose", ["-f", __dirname + "/docker-compose.test.yaml", "up", "--detach"]); if (error) throw new Error("Unable to run docker-compose. Docker is required in order to set up a MySQL server."); if (status != 0) throw new Error("docker-compose exited with an error: " + stderr); console.log("Docker set up..."); await sleep(30000); // Wait for MySQL server to start (did this as an attempt to fix the issue, does not work) // Connect to the MySQL server. let sequelize; try { sequelize = new Sequelize("test", "root", "root", { host: "127.0.0.1", port: 3305, dialect: "mysql", dialectOptions: { decimalNumbers: true, multipleStatements: true } }); } catch (error) { console.log(error.message); throw new Error("Unable to connect to the MySQL server. Make sure the MySQL server is running and the connection settings are correct."); } // Wait for the MySQL server to be up. let attempts = 0; while (true) { attempts++; try { // Test the connection. This is where I am getting the ECONNREFUSED error. await sequelize.authenticate(); break; } catch (error) { if (attempts >= 300) { console.log(error.message); throw error; } } await sleep(1000); } await populateDatabase(sequelize); sequelize.close(); }
已尝试的无效方案
- 增加MySQL容器启动后的等待时间(已设为30秒)
- 修改端口映射(替换其他端口)
- 更换Node.js版本
- 添加Docker
extra_hosts配置 - 配置Sequelize连接池
本地首次运行时会弹出“允许访问文件”弹窗,同意后可正常运行,拒绝则出现相同错误,不确定是否与CI问题相关。
解决方案
1. 改用Docker服务名称作为连接主机
GitHub Actions的Ubuntu runner中,通过127.0.0.1访问容器端口可能存在网络隔离问题,直接用Docker Compose的服务名作为连接主机更可靠:
- 修改Sequelize连接配置:
sequelize = new Sequelize("test", "root", "root", { host: "db", // 对应Docker Compose中的服务名 port: 3306, // 使用容器内部的MySQL端口,无需映射到主机 dialect: "mysql", dialectOptions: { decimalNumbers: true, multipleStatements: true } });
- 同时将Docker Compose的端口映射配置移除,避免不必要的网络暴露。
2. 检查Docker服务状态并添加网络权限
在GitHub Actions步骤中,先确保Docker服务正常运行,并且测试进程和容器在同一网络:
# 在npm ci之后添加 sudo systemctl restart docker docker network create test-network # 修改启动容器的命令,加入自定义网络 # 测试代码中spawnSync的参数改为: ["-f", __dirname + "/docker-compose.test.yaml", "up", "--detach", "--network", "test-network"]
3. 指定稳定版本的MySQL镜像
近期MySQL官方镜像可能有更新导致启动逻辑变化,指定具体稳定版本(如mysql:8.0):
修改Docker Compose中的image字段:
image: mysql:8.0
4. 排查数据库初始化脚本问题
在GitHub Actions中添加步骤查看容器日志,确认MySQL是否因初始化脚本错误未正常启动:
# 在启动容器后添加 docker logs $(docker ps -q --filter "name=db")
检查./init_db中的脚本是否存在路径、权限或语法问题。
5. 验证端口映射是否生效
在GitHub Actions中添加端口监听检查步骤:
# 在npm test之前添加 sudo netstat -tulpn | grep 3305
如果端口未被监听,说明Docker端口映射失败,需检查runner的Docker权限配置。
内容的提问来源于stack exchange,提问作者mmmrefrigerator
相关产品推荐
相关产品推荐

