Docker 部署踩坑记录:从“构建失败”到“服务跑通”,以及为什么数据被清空了

Docker 部署踩坑记录:从"构建失败"到"服务跑通",以及为什么数据被清空了

这篇文章记录一次把本项目(FastAPI + MySQL)用 Docker Compose 在 Windows 本地跑通、再准备迁移到远程 Linux 的过程中遇到的典型问题与解决方案。

对应仓库关键文件:


背景:我想要的目标很简单

  • 本地 Windows 先用 Docker 跑通(可访问 /docs
  • 然后把同一套 docker-compose.prod.yml + .env 拿到远程 Linux 一键复用

实际过程里,一共踩了 4 个坑:

  1. 构建阶段拉取 ghcr.io 失败(EOF)
  2. 后端容器一直重启:MySQL 认证失败(1045)
  3. MySQL 8 认证插件导致 PyMySQL 需要 cryptography
  4. 为了让新账号/密码生效,我删除了数据卷,导致历史数据被清空

下面按时间线复盘。


坑 1:Docker build 拉 ghcr.io/astral-sh/uv:latest 失败(EOF)

现象

执行:

bash 复制代码
docker compose --env-file .env -f docker-compose.prod.yml up -d --build

报错类似:

text 复制代码
failed to resolve source metadata for ghcr.io/astral-sh/uv:latest ... EOF

根因

原始 Dockerfile 使用了:

dockerfile 复制代码
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

这要求构建机能稳定访问 ghcr.io(GitHub Container Registry)。在部分网络环境下,访问 GHCR 会不稳定或被拦截,从而导致构建失败。

解决

把"从 GHCR 拷 uv 二进制"的方式,改成"从 PyPI 安装 uv":

dockerfile 复制代码
RUN python -m pip install --no-cache-dir -U pip && \
    python -m pip install --no-cache-dir uv

这样构建阶段只依赖 PyPI(通常更好访问),构建成功率大幅提升。


坑 2:构建成功但访问不了,原因是后端容器在重启

现象

构建已经完成,但访问 http://127.0.0.1:9006/docs 失败。

第一反应是"端口没映射/防火墙",但正确排查顺序是先看容器状态:

bash 复制代码
docker compose --env-file .env -f docker-compose.prod.yml ps

会看到类似:

text 复制代码
nongzidian_api   ...   Restarting (1) ...

也就是说:不是"访问不了",而是"服务根本没跑起来"。

根因

查看日志:

bash 复制代码
docker logs --tail 200 nongzidian_api

出现:

text 复制代码
sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1045, "Access denied for user 'root'@'...' (using password: YES)")

这是典型的 MySQL 认证失败。

当时 DATABASE_URL 使用了 root 账号连接:

env 复制代码
DATABASE_URL=mysql+pymysql://root:密码@db:3306/nongzidian

但 MySQL 官方镜像中,root 的远程登录策略、插件策略可能与你预期不同(尤其是容器间连接场景),导致被拒绝。

解决

改成使用业务账号连接 MySQL(更推荐的生产做法):

  • docker-compose.prod.yml 中让 MySQL 初始化创建业务用户:
    • MYSQL_USER
    • MYSQL_PASSWORD
  • .env 中改用业务用户拼 DATABASE_URL

示例:

env 复制代码
MYSQL_USER=nongzidian
MYSQL_PASSWORD=你的强密码
DATABASE_URL=mysql+pymysql://nongzidian:你的强密码@db:3306/nongzidian

坑 3:MySQL 8 认证方式导致 PyMySQL 报 cryptography 缺失

现象

认证失败的报错从 1045 变成了:

text 复制代码
RuntimeError: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods

根因

MySQL 8 常见的默认认证插件是 caching_sha2_password。PyMySQL 在使用某些认证方式时需要 cryptography 包参与加密过程,否则会直接报错。

即便你加了:

yaml 复制代码
command: --default-authentication-plugin=mysql_native_password

也不能保证所有用户/场景都一定不会走到 caching_sha2_password(历史数据卷里旧用户、初始化顺序、镜像行为差异等都会影响)。

解决(推荐)

在项目依赖里显式加入 cryptography,让后端在任何 MySQL 8 认证方式下都能稳定连接:

