上周上线 Spring Boot 服务,报表时间整体偏移 8 小时。docker exec 进容器 date 显示 UTC,宿主机却是 CST,时间戳全偏。记三个坑。
先看现象
docker exec -it myapp date
输出 Fri Oct 30 02:11:00 UTC 2026,宿主机却是 CST。再 cat /etc/timezone 写着 Etc/UTC,默认 UTC。
第一个坑:只设 TZ 环境变量没用
很多人第一招 docker run 加 -e TZ=Asia/Shanghai,我也试了,重建再 date 还是 UTC。根因是基础镜像 openjdk:17-jdk-slim 没有 /usr/share/zoneinfo,TZ 指向的路径不存在,glibc 回退 UTC。老版本 JVM、C 库、cron 读 /etc/localtime 不认 TZ,只设环境变量等于没改。
第二个坑:装 tzdata 时构建卡死
补时区得在 Dockerfile 装 tzdata。我先写 RUN apt-get install -y tzdata,docker build 直接卡死。查了才知道 tzdata 安装会弹交互界面,docker build 没终端就一直等。正确写法先关交互:
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y tzdata && \
ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
写入 /etc/timezone 内容为 Asia/Shanghai && \
dpkg-reconfigure --frontend noninteractive tzdata && \
rm -rf /var/lib/apt/lists
关键三步:装包、软链、写 /etc/timezone,少一步部分工具仍读错。
第三个坑:Alpine 镜像命令不一样
另一个服务用 alpine:3.18,照搬 apt 命令直接报 command not found。Alpine 是 apk 包管理,得换:
RUN apk add --no-cache tzdata && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
写入 /etc/timezone 内容为 Asia/Shanghai
apk 没有 dpkg-reconfigure,逻辑一样但命令全变。漏了 --no-cache 镜像多一层缓存。
我的根治做法
现在新镜像我都在 Dockerfile 写死时区。老容器临时救火可以:
docker run -e TZ=Asia/Shanghai -v /etc/localtime:/etc/localtime:ro myapp
挂载宿主机 localtime 进去,date 立刻变对。但这是应急,重建又回 UTC,最终改 Dockerfile。k8s 在 Pod 的 env 加 TZ=Asia/Shanghai 效果等同。
验证:改完 docker exec 进去 date 变 CST、cat /etc/timezone 是 Asia/Shanghai,日志时间戳就正常。下一篇写 Java 层强制用中国时区,你踩过的坑下篇写你最想看的。