生产环境备份必须用subprocess调用pg_dump或mysqldump原生命令,禁止用Django dumpdata;脚本需自行初始化Django环境、安全传参、UTC时间戳命名、校验备份完整性并严格权限控制。

备份脚本必须用 subprocess 调用 pg_dump 或 mysqldump,不能依赖 Django ORM
因为 Django 的 dumpdata 只导出序列化数据(JSON/YAML),不包含表结构、索引、约束、函数或权限信息,无法用于完整恢复。生产环境备份必须走数据库原生命令。
常见错误是直接用 python manage.py dumpdata --all > backup.json,结果恢复时发现外键失效、时间字段错乱、迁移记录丢失——这不是备份,是快照式导出。
- PostgreSQL 推荐用
pg_dump -U <code>USER-hHOST-pPORT-Fc --no-owner --no-aclDB_NAME(-Fc生成二进制格式,支持并行恢复和压缩) - MySQL/MariaDB 用
mysqldump --single-transaction --routines --triggers --events --hex-blob <code>DB_NAME(--single-transaction保证一致性,避免锁表) - 脚本中必须从
DATABASES配置读取USER、PASSWORD、HOST、PORT、NAME,但密码不能拼在命令行里(会泄露到ps进程列表),应通过环境变量或.pgpass文件传入
定时任务不能只靠 crontab 直接调 Python 脚本,要处理 Django 环境加载
直接写 0 2 * * * /usr/bin/python3 /path/to/backup.py 很大概率失败:找不到 django 模块、settings 未配置、os.environ["DJANGO_SETTINGS_MODULE"] 缺失。
正确做法是让脚本自己初始化 Django 环境,而不是依赖外部 shell 环境:
立即学习“Python免费学习笔记(深入)”;
- 脚本开头必须包含:
import os import django from django.conf import settings <p>os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings") django.setup() - 不要用
source venv/bin/activate或. venv/bin/activate—— cron 不读 shell profile,激活无效 - 显式指定 Python 解释器绝对路径(如
/opt/myproject/venv/bin/python3),避免 cron 使用系统默认的python3 - 在 crontab 中设置必要环境变量:
PATH=/usr/local/bin:/usr/bin:/bin,否则pg_dump可能找不到
manage.py dbbackup 类插件不适合生产自动备份
像 django-dbbackup 这类第三方包,底层仍是调用 pg_dump 或 mysqldump,但它把数据库凭据硬编码进 settings.py 或明文写进配置文件,且默认不校验备份文件完整性(比如 gzip 是否损坏、pg_dump 是否中途被 kill)。
更关键的是:它把备份逻辑耦合进 Django 生命周期,一旦项目启动失败(比如某个 app 的 apps.py 报错),dbbackup 命令就根本跑不起来——而你最需要备份的时候,往往就是服务刚崩完那会儿。
- 纯 Python 备份脚本应独立于 Django 启动流程,只依赖
subprocess和标准库 - 每次备份后必须执行基础验证:检查输出文件大小是否 > 1MB(防空文件)、用
pg_restore -l或gzip -t校验可读性 - 保留最近 7 天备份,用
find /backups -name "*.dump" -mtime +7 -delete清理,别依赖插件的--keep参数(它可能误删)
备份文件名必须含时间戳且避开时区陷阱
用 datetime.now().strftime("%Y%m%d_%H%M%S") 生成文件名看似合理,但如果服务器时区是 UTC,而运维习惯看北京时间,凌晨 2 点的备份实际叫 20240520_180000(UTC+8),查日志时极易搞混。
更麻烦的是,Django 的 TIME_ZONE 设置不影响 datetime.now(),后者永远按系统时区返回。
- 统一用 UTC 时间戳命名:
datetime.utcnow().strftime("%Y%m%dT%H%M%SZ"),明确标识时区 - 或者从
settings.TIME_ZONE动态加载时区对象再转换:from django.utils import timezone ts = timezone.now().astimezone(timezone.get_current_timezone()) filename = ts.strftime("%Y%m%d_%H%M%S") + ".dump" - 绝对不要用
time.time()或uuid.uuid4()——无法直观对应具体时刻,排查故障时抓瞎
备份脚本里最容易被跳过的,是权限控制和日志落盘:确保备份目录 chmod 700、备份文件 chmod 600,所有 subprocess.run() 必须捕获 stderr 并写入日志文件,而不是只打 print()。cron 默认不继承终端 stdout/stderr,没日志等于没备份。


