并且更新 uv.lock,否则 Dockerfile 里的:

bash 复制代码
uv sync --frozen ...

会因锁文件不匹配而失败。


坑 4:为什么"跑通了",但历史数据全没了?

现象

容器最终确实跑通了,/docs 能访问,但发现之前 MySQL 里已有的数据不见了。

根因

MySQL 数据存在 Docker 的命名卷里(本项目是 mysql_data)。

当你修改这类 MySQL 初始化参数时:

  • MYSQL_ROOT_PASSWORD
  • MYSQL_USER
  • MYSQL_PASSWORD
  • MYSQL_DATABASE

如果数据卷已经存在,MySQL 容器启动时通常会跳过初始化逻辑(因为它认为已经初始化过了)。

为了让"新账号/新密码"生效,常见做法是删除数据卷:

bash 复制代码
docker compose --env-file .env -f docker-compose.prod.yml down -v

注意这个 -v 的含义:删除卷 = 删除数据库文件 = 数据清空。

解决与正确姿势

如果你只是本地测试,删卷没问题;但如果你在正式环境已经有数据了,必须先备份。

方案 A:删卷前先备份(推荐)
bash 复制代码
docker exec nongzidian_mysql mysqldump -uroot -p你的root密码 nongzidian > backup.sql

然后再删卷重建。

方案 B:不删卷,手动在 MySQL 里创建用户(生产更常见)

进入 MySQL 容器执行 SQL(思路示例):

sql 复制代码
CREATE USER IF NOT EXISTS 'nongzidian'@'%' IDENTIFIED BY '你的强密码';
GRANT ALL PRIVILEGES ON nongzidian.* TO 'nongzidian'@'%';
FLUSH PRIVILEGES;

这样既能让后端用业务账号登录,也不会丢数据。


额外小坑:Windows PowerShell 的 curl

在 Windows PowerShell 里,curl 常常是 Invoke-WebRequest 的别名,行为和 Linux 的 curl 不一样。

如果你想用真正的 curl,可用:

bash 复制代码
curl.exe -I http://127.0.0.1:9006/docs

或者直接用浏览器打开 /docs


最终我得到的一套"更稳"的结论

  1. Docker 构建阶段尽量少依赖不稳定的镜像源(例如 GHCR),否则"能跑/不能跑"全看网络脸色
  2. MySQL 连接不要用 root,推荐用业务账号(权限最小化、行为更可控)
  3. MySQL 8 + PyMySQL 场景,显式加 cryptography 可以显著降低认证相关坑
  4. 修改 MySQL 初始化环境变量时要牢记:已有数据卷时这些变量不一定生效
  5. down -v 是大杀器:它解决问题也会清空数据,上线前必须备份

如果你准备把这套部署搬到远程 Linux,建议顺序是:

  1. 本地跑通并固定配置
  2. .env 里的密码全部换成生产强密码
  3. 远程上首次部署可以用"删卷初始化",但一旦有数据就改用"备份 + 迁移/手动建用户"的方式
相关推荐
biter down28 分钟前
基于 Pywinauto 的 QQ 音乐 GUI 自动化测试实践
python
人道领域31 分钟前
【LeetCode刷题日记】669.修剪二叉搜索树
开发语言·python·算法
EntyIU2 小时前
mineru从安装部署到测试使用完整指南
python·ocr
qq_452396232 小时前
第十篇:《Dockerfile 最佳实践与镜像瘦身》
docker
安替-AnTi2 小时前
厚朴 APK 搜索接口分析
python·apk·解析·taobao
Plastic garden2 小时前
Docker(1)
运维·docker·容器
山川湖海3 小时前
AI时代快速学编程语言的陷阱(以Python为例)
大数据·人工智能·python
H Journey3 小时前
Supervisor 进程管理工具介绍
python·supervisor·linux 运维
春日见3 小时前
5分钟入门强化学习之动态规划算法与实现
大数据·人工智能·python·算法·机器学习·计算机视觉
gs801404 小时前
网络隐形杀手:从 Could not connect to SMTP host 报错深度剖析 Docker MTU 黑洞理论与实战
网络·docker·容器