半夜不是没人用,是所有人都在抢资源

举报
yd_257405204 发表于 2026/10/18 11:10:01 2026/10/18
【摘要】 半夜不是没人用,是所有人都在抢资源 把任务排在 0 0 * * * 是最省事也最容易出错的选择。00:00 这一刻,日志切割(logrotate)、数据库全量备份、对象存储同步、报表跑批、证书续期,再加上其他团队几十个

半夜不是没人用,是所有人都在抢资源

把任务排在 0 0 * * * 是最省事也最容易出错的选择。00:00 这一刻,日志切割(logrotate)、数据库全量备份、对象存储同步、报表跑批、证书续期,再加上其他团队几十个 0 0 * * *,都会在同一秒启动。CPU 被占满、连接池被占满、备份占满磁盘 IO,你的任务超时退出;白天手动跑一次又恢复正常——这就是“只在半夜失败”的典型现场。

做法很简单:把任务分散到不同时间。sleep $((RANDOM % 300)) 放在脚本第一行,或者直接用 17 2 * * * 这种带偏移的表达式。跑批任务按依赖关系串起来,不要都堆在 00:00。

容器里的 0 点,不是你以为的 0 点

cron 表达式本身不带时区,由系统时区解释。而绝大多数基础镜像默认 TZ=UTC,所以你写的 0 0 * * * 在 UTC 执行,对应北京时间早上 8 点——白天看着正常,一到月初对账就发现数据日期整体偏了一天。

  • cronie 支持在 crontab 顶部写 CRON_TZ=Asia/Shanghai,注意它只影响其后的行。
  • systemd timer 用 OnCalendar=*-*-* 00:10:00 Asia/Shanghai,比改全局时区安全。
  • K8s CronJob 从 1.27 起用 spec.timeZone 指定,别依赖容器镜像的 TZ。

业务代码里也别用 LocalDateTime.now(),统一显式传入时区,这样容器时区改动不会悄悄改变语义。

跨天任务:日期在跑到一半时变了

23:58 启动的任务,跑到 00:03 才写结果,中间两次调用 date 取到的是两天。分区表写错、增量窗口错位、幂等键算错,结果全靠运气。

约定一条:时间只在入口取一次。

RUN_DATE=$(date -d 'yesterday' +%F),之后所有 SQL、文件名、下游参数都传这个变量,脚本内部禁止再出现 now() 或 CURRENT_DATE。SQL 里如果必须用当前时间,改成显式参数 WHERE dt = '${RUN_DATE}'。这条规则能消除一大半“月初或年末才复现”的异常问题。

退出码 0 不代表成功

三种最常见的假成功:curl 拿到 200,但响应体是 {"code":500};cmd | tee app.log 的退出码来自 tee,前面的命令崩了也算成功;set -e 在 if、||、命令替换里会被绕过。

  • 脚本头写 set -euo pipefail,管道中任一环节失败都算失败。
  • HTTP 任务同时校验状态码和业务码,失败时执行 exit 1。
  • 加 flock -n /var/lock/report.lock 防止重复执行,加 timeout 3600 防止卡死。
  • 加心跳:任务成功时请求 curl -fsS https://hc.example.com/ping/xxx,超过预期周期没收到就告警。只告警“失败”是不够的,任务被调度器删掉、机器下线,都不会产生失败事件。

被忽略的半夜杀手:日志与磁盘

logrotate 通常也在 00:00 运行。如果配置是 create,而进程不重新打开文件句柄,它就会继续往已 unlink 的旧文件写入——du 看不到,df 一路涨到 100%,然后所有定时任务在同一晚集体失败。排查时先看 lsof +L1。

加固清单:监控 df 的 inode 与使用率并在 85% 告警;日志用 copytruncate,或让应用自己按大小轮转;关键任务的成功、失败、耗时都打结构化日志,并带上 run_id 和 RUN_DATE,第二天出问题能一眼定位是哪一轮、哪个参数。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。