前言
- Alpine Linux 使用的是 musl libc 而不是大多数 Linux 发行版以及 OpenJDK 所使用的 glibc,所以OpenJDK无法直接在alpine镜像中使用
- 目前 Oracle 官方只提供
JDK 21
、JavaFX 21
、JMC 8
的生产就绪 GA Releases
版本,openJdk17 的构建已经过时,hub.docker 所提供的的 OpenJDK 的 docker 官方镜像也只能作为预览使用
作为替代,可以使用 amazoncorretto
或 eclipse-temurin
作为基础镜像
dockerfile
FROM amazoncorretto:17-alpine3.18-jdk
dockerfile
FROM eclipse-temurin:17-jre-alpine
自建镜像
amazoncorretto
- Jdk地址:amazon-corretto-17-downloads
- 镜像地址:hub.docker/amazon-corr...
- Dockerfile: github/corretto/corretto-docker/17/jdk/alpine/3.18/Dockerfile
eclipse-temurin
- Jdk地址:eclipse-temurin-17-downloads
- 镜像地址:hub.docker/eclipse-tem...
- Dockerfile: github/adoptium/containers/17/jre/alpine/Dockerfile
编写 Dockerfile
从JDK地址中找到需要对应JDK的下载链接和SHA256之后,创建如下dockerfile,不同版本放置对应的链接和SHA256即可
参考上述链接中的 Dockerfile,构建的整体思路如下
-
第一构建阶段
- 使用
alpine:3.18
作为基础镜像。 - 设置 JDK 下载链接、SHA256 散列值和安装目录。
- 安装所需的工具,如
wget
、tar
、gzip
和binutils
。 - 下载并校验 JDK。
- 解压 JDK 到指定目录,并移除不必要的文件。
- 设置环境变量,使 JDK 可用。
- 编译一个简单的 Java 类,用于在最终镜像中检查文件编码。
- 使用
jlink
创建一个自定义的 JRE,包含所需的模块。
- 使用
-
第二阶段构建
- 再次使用
alpine:3.18
作为基础镜像。 - 将第一阶段构建的自定义 JRE 和编译好的 Java 类复制到新镜像中。
- 设置环境变量,使 JRE 可用。
- 安装必要的软件包,如 CA 证书、本地化支持和时区数据,以确保 Java 应用在此环境中正常运行。
- 最后,检查 Java 运行环境是否正确安装,并清理不再需要的文件。
- 再次使用
dockerfile
# 第一阶段:使用基础镜像来下载和安装JDK
FROM alpine:3.18 as builder
# 设置 JDK 下载链接、SHA256 散列值和安装目录
ARG JDK_URL='https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9/OpenJDK17U-jdk_x64_alpine-linux_hotspot_17.0.9_9.tar.gz'
ARG JDK_SHA256='c2a571a56e5bd3f30956b17b048880078c7801ed9e8754af6d1e38b9176059a9'
ARG JDK_DIR=/usr/lib/jvm/java17
# 安装必要的工具 ,binutils 是使用jlink所需的工具
RUN set -eux; \
apk add --no-cache wget tar gzip binutils;
# 下载并校验 JDK
RUN set -eux; \
wget -O jdk.tar.gz ${JDK_URL}; \
echo "${JDK_SHA256} *jdk.tar.gz" | sha256sum -c -; \
mkdir -p ${JDK_DIR}; \
tar --extract \
--file jdk.tar.gz \
--directory ${JDK_DIR} \
# 去除归档中每个文件路径的第一部分,因为每个JDK压缩包都有一个不同名的根文件夹
--strip-components 1 \
--no-same-owner; \
# 删除源文件,虽然这里删不删都不影响第二阶段
rm -f jdk.tar.gz ${JDK_DIR}/lib/src.zip;
# 设置环境变量
ENV JAVA_HOME=${JDK_DIR}
ENV PATH=$JAVA_HOME/bin:$PATH
# 编译用于检查 file.encoding 的 Java 类,在第二阶段中,由于没有javac,需要提前编译
RUN echo 'public class CheckEncoding { public static void main(String[] args) { System.out.println(System.getProperty("file.encoding")); } }' > CheckEncoding.java && \
javac CheckEncoding.java && \
rm CheckEncoding.java
# 使用 jlink 创建自定义 JRE,扫描jmods目录添加除GUI、实验性孵化模块以外的所有模块
# 特别注意的是,如果你使用spring程序,务必保留 jdk.unsupported 模块
# 否则会导致objenesis无法跳过构造函数创建代理,从而对没有默认构造函数的BEAN引发NoSuchMethodException
# https://stackoverflow.com/questions/51332334/nosuchmethodexception-springframework-boot-autoconfigure-http-httpmessageconver
# 如果是java 21+ 应使用 compress=zip-6 https://bugs.openjdk.org/browse/JDK-8293667
RUN set -eux; \
MODULES=$(ls ${JAVA_HOME}/jmods | sed 's/.jmod$//' | grep -vE 'java.desktop|jdk.incubator.foreign|jdk.incubator.vector' | tr '\n' ',' | sed 's/,$//') \
&& $JAVA_HOME/bin/jlink \
--module-path ${JAVA_HOME}/jmods \
--add-modules $MODULES \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /jre;
# 为最终镜像设置新的基础镜像
FROM alpine:3.18
# 注意第一阶段的变量无法保留到第二阶段
ARG JDK_HOME_DIR=/opt/java/jdk17
# 复制自定义 JRE 和 CheckEncoding.class 到新镜像
COPY --from=builder /jre ${JDK_HOME_DIR}
COPY --from=builder /CheckEncoding.class /CheckEncoding.class
# 设置环境变量
ENV JAVA_HOME=${JDK_HOME_DIR}
ENV PATH=$JAVA_HOME/bin:$PATH
# 添加所需软件
RUN set -eux; \
apk add --no-cache \
# utilities for keeping Alpine and OpenJDK CA certificates in sync
# https://github.com/adoptium/containers/issues/293
ca-certificates p11-kit-trust \
# locales ensures proper character encoding and locale-specific behaviors using en_US.UTF-8
musl-locales musl-locales-lang \
tzdata \
; \
rm -rf /var/cache/apk/*;
# 设置其它环境变量
ENV TZ='Asia/Shanghai'
ENV LANG=C.UTF-8
# 检查JAVA运行环境并删除 CheckEncoding.class
RUN set -eux; \
echo "Verifying install ..."; \
echo "java --version"; java --version; \
echo "file.encoding:"; java -cp / CheckEncoding | grep -q 'UTF-8'; \
rm -f /CheckEncoding.class; \
echo "Complete.";
将Dockerfile上传到仓库,并建立云效流水线
由于需要下载JDK、系统镜像、软件,建议选择云效中国香港构建集群
运行流水线并拉取镜像
查看镜像大小,可以发现镜像解压后只有 99MB
shell
[root@localhost ~]# docker image ls registry.cn-shanghai.aliyuncs.com/xxx/alpine
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.cn-shanghai.aliyuncs.com/xxx/alpine 3.18-temurin-jre17 f0958c9ca363 8 hours ago 97.7MB
registry.cn-shanghai.aliyuncs.com/xxx/alpine 3.18-corretto-jre17 cf9a27c2bfee 8 hours ago 99.2MB
而 eclipse-temurin 提供的 alpine jre 大小为
shell
[root@localhost ~]# docker image ls eclipse-temurin
REPOSITORY TAG IMAGE ID CREATED SIZE
eclipse-temurin 17-jre-alpine 2d5a7235045b 4 weeks ago 177MB