使用Docker+FastAPI+SQLAlchemy无法连接容器内MySQL服务器
解决FastAPI容器无法连接MySQL容器的问题
问题分析
报错Can't connect to MySQL server on 'db:3306'通常由以下原因导致:
- App容器在MySQL容器完全启动并就绪前就尝试发起连接
- SQLAlchemy配置未遵循Docker最佳实践,硬编码连接字符串(虽本次值正确,但易引发后续维护问题)
- MySQL用户权限限制,不允许跨容器访问
- MySQL初始化脚本执行异常,未完成数据库/用户创建
解决方案
1. 让App等待MySQL就绪后再启动
depends_on仅保证MySQL容器先启动,但不保证MySQL服务已就绪可接受连接。需给App添加等待逻辑:
方法1:用netcat检测端口(需在App镜像中安装netcat)
修改docker-compose.yml中app服务的command:
services: app: # 保留原有配置 command: sh -c "while ! nc -z db 3306; do sleep 1; echo '等待MySQL启动...'; done; uvicorn main:app --host 0.0.0.0 --port 80"
同时在Dockerfile中添加netcat安装命令(根据基础镜像调整):
- Debian/Ubuntu基础镜像:
RUN apt-get update && apt-get install -y --no-install-recommends netcat && rm -rf /var/lib/apt/lists/* - Alpine基础镜像:
RUN apk add --no-cache netcat-openbsd
方法2:使用wait-for-it脚本
下载wait-for-it.sh到项目根目录,修改Dockerfile复制并赋予执行权限:
COPY wait-for-it.sh /apps/wait-for-it.sh RUN chmod +x /apps/wait-for-it.sh
再修改docker-compose的command:
command: sh -c "/apps/wait-for-it.sh db:3306 -- uvicorn main:app --host 0.0.0.0 --port 80"
2. 修正SQLAlchemy配置,读取环境变量
避免硬编码连接字符串,读取docker-compose中设置的DATABASE_URL环境变量:
import os from sqlalchemy import create_engine from sqlalchemy.orm.session import sessionmaker from sqlalchemy.ext.declarative import declarative_base # 优先读取环境变量,未获取到则使用默认值 database_url = os.getenv("DATABASE_URL", "mysql+mysqlconnector://user_x:pass_xx@db:3306/x_db") ENGINE = create_engine(database_url, echo=True) SESSION = sessionmaker(bind=ENGINE) BASE = declarative_base()
3. 检查MySQL用户权限
确保init.sql中创建的用户允许从任意IP(含Docker内部网络)访问:
# 替换原有创建用户的语句 CREATE USER 'user_x'@'%' IDENTIFIED BY 'pass_xx'; GRANT ALL PRIVILEGES ON x_db.* TO 'user_x'@'%'; FLUSH PRIVILEGES;
若用户为user_x@localhost,则仅能在MySQL容器内部访问,App容器无法连接。
4. 排查MySQL初始化日志
查看MySQL容器启动日志,确认初始化是否完成:
docker logs <mysql-container-name>
若日志存在语法错误等异常,需修正init.sql后重新启动容器。
验证步骤
- 停止并清理所有容器:
docker-compose down -v(-v会删除数据卷,测试场景使用,生产环境谨慎操作) - 重新构建并启动:
docker-compose up --build - 查看App容器日志,确认是否成功连接MySQL
内容的提问来源于stack exchange,提问作者Uziel Trujillo
相关产品推荐
相关产品推荐

