背景
写了个 Python 脚本,每分钟往数据库写一次 CPU、内存数据:
* * * * * /usr/bin/python3 /home/YOUR_USER/app/write_db.py手动跑没问题,但 Crontab 里跑总是失败。
原因
两个问题:
问题 1:Crontab 环境没有 .env 变量
write_db.py 里用 os.environ.get(“DB_ROOT_PASSWORD”) 读密码。手动跑时终端有这些变量,但 Crontab 是一个干净的环境,什么都没有。
问题 2:脚本可能堆积
如果某次脚本卡住(数据库慢、网络抖动),1 分钟后又起一个进程,10 分钟后堆积 10 个。
解决
第一步:加载 .env
* * * * * export $(cat /home/YOUR_USER/app/.env | xargs) && /usr/bin/python3 /home/YOUR_USER/app/write_db.py >> /home/YOUR_USER/app/write_db.log 2>&1拆解:
export $(cat .env | xargs) → 把 .env 里的 KEY=VALUE 加载为环境变量
&& → 加载成功才执行后面的命令
log 2>&1 → 输出和错误都追加到日志
第二步:加锁防重入
* * * * * flock -xn /tmp/write_db.lock bash -c 'export $(cat /home/YOUR_USER/app/.env | xargs) && /usr/bin/python3 /home/YOUR_USER/app/write_db.py' >> /home/YOUR_USER/app/write_db.log 2>&1flock -xn 含义:
-x → 排他锁
-n → 非阻塞(拿不到锁立即退出,不等待)
效果:上一次没跑完,这一次直接跳过。不会堆积。
第三步:验证
# 看 Crontab 生效 crontab -l # 等 1 分钟,看日志 tail -5 /home/YOUR_USER/app/write_db.log预期:每分钟出现一条新记录。
测锁生效:
# 手动持锁 30 秒 flock -xn /tmp/write_db.lock sleep 30 & # 立刻再试一次(应该拿不到锁) flock -xn /tmp/write_db.lock echo "这行不该输出" echo "退出码: $?"预期:第二行不输出,退出码是 1。
三个坑
坑 1:以为 Crontab 会继承用户环境
不会。Crontab 的环境极其干净,连 PATH 都跟登录 Shell 不一样。所有环境变量都要显式加载。
坑 2:export $(cat .env | xargs) 里 .env 有空格会炸
如果 .env 的值里有空格(比如 DB_DESC=my database),xargs 会把它拆成两个参数。.env 里尽量别写带空格的值。
坑 3:flock 的锁文件放 /tmp 就行
不用放项目目录——/tmp 重启会自动清,没有残留。而且不同脚本要用不同的锁文件(/tmp/scriptA.lock、/tmp/scriptB.lock),否则会互相阻塞。