时间整整差了八小时
那天上午,同事在群里问我:昨晚那个定时清理任务,是不是出问题了?我说没有啊,日志显示跑成功了。他说:那你看看时间——清理任务配的是凌晨两点跑,日志里写的是上午十点。
我打开日志一看,每一条时间戳都比实际早了……不对,是晚了整整八个小时。凌晨两点的任务,日志记成了上午十点。
但任务确实是凌晨两点跑的,因为业务那边两点之后数据就被清了。
也就是说:任务跑对了,日志记错了。
一通乱查
我自信满满地开始排查。
- 是不是服务器时间不准?
date看了一下,服务器显示的时间是当前北京时间,对的。
- 是不是 NTP 没同步、时钟漂移了?
timedatectl看了,NTP 同步正常,没有漂移。
- 是不是日志框架的配置里时区写死了?翻了一遍配置,没看到时区相关字段。
- 是不是任务实际跑晚了八小时?我又去翻业务侧的记录,确认任务确实是凌晨两点执行的——业务数据两点之后就被清理了,铁证如山。
- 是不是日志采集有延迟?日志是应用直接写的本地文件,没有采集链路,不存在延迟。
- 是不是这台机器的系统时区和服务时区不一致?
最后这一条,我查了好几遍才反应过来。date 显示的是北京时间没错,但我漏看了一个东西——日志框架读的时间,不是系统时间,是 JVM 的时间,而 JVM 默认用的是……UTC。
真相
熟悉容器化部署的人这时候大概要笑出声了。
那台机器上跑的是一个容器化应用。宿主机的时区是 Asia/Shanghai,date 显示北京时间,一切正常。但容器内部默认是 UTC 时区——这是几乎所有基础镜像的默认行为,从 Alpine 到 Debian 到 Ubuntu,清一色 UTC。
应用跑在容器里,JVM 继承容器的时区,也就是 UTC。于是日志框架打印时间戳时,用的是 UTC 时间。北京时间凌晨两点,正好是 UTC 前一天的下午六点——不对,是 UTC 当天的……算了,反正是差了八个小时。日志里记的每一行,都比北京时间晚八小时(或者说,UTC 的"凌晨两点"对应的是北京的"上午十点",方向取决于你怎么算,但差值永远固定:八小时)。
任务在容器里按 UTC 的凌晨两点跑?也不对,任务其实是按宿主机 cron 跑的,宿主机是北京时间两点触发的,触发了容器里的应用,应用用 UTC 打了日志。所以:执行时间对,日志时间错。
解决简单到令人窒息:容器启动时挂一个环境变量 TZ=Asia/Shanghai,或者把宿主机的 /etc/localtime 挂进容器。一行配置的事。
几条血泪教训
折腾半天换来的几句话,送给每一个觉得"时间能有什么坑"的人:
- "差八小时"基本就是时区问题,第一时间查时区。 这是中国开发者最经典的时区坑——UTC 和 Asia/Shanghai 差八小时,看到这个差值,别犹豫,直接查时区配置。从服务器时区、应用时区、到容器时区,逐层确认。
- 容器默认是 UTC,这不是 bug 是约定。 几乎所有基础镜像出厂都是 UTC 时区,不管你宿主机是哪个时区。容器里的应用读到的"当前时间"是 UTC 时间。部署容器化应用,第一步就该设
TZ环境变量,或者挂载/etc/localtime。
- 日志时间用 UTC 存、本地时区显示,别混用。 业界最佳实践是日志统一存 UTC 时间戳,展示时再按用户时区转换。最怕的是同一份日志里,有的行用 UTC、有的行用本地时间,排查时能把人逼疯。
- "date 对 ≠ 应用时间对"。
date显示的是系统时钟,应用读的时区可能完全不同——尤其是跑在容器、JVM、Python 里的应用,它们各自有自己的时区上下文。排查时间问题,要查的是"应用看到的时区",不是"系统时间"。
- 点赞
- 收藏
- 关注作者
评论(0)