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. 远程上首次部署可以用"删卷初始化",但一旦有数据就改用"备份 + 迁移/手动建用户"的方式
相关推荐
再玩一会儿看代码1 小时前
如何理解神经网络中的权重参数?从一张图看懂模型参数量计算
人工智能·经验分享·python·深度学习·神经网络·机器学习
2301_779622411 小时前
mysql如何通过主从备份实现读写分离_配置mysql架构模式
jvm·数据库·python
m0_741173331 小时前
HTML5中WebSocket在弱网环境下的延迟抖动算法补偿
jvm·数据库·python
l1t1 小时前
astral-sh发布的musl和gnu版本standalone python 性能比较
开发语言·python
2401_871492851 小时前
Pandas如何做时间差对齐_pd.merge_asof按最近的时间戳合并两表
jvm·数据库·python
sg_knight2 小时前
Python 设计模式:迭代器模式——用优雅的方式遍历一切
python·设计模式·迭代器模式
阿豪只会阿巴2 小时前
【没事学点啥】TurboBlog轻量级个人博客项目——Turbo Blog 项目学习与上线指南
开发语言·python·学习·状态模式
Slow菜鸟2 小时前
Docker 学习篇(三)| Docker安装指南(Linux版)
linux·学习·docker
飞Link3 小时前
构筑你的数字第二大脑:Obsidian 深度解析与配置指南
开发语言·python