切换语言为:繁体

Docker中更新JDK时区规则

  • 爱糖宝
  • 2024-08-28
  • 2067
  • 0
  • 0

背景

最近, 由于部分国家和地区取消了夏令时, 导致我们的系统中的定时任务出现了提前执行的情况。这个问题的根源在于, 我们当前使用的 Java 开发工具包 (JDK) 内置的时区规则数据已经过期, 无法及时反映各地区时区调整的最新变化。

为了解决这一问题, 在不升级整个 JDK 版本的前提下, 我们需要寻找一种更加便捷的方式, 来独立更新 JDK 中的时区规则数据。这不仅可以确保我们的系统时间处理功能与最新的时区规则保持一致, 也避免了全量升级 JDK 所带来的风险和工作量。

以下重点介绍在 Docker 中如何更新 JDK 的 time zone database。

了解 Time Zone Database

时区数据库 (Timezone Database) 通常被称为 tz 或 zoneinfo, 是一个由 IANA (互联网名称与数字分配机构) 管理和维护的权威数据库。它包含了全球各地区的时区信息, 包括时区边界、与 UTC (世界统一时间) 的偏移量, 以及夏令时的实施规则等详细数据。

该数据库遵循 BCP 175 标准, 会定期根据各国政府机构对时区规则的调整而进行更新, 以确保数据的准确性和时效性。这个数据库格式也被称为"Olson 数据库", 因为它最初是由艾伦·奥尔森 (Olson) 开发的。

Oracle 公司在其不同版本的 Java 开发工具包 (JDK) 中都内置了时区数据库的不同版本,具体可参考 Timezone Data Versions in the JRE Software。Oracle 依赖这些公开提供的时区数据, 并提供了 TZUpdater 工具, 允许用户根据需要更新 JDK 和 JRE 中的时区数据, 以适应各国夏令时规则的变化。

从 Java 8 开始, 时区数据库文件 tzdb. Dat 被放置在 JAVA_HOME/jre/lib 目录下。Oracle 的 JDK 中的时区数据库实现是基于 IANA 时区数据库, 并且可以通过 Oracle 提供的工具进行版本更新和维护, 以保证时区数据的最新状态。

关于 TZUpdater 的下载链接和使用方法, 可以分别参考 Oracle 官方提供的 Java SE Timezone Updater Download时区更新程序工具使用说明, 其中后者对工具的安装、命令选项、使用场景等进行了非常详细的介绍。

在 Docker 中更新 TZ

在 docker 中更新 TZ 主要是利用 TZUpdate 工具进行更新,在 Dockerfile 中将 tzupdate.jar 复制到 docker 容器中,再执行更新操作,具体步骤如下:

  1. 选择合适的基础镜像,通常使用官方的 OpenJDK 镜像作为基础镜像, 例如: FROM openjdk:8-jdk-slim

  2. 复制应用程序 JAR 文件和 TZUpdater 工具,将应用程序 JAR 文件和 TZUpdater 的 JAR 文件复制到 Docker 镜像中: COPY tzupdater.jar tzupdater.jar

  3. 获取最新的时区数据,有两种方式获取最新的时区数据: a. 下载最新 tzdata 包并复制到镜像中:

    COPY tzdata2024a.tar.gz tzdata2024a.tar.gz 
    RUN java -jar tzupdater.jar -l file:///tzdata-latest.tar.gz

    b. 直接指定最新 tzdata 包的链接,这种方式更加简洁, 每次构建镜像时都会自动下载最新的时区数据,例如: RUN java -jar tzupdater.jar -l https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz

整体的 Dockerfile 参考如下:

# 使用 OpenJDK 8 作为基础镜像  
FROM openjdk:8-jdk-slim  
  
VOLUME /tmp  
  
# 复制应用程序 JAR 文件  
COPY app.jar app.jar

# 复制 tzupdar.jar 到docker中
COPY tzupdater.jar tzupdater.jar  
  
# 更新时区数据
COPY tzdata2024a.tar.gz tzdata2024a.tar.gz  
RUN java -jar tzupdater.jar -l file///tzdata2024a.tar.gz 

# 或者这种
# RUN java -jar tzupdater.jar -l file///tzdata2024a.tar.gz](https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz
  
EXPOSE 8080  
  
ENTRYPOINT ["java", "-jar", "app.jar"]

自制 JDK 并更新 TZ

为了避免在每次构建应用程序镜像时都重复执行时区更新操作, 从而产生不必要的开销, 我们可以采用一种更加优化的方式。首先, 基于官方的 JDK 基础镜像, 创建一个包含最新时区数据的自定义基础镜像。然后, 应用程序的 Dockerfile 直接使用这个自定义基础镜像作为基础镜像, 这样就无需在应用程序镜像构建过程中再执行时区更新操作了, 从而提高了效率。

基于 adoptopenjdk:8-jdk-hotspot 镜像, 我编写了一个自定义的 Dockerfile, 用于构建包含最新时区数据的基础镜像。Dockerfile 的内容如下:

FROM adoptopenjdk:8-jdk-hotspot

COPY tzupdater.jar tzupdater.jar
COPY tzdata2024a.tar.gz tzdata2024a.tar.gz
RUN java -jar tzupdater.jar -l file:///tzdata2024a.tar.gz

接下来, 我可以基于这个自定义的 Dockerfile 构建包含最新时区数据的基础镜像。构建完成后, 可以为该镜像打上适当的标签, 并将其推送到团队共享的镜像仓库中, 供其他成员使用。

总结

为了解决由于时区规则变更导致的定时任务执行异常问题, 我们需要更新 JDK 中内置的时区数据库 (Time Zone Database)。Oracle 提供了 TZUpdater 工具, 允许我们独立更新 JDK 中的时区数据, 无需升级整个 JDK 版本。

在 Docker 环境中, 我们可以在 Dockerfile 中利用 TZUpdater 工具更新时区数据,为了进一步优化,我们可以基于官方 JDK 镜像创建一个自定义的基础镜像,在其中预先更新好最新的时区数据。这样,应用程序的 Dockerfile 只需要使用这个自定义基础镜像,就无需再执行时区更新操作,从而提高了构建效率。该自定义基础镜像可以在团队内部共享使用。

通过以上方式, 我们可以确保 Docker 容器中运行的 Java 应用程序始终使用最新的时区规则, 避免由于时区变更导致的问题, 同时也遵循了 Docker 镜像分层和复用的最佳实践, 提高了开发效率。

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.