故障现象
容器内时间,已经通过 ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 修正好了。
root@094bd0f1b1b2:/usr/local/tomcat# date
Fri Jan 13 11:09:02 AM CST 2023
root@e41641d35798:/usr/local/tomcat# date +%Z
CST
root@e41641d35798:/usr/local/tomcat# date +'%:z %Z'
+08:00 CST
root@e41641d35798:/usr/local/tomcat# ll /etc/localtime
lrwxrwxrwx 1 root root 33 Jan 13 13:18 /etc/localtime -> /usr/share/zoneinfo/Asia/Shanghai
root@e41641d35798:/usr/local/tomcat# cat /etc/timezone
Etc/UTC
root@e41641d35798:/usr/local/tomcat# echo $TZ
但是,docker logs 显示时间仍然不正常
2023-01-13 03:07:50.363 INFO 1 --- [nio-8080-exec-1] c.s.lis.pubfun.OptimisticLockDataCheck : 不做校验
2023-01-13 03:07:50.398 INFO 1 --- [nio-8080-exec-1] c.s.lis.pubfun.OptimisticLockDataCheck : 不做校验
2023-01-13 03:07:50.404 INFO 1 --- [nio-8080-exec-1] c.s.lis.pubfun.OptimisticLockDataCheck : 不做校验
JVM怎么获取timezone信息
- JVM uses the environment variable
TZif it is set. - If TZ is not set, then JVM looks for the file
/etc/sysconfig/clockand finds theZONEentry. - If neither
TZnorZONEis set, JVM compares the contents of/etc/localtimeto the files in/usr/share/zoneinfolooking for a match. The matching path and filename under/usr/share/zoneinfoprovides the time zone.
故障分析
Java 在 unix 系统下如何判断 time zone ? 翻译如下
当没有设置 TZ 环境变量的时候, POSIX specification 并没有指定如何判断 timezone 。我在 Linux Standard Base 上没有找到相关资料。 base system library (GNU libc) 使用 /etc/localtime 来判断 timezone。在非嵌入式Linux上,/etc/localtime 是存储 timezone 信息的地方,理想情况这个故事就到此结束了。
(然而, FreeBSD 、NetBSD 、 OpenBSD 使用 /etc/localtime。 Solaris 还有一些其他系统使用 /etc/TIMEZONE。参考 Rosetta Stone for Unix , Dietlibc (used in some embedded Linux systems) 使用 /etc/localtime, uClibc 使用 /etc/TZ (unless patched).)
然而,Java 不是这么做的。Debian 和 Ubuntu 有一个 /etc/timezone 文件保存 timezone 的信息。这个文件用于系统打包,这样它就可以记住像Europe/Amsterdam这样的地理名称,而不仅仅是时区的描述(比如:CET、CEST和CEDT)。这不仅对利于人类理解,而且在更新地理区域设置时更坚实。Sun (现在是 Oracle) Java 偏向使用 /etc/timezone (Red Hat发行版对应于 /etc/sysconfig/clock ) see bug #6456628 而不是 /etc/localtime, OpenJDK 和 gcj 随之效防。
参考:
How do I find the current system timezone?
Java Time Zone is messed up.
解决方案很简单:同时更新 /etc/timezone 和 /etc/localtime 文件。在 Debian 和 Ubuntu上, 官方推荐使用 dpkg-reconfigure tzdata。只对一个应用设置时区,设置 TZ 环境变量即可 (在所有unix平台适用)。
解决方案
echo "Asia/Shanghai" > /etc/timezone
dpkg-reconfigure -f noninteractive tzdata
sudo ln -fs /usr/share/zoneinfo/Europe/Dublin /etc/localtime
sudo dpkg-reconfigure -f noninteractive tzdata
Dockerfile 范例
FROM tomcat:9.0.69-jdk8-temurin
VOLUME $CATALINA_HOME/logs
ARG WAR_FILE=target/*.war
COPY $WAR_FILE ./webapps/
COPY ./docker-entrypoint.sh .
RUN set -eux; \
\
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata; \
chmod +x ./docker-entrypoint.sh
EXPOSE 8080
ENTRYPOINT ["./docker-entrypoint.sh"]
CMD ["catalina.sh", "run"]