第 13 课:让 FastAPI 连接宝塔 MySQL
第四阶段 · 真实 student-management-api · 面向 Docker 初学者
本课完成标准
你能够:
- 理解容器内 127.0.0.1 与 ECS 主机的区别;
- 选择 host.docker.internal 或 ECS 内网 IP;
- 从容器内部测试主机名、TCP 3306 和 MySQL 登录;
- 使用 DB_HOST、DB_PORT、DB_USER、DB_PASSWORD、DB_NAME;
- 保持 3306 不开放公网;
- 关闭生产环境 SQL 详细日志。
1. 先画清网络关系
FastAPI 容器
│ TCP 3306
▼
ECS 主机 MySQL(宝塔安装和管理)
在容器中:
| 地址 | 代表什么 |
|---|---|
| 127.0.0.1 | 当前 API 容器自己,不是 ECS 主机 |
| host.docker.internal | Docker 提供的主机名;Linux 需要映射到 host-gateway |
| ECS 内网 IP | 服务器在阿里云 VPC 中的地址 |
| 公网 IP | 不应作为同机数据库连接的首选 |
**推荐顺序:**同一台 ECS 上先尝试 host.docker.internal + host-gateway;如果环境不支持,再使用 ECS 内网 IP,并确认 MySQL 监听和授权。
2. 检查宝塔 MySQL 状态
在宝塔面板确认 MySQL 正在运行,并记录数据库名、应用用户和端口。通过 ECS 终端查看监听:
sudo ss -lntp | grep 3306
sudo systemctl status mysqld || sudo systemctl status mysql
可能看到 MySQL 监听 127.0.0.1:3306、ECS 内网地址或 0.0.0.0:3306。不要为了“先跑通”直接开放安全组 3306 到公网。
| 监听结果 | 含义 | 下一步 |
|---|---|---|
| 127.0.0.1:3306 | 只接受主机回环连接 | 检查是否允许 Docker 网桥/主机网关访问 |
| ECS 内网 IP:3306 | 接受该内网接口连接 | 使用 ECS 内网 IP 测试 |
| 0.0.0.0:3306 | 多个接口监听 | 仍需用安全组和用户来源限制访问 |
3. 给 Compose 加主机网关别名
在 compose.yaml 的 api 服务中增加:
services:
api:
extra_hosts:
- "host.docker.internal:host-gateway"
如果已有 api 配置,只添加这些行。然后把 ECS 上的 .env 设置为:
DB_HOST=host.docker.internal
DB_PORT=3306
DB_USER=student_app
DB_NAME=student_db
DB_PASSWORD=请填写宝塔中创建的密码
不要在课程文件、GitHub 或聊天中填写真实密码。如果密码曾经暴露,先在宝塔 MySQL 中修改。
4. 重新创建容器并解析主机名
docker compose up -d --build
docker compose ps
docker compose exec api python -c "import socket,os; h=os.environ['DB_HOST']; print(h, socket.gethostbyname(h))"
如果输出一个 IP,说明容器能解析主机名;这还不代表 3306 一定可连。继续测试 TCP:
docker compose exec api python -c "import socket,os; s=socket.create_connection((os.environ['DB_HOST'], int(os.environ.get('DB_PORT','3306'))), 5); print('tcp ok'); s.close()"
| 结果 | 判断 |
|---|---|
| 能解析、TCP ok | 网络路径基本正常,继续测试 MySQL 账号 |
| getaddrinfo 错误 | 主机名或 extra_hosts 配置错误 |
| Connection refused | MySQL 没监听该地址/端口 |
| Timeout | 防火墙、监听地址或网络路径阻断 |
5. 从容器内部测试 MySQL 登录
镜像已经安装 PyMySQL,可以让 Python 从环境变量读取密码:
docker compose exec api python -c "import os,pymysql; c=pymysql.connect(host=os.environ['DB_HOST'], port=int(os.environ['DB_PORT']), user=os.environ['DB_USER'], password=os.environ['DB_PASSWORD'], database=os.environ['DB_NAME'], connect_timeout=5); print(c.get_server_info()); c.close(); print('mysql ok')"
不要把完整命令输出中的环境变量或连接串复制到公开聊天。
登录失败时重点看:
- 用户名是否是宝塔创建的 student_app;
- 用户是否授权了 student_db;
- MySQL 用户的 Host 来源是否允许 Docker 网桥或主机网关;
- 密码是否修改过但 ECS 的 .env 仍是旧值。
6. 让 FastAPI 使用同一套变量
项目的 config.py 会读取 DB_HOST、DB_PORT、DB_USER、DB_PASSWORD、DB_NAME,生成 mysql+pymysql 连接 URL。Alembic 的 migrations/env.py 也读取同名变量。
修改 .env 后需要重新创建容器:
docker compose up -d
docker compose exec api env | grep '^DB_' | sed 's/DB_PASSWORD=.*/DB_PASSWORD=******/'
docker compose logs --tail=100 api
验证页面:
curl -i http://127.0.0.1:8000/login
curl -i http://127.0.0.1:8000/students
/students 需要登录和数据库,具体返回可能是未授权响应;重点观察日志中不再出现连接拒绝、认证失败或 Unknown database。
7. 生产日志不要输出 SQL 和密码
当前项目的 database.py 使用了 echo=True,生产环境应改为:
engine = create_engine(
settings.database_url,
echo=False
)
不要通过日志打印完整连接 URL。SQL 日志可能包含用户输入或敏感数据。
8. 常见错误排查
| 错误 | 优先检查 | 不要做什么 |
|---|---|---|
| Unknown MySQL server host | extra_hosts、DB_HOST | 不要改成公网 IP 试运气 |
| Can't connect / refused | ss -lntp、MySQL 监听地址 | 不要开放 3306 到 0.0.0.0/0 |
| Access denied | 用户名、密码、用户 Host 和授权 | 不要改用 root |
| Unknown database | student_db 是否存在 | 不要在应用启动时偷偷创建生产库 |
| 页面 500 | docker compose logs api | 不要只看浏览器提示 |
9. 一分钟自测
问题 1:容器里的 127.0.0.1 指向哪里?
- A. 当前 FastAPI 容器自己
- B. ECS 主机 MySQL
- C. GitHub 服务器
问题 2:连接数据库失败时,为什么先测 DNS 和 TCP?
- A. 区分地址解析、网络连通和账号认证问题
- B. 自动修改 MySQL 密码
- C. 自动执行 Alembic 迁移
答案:1A,2A。
本课通关证据
docker compose exec api python -c "import socket,os; print(socket.gethostbyname(os.environ['DB_HOST']))"
docker compose exec api python -c "import socket,os; s=socket.create_connection((os.environ['DB_HOST'],3306),5); print('tcp ok'); s.close()"
docker compose logs --tail=100 api
完成标准:容器能解析主机地址、TCP 3306 可达、专用用户能登录 student_db,需要数据库的页面不再出现连接错误,且 3306 没有公网规则。
如果遇到错误,请贴完整命令和输出,但隐藏密码、Token 与私钥。
← 第 12 课:真实项目 Dockerfile 与 Compose · 第 14 课:SQLModel + Alembic →
想系统学习这门课程?
你已阅读完本文。需要更完整的知识体系,可以继续了解「Vibe Coding 实战课程」。
查看课程详情