Docker容器默认UTC时区,应用日志与东八区相差8小时

来源:互联网 时间:2026-10-31

对账脚本的时间对不上,订单表的创建时间是2026-10-24 10:15:22,应用日志里同一笔写的是02:15:22。两边差了整整八小时,宿主机上date输出的时间是准的。

容器启动时如果没有显式指定时区,多数官方基础镜像里也没有/etc/localtime这个文件,glibc找不到就退回UTC。日志里的时间由应用自己生成,取的是容器内的时间,于是整批记录都往前提了八小时。docker logs看到的时间戳不一定可信,它默认照抄应用输出,应用写UTC它就显示UTC,加--timestamps才是dockerd自己打的时间戳,格式固定为UTC带Z后缀,和容器时区无关。宿主机准不代表容器准,这两套是分开的。

做法有三种,挂载宿主机的/etc/localtime到容器同名路径并加只读参数,设置TZ环境变量,在镜像里装tzdata再把TZ写进Dockerfile。三种都能达到目的,前提各不相同,选哪种看部署方式和基础镜像。

TZ这一条有前提。基于glibc的镜像认这个变量,debian、ubuntu、centos都行;glibc是这类镜像默认的C运行库,时区数据由它读取。基于alpine的镜像用的是musl,镜像里默认不带时区数据,export TZ=Asia/Shanghai之后date还是UTC,得先apk add tzdata。这一条踩过好几次,同一个compose文件,换了基础镜像就失效,日志里的时间又回去了。挂载localtime的做法不依赖libc,alpine下也能用,缺点是容器跟宿主机绑在一起,换一台机器就得记得重新挂。

还有一种情况完全绕开容器时区,应用自己在配置里写死了时区,PHP的date.timezone、Java的user.timezone都算,这类写死在应用配置里之后,容器改对了也不跟着变。PHP的话,php.ini里的date.timezone一旦写上Asia/Shanghai,date()和date_default_timezone_get()都按它走,跟容器里的UTC无关。MySQL容器还多一层,它有自己的time_zone参数,默认是SYSTEM,跟容器时区走,写进my.cnf之后就跟容器无关了。

验证不用猜,你进容器执行date,输出带UTC还是带CST一目了然。再看一眼/etc/localtime是不是软链,以及php -i里date.timezone那一行。三处里有一处不对,日志和数据库就会各说各话,误差固定是八小时。

改宿主机时区是不对的,宿主机上的其他服务、日志轮转、证书校验都按它走,为了一个容器动全局,代价不成比例。真要统一,就在镜像层面把tzdata和TZ都打进去,一次做完,之后换机器不用再改。

现在compose里挂载localtime,同时保留TZ变量,镜像换成alpine时也有tzdata兜底。定时任务这块要单独核对,容器里的crond按容器时区算,写着0 2 * * *的任务,实际在本地时间上午十点跑。对账、报表、备份这类按点执行的任务,改完时区之后要重新对一遍执行时间,别只看日志时间对了就以为全对。跨时区的日志检索也顺手改掉,之前按字符串比较时间做的报警规则全部要重写,否则阈值判断会整批偏移。

相关文章

标签:

A5创业网 版权所有