由于官方的openJDK17镜像近500MB,属于太大,不利于微服务镜像打包部署。
所以自定义基于alpine 3.18 和 openjdk 17 制作JRE镜像,构建Springboot项目镜像(JRE是java运行环境,不包含开发工具所以小)。
先看结论,一共测试了三种方式。

注意:只编译java SE平台的Jre环境在运行Springboot项目会有问题,精简过头也不行,少了依赖。
!!!推荐用jre17all,这是完整的JRE环境包,测试是完全没问题的。

  • jre17se 只构建了javaSE平台模块包的JRE环境的镜像
  • jre17all 构建了所有包模块的JRE环境的镜像
  • jre17 直接用adoptium下载的JRE来创建的镜像
  • openjdk 官方的镜像
1
2
3
4
5
>docker images
jre17se latest cd9e997465f6 7 seconds ago 81MB
jre17all latest 8d7173e8cdd9 8 minutes ago 113MB
jre17 latest 9c0b10d8c81d 1 hours ago 208MB
openjdk 17 5f94f53bbced 2 hours ago 471MB

选型:

  • 基础镜像apline 大小7MB
  • JDK17安装包有180MB,运行环境只需要JRE,所以这里直接从预编译OPENJDKJRE下载

第一种直接使用已有的JRE创建镜像

这里我做了测试,直接用adoptium构建好的JRE包,去做镜像的话,最后的大小居然有200MB。虽然比官方的OPENJDK的470MB要小一半。但是还不够精简。

所以后面还是自已编译构建JRE包,可以直接跳到第二种方案查看<<<


首先创建一个目录,将下载的Jre压缩包放入。如果网络访问github正常的话,也可以直接下载

1
wget https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9/OpenJDK17U-jre_x64_alpine-linux_hotspot_17.0.9_9.tar.gz

创建Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 使用Alpine 3.18作为基础镜像
FROM alpine:3.18

# 复制JDK压缩包到镜像中
COPY OpenJDK17U-jre_x64_alpine-linux_hotspot_17.0.9_9.tar.gz /tmp/openjdk.tar.gz

# 设置环境变量
ENV JAVA_HOME=/opt/jdk
ENV PATH=$JAVA_HOME/bin:$PATH

# 更新Alpine的软件源为阿里云
RUN echo "https://mirrors.aliyun.com/alpine/v3.18/main" > /etc/apk/repositories \
&& echo "https://mirrors.aliyun.com/alpine/v3.18/community" >> /etc/apk/repositories

# 安装必要的软件包,包括时区设置和字体支持
RUN apk add --no-cache fontconfig libretls ttf-dejavu tzdata \
&& rm -rf /var/cache/apk/* \
&& echo "Asia/Shanghai" > /etc/timezone \
&& ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

# 解压JDK并清理无用文件
RUN set -eux; \
mkdir -p "$JAVA_HOME"; \
tar --extract \
--file /tmp/openjdk.tar.gz \
--directory "$JAVA_HOME" \
--strip-components 1 \
--no-same-owner \
&& rm -f /tmp/openjdk.tar.gz \
&& find "$JAVA_HOME" -name '*.diz' -delete \
&& find "$JAVA_HOME" -name '*.txt' -delete \
&& rm -rf "$JAVA_HOME"/man \
&& rm -rf "$JAVA_HOME"/legal

# 清理缓存和临时文件
RUN rm -rf /var/cache/apk/* \
&& rm -rf /tmp/*

# 可选:打印Java版本信息
CMD ["sh", "-c", "echo 'Java version:' && java -version && echo 'Current date and time:' && date"]

执行build,并启动容器验证

1
docker build -t jre17 .
1
2
// 执行会打印JAVA版本信息和时间信息
docker run --rm jre17

这种方式构建的是最全的,但是感觉还是有点大,所以还是自定义吧!

第二种自己构建JRE环境包

思路:首先用JDK包构建自定义的JRE环境包,再将JRE包制作到镜像里。需要采用Docker的多阶段构建,可以减少镜像大小。
老样子 先下载对应Alpine平台的的包,放到同级目录。如下:

Dockerfile文件内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 第一阶段:使用基础镜像来下载和安装JDK
FROM alpine:3.18 as builder

# 设置 JDK并复制本地压缩包
ARG JDK_DIR=/usr/lib/jvm/java17
COPY OpenJDK17U-jdk_x64_alpine-linux_hotspot_17.0.12_7.tar.gz ./jdk.tar.gz

# 安装必要的工具,binutils 是使用 jlink 所需的工具
RUN set -eux; \
apk add --no-cache tar gzip binutils;

# 解压 JDK
RUN set -eux; \
mkdir -p ${JDK_DIR}; \
tar --extract \
--file jdk.tar.gz \
--directory ${JDK_DIR} \
--strip-components 1 \
--no-same-owner;

# 设置环境变量
ENV JAVA_HOME=${JDK_DIR}
ENV PATH=$JAVA_HOME/bin:$PATH

# 使用 jlink 创建自定义JRE --add-modules这个参数传全部模块:ALL-MODULE-PATH 或者指定只包含JAVA SE平台下的模块:java.se
RUN set -eux; \
$JAVA_HOME/bin/jlink \
--module-path ${JAVA_HOME}/jmods \
--add-modules ALL-MODULE-PATH \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /jre17;

# 第二阶段:创建最终镜像
FROM alpine:3.18

# 设置 JDK 安装目录
ARG JDK_HOME_DIR=/opt/java/jdk17

# 复制自定义 JRE 到新镜像
COPY --from=builder /jre17 ${JDK_HOME_DIR}

# 设置环境变量
ENV JAVA_HOME=${JDK_HOME_DIR}
ENV PATH=$JAVA_HOME/bin:$PATH

# 设置其它环境变量
ENV TZ='Asia/Shanghai'
ENV LANG=C.UTF-8

# 更新 Alpine 的软件源为阿里云
RUN echo "https://mirrors.aliyun.com/alpine/v3.18/main" > /etc/apk/repositories \
&& echo "https://mirrors.aliyun.com/alpine/v3.18/community" >> /etc/apk/repositories

# 安装必要的软件包,包括时区设置和字体支持
RUN apk add --no-cache fontconfig libretls ttf-dejavu tzdata musl-locales musl-locales-lang ca-certificates p11-kit-trust \
&& rm -rf /var/cache/apk/*

# 测试 Java 安装和显示当前时间 不需要可以删除这一行
CMD ["sh", "-c", "echo 'Java version:' && java -version && echo 'Current date and time:' && date"]

部署并验证

执行build,并启动容器验证

1
docker build -t jre17all .
1
2
3
4
5
// 执行会打印JAVA版本信息和时间信息
docker run --rm jre17all
// 打标签 上传到私有镜像仓库
docker tag jre17all registry.cn-hangzhou.aliyuncs.com/blog/jre17
docker push registry.cn-hangzhou.aliyuncs.com/blog/jre17

然后拿一个Springboot微服务验证一下
修改微服务Dockerfile首行的基础镜像为自定义的JRE from registry.cn-hangzhou.aliyuncs.com/blog/jre17

1
2
3
4
5
6
FROM registry.cn-hangzhou.aliyuncs.com/blog/jre17
MAINTAINER dollcode
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]