Mac M1环境下Docker化Flask应用无法连接MySQL的解决方法
问题背景
在Mac M1环境下,通过docker-compose部署Flask应用(Python 3.8 + Flask 2.2.2)和arm64v8/mysql 8服务时,Flask应用抛出连接错误:
sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2003, "Can't connect to MySQL server on '127.0.0.1' ([Errno 111] Connection refused)")
已尝试修改my.cnf开放远程连接,但问题未解决。
解决方案
1. 修正数据库连接地址
容器内的Flask应用不能用127.0.0.1连接MySQL——每个容器是独立的网络环境,在docker-compose的自定义网络app_network中,需要用MySQL服务名db作为主机地址。
修改Flask的数据库连接字符串示例:
import os from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = ( f"mysql+pymysql://{os.getenv('MYSQL_USER')}:{os.getenv('MYSQL_PASSWORD')}@" f"{os.getenv('MYSQL_HOST', 'db')}:{os.getenv('MYSQL_PORT', 3306)}/{os.getenv('MYSQL_DATABASE')}" ) db = SQLAlchemy(app)
这里的db对应docker-compose中MySQL服务的名称,容器会通过自定义网络自动解析该名称到MySQL容器的IP。
2. 确保MySQL服务完全就绪
depends_on仅保证MySQL容器先启动,但不保证数据库服务已经初始化完成。需要在Flask应用中添加连接重试逻辑:
import time from sqlalchemy import create_engine from sqlalchemy.exc import OperationalError def wait_for_db(): retries = 10 db_uri = os.getenv('SQLALCHEMY_DATABASE_URI') while retries > 0: try: engine = create_engine(db_uri) with engine.connect(): print("数据库连接成功") return except OperationalError as e: retries -= 1 print(f"数据库未就绪,重试剩余次数:{retries},错误信息:{e}") time.sleep(2) raise Exception("数据库初始化超时,无法建立连接") # 在Flask应用启动前调用 wait_for_db()
3. 检查MySQL绑定配置
确保my.cnf中设置允许所有IP访问:
[mysqld] bind-address = 0.0.0.0 port = 3306
注意:官方MySQL镜像默认会处理权限配置,无需手动修改/var/lib/mysql的所有权,可删除Dockerfile中的RUN chown -R mysql:root /var/lib/mysql/指令。
4. 配置数据库用户远程访问权限
在./docker/db目录下添加初始化SQL脚本(如init.sql),赋予用户从任意主机连接的权限:
GRANT ALL PRIVILEGES ON `${MYSQL_DATABASE}`.* TO `${MYSQL_USER}`@`%` IDENTIFIED BY `${MYSQL_PASSWORD}`; FLUSH PRIVILEGES;
该脚本会在MySQL容器首次启动时自动执行,解决用户仅能本地连接的问题。
5. 简化MySQL Dockerfile
官方arm64v8/mysql镜像已支持通过环境变量初始化数据库,无需在Dockerfile中重复定义ARG和ENV,简化后的Dockerfile:
FROM arm64v8/mysql COPY my.cnf /etc/mysql/my.cnf
内容的提问来源于stack exchange,提问作者jithendran

