一、使用Dockerfile构建镜像

# ==========================================
# 第一阶段:构建阶段
# ==========================================
FROM ubuntu:24.04 AS builder

ENV DEBIAN_FRONTEND=noninteractive

# 只安装 jlink 必须的工具,不需要时区/证书那些花里胡哨的
RUN apt-get update && \
apt-get install -y --no-install-recommends binutils tar gzip ca-certificates && \
rm -rf /var/lib/apt/lists/*

# 【这里用你原来的 JDK 包即可 (glibc版),不需要换】
COPY jdk-21_linux-x64_bin.tar.gz /tmp/openjdk.tgz
ENV JAVA_HOME=/usr/local/jdk-builder
ENV PATH=$JAVA_HOME/bin:$PATH

RUN set -eux; \
mkdir -p "$JAVA_HOME"; \
tar --extract --file /tmp/openjdk.tgz --directory "$JAVA_HOME" --strip-components 1 --no-same-owner; \
rm /tmp/openjdk.tgz; \
java --version

# 生成 JRE
RUN jlink \
--module-path $JAVA_HOME/jmods \
--add-modules ALL-MODULE-PATH \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /javaruntime

# ==========================================
# 第二阶段:运行阶段 (Ubuntu 24.04,保 PDF/图片稳定)
# ==========================================
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive
ARG TZ=Asia/Shanghai

# 安装字体库,完美支持 PDF/图片
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates p11-kit tzdata fontconfig libfreetype6 && \
rm -rf /var/lib/apt/lists/*

# 配置时区
RUN ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime && \
echo "${TZ}" > /etc/timezone && \
dpkg-reconfigure --frontend noninteractive tzdata

# 环境变量
ENV JAVA_HOME=/usr/local/openjdk-21
ENV PATH=$JAVA_HOME/bin:$PATH
ENV LANG=C.UTF-8
ENV TZ=${TZ}
ENV JAVA_TOOL_OPTIONS="-Xmx1024M -Xms256M"

# 复制 JRE
COPY --from=builder /javaruntime $JAVA_HOME

# 安全与性能优化
RUN set -eux; \
{ \
echo '#!/usr/bin/env bash'; \
echo 'set -Eeuo pipefail'; \
echo 'trust extract --overwrite --format=java-cacerts --filter=ca-anchors --purpose=server-auth "$JAVA_HOME/lib/security/cacerts"'; \
} > /etc/ca-certificates/update.d/docker-openjdk; \
chmod +x /etc/ca-certificates/update.d/docker-openjdk; \
/etc/ca-certificates/update.d/docker-openjdk; \
find "$JAVA_HOME/lib" -name '*.so' -exec dirname '{}' ';' | sort -u > /etc/ld.so.conf.d/docker-openjdk.conf; \
ldconfig; \
java -Xshare:dump; \
java --version

# 非 root 用户
RUN groupadd -r appuser -g 1001 && useradd -r -u 1001 -g appuser -d /app -s /sbin/nologin appuser
RUN mkdir -p /app && chown -R appuser:appuser /app
USER appuser
WORKDIR /app

ENTRYPOINT ["java", "-jar"]
CMD ["/app/app.jar"]
docker build -t moshangjunmowen/jdk-jre:v21.0.11 .

二、镜像使用

基础:

docker run -d  -p 8450:1024   -v $(pwd)/jun-admin-dev-3.0.0.jar:/app/app.jar   moshangjunmowen/jdk-jre:v21.0.11

若想更改时区:

docker run -d -e TZ=UTC -p 8450:1024   -v $(pwd)/jun-admin-dev-3.0.0.jar:/app/app.jar   moshangjunmowen/jdk-jre:v21.0.11

如果需要自定义端口(例如应用监听 8081,映射到宿主机 9090)

docker run -d \
-p 宿主机端口:容器内应用端口 \
-v $(pwd)/jun-admin-dev-3.0.0.jar:/app/app.jar \
moshangjunmowen/jdk-jre:v21.0.11 \
/app/app.jar --server.port=容器内应用端口

如果需要传递其他 JVM 参数或应用参数

直接在镜像名后面添加即可,例如设置 JVM 内存:

docker run -d \
-p 8450:1024 \
-v $(pwd)/jun-admin-dev-3.0.0.jar:/app/app.jar \
-e JAVA_TOOL_OPTIONS="-Xmx512m -Xms128m -XX:+UseG1GC" \
moshangjunmowen/jdk-jre:v21.0.11

三、在Dockerfile的使用

FROM moshangjunmowen/jdk-jre:v17.0.19

# 临时切换 root 创建日志目录,并授权给 appuser
USER root
RUN mkdir -p /home/logs && chown appuser:appuser /home/logs

# 恢复非 root 用户
USER appuser

ARG JAR_FILE
LABEL maintainer="moshangjunmowen@foxmail.com"

COPY --chown=appuser:appuser ${JAR_FILE} /app/app.jar

ENV JAVA_TOOL_OPTIONS="-Djava.security.egd=file:/dev/./urandom ${JAVA_TOOL_OPTIONS}"

CMD ["/app/app.jar"]

注意需要使用前,提前创建日志文件保存目录和授权

mkdir -p /www/mes/logs

sudo chown -R 1001:1001 /www/logs/jun-mes

四、额外展示Springboot+Maven项目CICD配置案例

variables:
# 网关/服务配置(保持不变)
MES_BACK_CONTAINER: "mowen-blog-admin-api"
MES_BACK_IMAGE: "mowen-blog-admin-api:v1.0.0"
MES_BACK_PORT: 8280

# Maven 本地仓库改用项目目录下的 .m2/repository,避免宿主机权限问题
MAVEN_OPTS: "-Djava.awt.headless=true -Dmaven.repo.local=${CI_PROJECT_DIR}/.m2/repository"

# 全局缓存:缓存 Maven 本地仓库,加速后续构建
cache:
key: "${CI_COMMIT_REF_SLUG}-maven"
paths:
- .m2/repository/

stages:
- build
- deploy

# ==================== 构建阶段 ====================
build-test:jdk17:
stage: build
image: moshangjunmowen/maven-jdk17:v3
script:
- echo "=============== 开始编译打包任务(test) ==============="
- mvn clean package -Dmaven.test.skip=true -P test
- mkdir -p build
- cp Dockerfile build/
- cp mowen-admin/target/mowen-admin-test-1.0.0.jar build/
artifacts:
paths:
- build/
expire_in: 1 day
tags:
- blog-test

build-prod:jdk17:
stage: build
image: moshangjunmowen/maven-jdk17:v3
script:
- echo "=============== 开始编译打包任务(prod) ==============="
- mvn clean package -Dmaven.test.skip=true -P prod
- mkdir -p build
- cp Dockerfile build/
- cp mowen-admin/target/mowen-admin-prod-1.0.0.jar build/
artifacts:
paths:
- build/
expire_in: 1 day
only:
- main
tags:
- pioneer-mes-prod

# ==================== 部署阶段 ====================
deploy-test:jdk17:
stage: deploy
image: docker:29.4.3
needs: ["build-test:jdk17"]
script:
- echo "=============== 开始部署应用(test) ==============="
- cd build
- docker ps -a | grep $MES_BACK_CONTAINER && docker stop $MES_BACK_CONTAINER && docker rm $MES_BACK_CONTAINER || echo "not exist"
- docker images | grep $MES_BACK_IMAGE && docker rmi -f $MES_BACK_IMAGE || echo "not exist"
- docker build --build-arg JAR_FILE=mowen-admin-test-1.0.0.jar -t $MES_BACK_IMAGE .
- docker network ls | grep -w jun-boot || docker network create jun-boot
- docker run -d --restart=always -p $MES_BACK_PORT:1024 --network=jun-boot --log-opt max-size=50m --log-opt max-file=3 --name $MES_BACK_CONTAINER $MES_BACK_IMAGE
tags:
- blog-test

deploy-prod:jdk17:
stage: deploy
image: docker:29.4.3
needs: ["build-prod:jdk17"]
script:
- echo "=============== 开始部署应用(prod) ==============="
- cd build
- docker ps -a | grep $MES_BACK_CONTAINER && docker stop $MES_BACK_CONTAINER && docker rm $MES_BACK_CONTAINER || echo "not exist"
- docker images | grep $MES_BACK_IMAGE && docker rmi -f $MES_BACK_IMAGE || echo "not exist"
- docker build --build-arg JAR_FILE=mowen-admin-prod-1.0.0.jar -t $MES_BACK_IMAGE .
- docker network ls | grep -w jun-boot || docker network create jun-boot
- docker run -d --restart=always -p $MES_BACK_PORT:1024 --network=jun-boot --log-opt max-size=50m --log-opt max-file=5 --name $MES_BACK_CONTAINER $MES_BACK_IMAGE
only:
- main
tags:
- pioneer-mes-prod