资讯动态

Docker封装Git+Maven+JDK8环境,实现Spring Boot一键自动化部署

发布时间:2026/10/15 13:05:22 来源:尧图企业网站定制
在服务器上折腾 Java 部署环境绝对是我见过最能消磨耐心的活儿之一。之前帮某后端团队搭上线流程每次新同事入职光是把 JDK8、Git、Maven 私有仓库配置、本地依赖缓存这些凑齐就得小半天。更头疼的是版本冲突有人用 JDK11有人用 1.8构建出来的行为还不一样。后来我直接换了个思路把 Git Maven JDK8 整套工具链塞进一个 CentOS7 的 Docker 镜像里再配一套自动化部署脚本Spring Boot 项目从拉取代码、Maven 打包到启动服务一条命令全搞定。镜像在哪台机器上跑环境就完全一样根本不存在“在我这是好的啊”这种问题。这套方案适合谁后端开发、运维、测试都适用尤其是小团队还没有正式 CI 基础设施又不想引入太重的东西用 Docker 封装“构建环境”是性价比极高的中间态。今天把这套做法的完整过程和踩坑实录都写出来包括 Dockerfile 怎么写、轻量级体现在哪、部署脚本怎么设计以及那些文档里绝对不会写的问题。1. 这套“环境镜像”方案到底解决什么问题1.1 传统环境部署的痛点先把最原始的方案拿出来对比你才知道为什么我要绕一圈用 Docker。假设你在宿主机上直接装 Git、Maven、JDK8流程一般是下载 JDK 的 tar 包、解压到/usr/local、配JAVA_HOME、再把/etc/profile改一遍然后下载 Maven、配MAVEN_HOME、改settings.xml指向私有仓库接着装 Git还要处理 SSH key。问题在于这几个环境变量是全局的一个机器上如果跑多个项目有的用 JDK8有的用 JDK17你改一次 profile 就要命中一次其他项目。而且每换一台云服务器这些步骤再完整重来一遍。我见过某台服务器上残留了三四个版本 Java 目录which java指到的和echo $JAVA_HOME完全对不上排查起来极其痛苦。Docker 方案的本质是把“环境”这个最不稳定的变量从宿主机上剥离出来变成镜像内容的一部分。你构建镜像的时候把 JDK8、Git、Maven 装好运行的时候容器天然继承这些配置。哪个环境不是一套镜像集群里有五台节点就起五个容器每台保证一致比挨个装机可靠得多。1.2 轻量级镜像的三个硬指标标题里提到了“轻量级”这个概念不能停留在嘴上得有硬指标可衡量。我自己衡量一个构建环境镜像有三个标准体积尽量小、依赖层尽量少、启动速度尽量快。体积这块centos:7基础镜像大约 200MB加上 OpenJDK8 的 tar 包和解压后的目录、Maven 完整版包含所有插件和自带仓库缓存以及 Git 相关依赖最终体积会膨胀到 500MB 往上。500MB 看起来不小但对于构建环境来说是完全可以接受的因为它是复用的一个镜像构建好后反复使用不会影响部署产物的大小。真正的轻量级优化目标是把 JRE 精简掉、把 Maven 的发行包瘦身、把 yum 留下的缓存清干净这些后面专门讲。启动速度取决于镜像层数和入口命令。层数太多每次拉取会慢命令太重比如启动时做复杂初始化也会拖慢。我们的场景里镜像不太需要频繁启停但还是要注意分层策略比如把环境变量放在最后一层这样后续改配置不会重新跑整个安装流程。2. 手写 Dockerfile构建 GitMavenJDK8 的 CentOS7 基础镜像2.1 版本搭配与下载路径的关键取舍写 Dockerfile 之前先定版本这步不能凭感觉来几个关键组合要理清楚JDK 方面必须是 8具体小版本建议选一个稳定版比如jdk8u202左右的版本根据你的实际场景选择合法可用的镜像地址。为什么要锁小版本老项目的依赖扫描或混淆插件可能对不同小版本表现不一样锁死才能复现。Maven 版本要特别注意和 JDK8 的兼容性。Maven 3.8.x 大多数版本要求 Java 7 以上能兼容 JDK8但如果你选了 Maven 4它会要求 Java 17和 JDK8 直接冲突。所以这里稳妥的选择是 3.6.3 或 3.8.8这两个我都实测过JDK8 环境跑mvn clean package完全没问题。Git 的版本其实不需要太纠结用 CentOS7 的 yum 源默认版本即可通常在 1.8 左右。对自动化部署来说git clone、git checkout、git pull这些基础操作完全够用。唯一要注意的是有些老版本 Git 对 HTTPS 证书校验比较严格如果私有仓库用的是自签证书后面可能要单独配置。下载路径是另一个容易踩坑的地方。Oracle 官方 JDK 的下载地址经常变动建议用公开的归档地址或云厂商的镜像源。Maven 的官方压缩包在 Apache 镜像站上如果服务器在国外可能慢可以在 Dockerfile 里把下载源替换成国内镜像站构建速度差别非常明显。2.2 Dockerfile 逐段解析与完整配置先给出完整配置然后逐段说为什么这么写# 构建环境镜像git maven jdk8 on centos7 FROM centos:7 # 设置基础环境变量后续配置和运行阶段都会生效 ENV JAVA_HOME/usr/local/jdk1.8.0_202 \ MAVEN_HOME/usr/local/maven-3.8.8 \ PATH/usr/local/jdk1.8.0_202/bin:/usr/local/maven-3.8.8/bin:/usr/local/git/bin:$PATH \ LANGen_US.UTF-8 \ TZAsia/Shanghai # 安装基础依赖 RUN yum install -y wget tar git \ yum clean all rm -rf /var/cache/yum # 安装 JDK8解压即用无需额外依赖 RUN wget -q --no-check-certificate -O /tmp/jdk8.tar.gz \ https://xxx/jdk-8u202-linux-x64.tar.gz \ tar -zxf /tmp/jdk8.tar.gz -C /usr/local \ rm -rf /tmp/jdk8.tar.gz /usr/local/jdk1.8.0_202/*src.zip # 安装 Maven配置阿里云镜像加速 RUN wget -q -O /tmp/maven.tar.gz \ https://xxx/apache-maven-3.8.8-bin.tar.gz \ tar -zxf /tmp/maven.tar.gz -C /usr/local \ mv /usr/local/apache-maven-3.8.8 /usr/local/maven-3.8.8 \ rm -rf /tmp/maven.tar.gz \ mkdir -p /root/.m2/repository # 写入 Maven 阿里云镜像配置和 JDK8 编译参数 RUN mkdir -p /root/.m2 \ echo settings xmlnshttp://maven.apache.org/SETTINGS/1.0.0 ... /root/.m2/settings.xml # 工作目录 WORKDIR /data/code CMD [/bin/bash]几个关键点展开说第一把JAVA_HOME、MAVEN_HOME这些变量用ENV而不是写进/etc/profile是因为容器运行时没有执行 profile 文件的过程ENV会直接注入到容器的环境变量里docker run的时候就能直接拿到。第二为什么顺序是 wget、tar、git 一次装完再yum clean all这样能把 yum 留下的 cache 清理干净让每一条 RUN 的层体积尽量小。如果你把 yum 和 clean 分成两条 RUN中间会产生一个包含缓存文件的层镜像体积白白多出几十 MB。第三JDK8 在容器里有个容易被忽略的问题时区和语言。默认的 centos:7 时区是 UTC容器日志时间和宿主机相差八小时排查线上问题非常误导。我建议直接在 ENV 里设置TZAsia/Shanghai不过严格来说还需要系统安装 tzdata 包才能让这个时区生效最简单的方式是yum install -y tzdata后设置。同理LANG影响 Java 打日志时的编码不设置的话某些输出会变成纯英文或者乱码。第四Maven 的settings.xml值得单独说。如果你只配置了镜像源没有配 JDK 编译参数某些老项目会有 Java 版本不一致的问题但设置maven.compiler.source/target1.8这种参数通常写在项目的pom.xml里所以镜像里只要把镜像源配好就行。我在实际项目里不仅配了阿里云镜像还加了一个私服仓库的 profile这样公司内部依赖也能直接从私服拉。2.3 构建、验证与镜像体积控制Dockerfile 写完后构建镜像docker build -t build-env:jdk8-2024.03 .构建过程如果镜像源没问题一般两分钟以内能完成。之后必须做一次环境验证不能直接拿去做自动化部署。验证清单和命令我放在下面# 查看环境变量是否完整 docker run --rm build-env:jdk8-2024.03 env | grep -E JAVA|MAVEN|PATH # 验证 Java 版本 docker run --rm build-env:jdk8-2024.03 java -version # 验证 Maven 版本及配置 docker run --rm build-env:jdk8-2024.03 mvn -v # 验证 Git 可用性 docker run --rm build-env:jdk8-2024.03 git --version # 查看镜像体积 docker images build-env # 查看历史层数 docker history build-env:jdk8-2024.03这几个验证命令跑完基本能确认这个构建镜像可用了。镜像体积方面这个基础镜像常见结果是 400MB~600MB具体取决于 JDK 解压后的体积和 Maven 发行包的大小。如果你觉得偏大可以尝试砍掉不必要的依赖包比如wget构建完就可以卸载但注意如果后面要继续用它下载依赖就不能删这个需要结合你的流程权衡。注意构建完成后的镜像 tag 不要习惯性地写latest。我的习惯是加上日期和 JDK 版本号比如build-env:jdk8-2024.03这样更新环境后旧镜像还能回滚不会一改配置所有部署行为全变。3. Spring Boot 自动化部署脚本一条命令从拉码到启动3.1 部署流程设计与容器运行方式的配合用这个构建环境镜像做自动化部署不是说你还要跑到容器里面敲命令而是把容器当成一个“标准构建环境”把代码和脚本通过宿主机目录挂载进去在容器内部执行一次完整的部署流程。整个流程设计成五个阶段拉取代码 - 切换到目标分支 - Maven 构建 - 停止旧进程 - 启动新进程。Docker 的核心特征在这里体现为代码和编译结果放在挂载卷里镜像本身不保存任何项目数据。这样做的好处是镜像动一次可以服务于 N 个项目只需要在启动容器时把不同项目的工作目录挂载进去环境部分不用做任何改动。这也决定了我们的容器是“用完即走”型的。每个项目部署时临时启动一个容器配好挂载卷跑完脚本自动退出。宿主机的部署脚本负责处理容器生命周期核心逻辑集中在 deploy.sh 里面。3.2 完整的 deploy.sh 与宿主机一键脚本先看容器内执行的主脚本。这里的设计思路是让脚本接收环境变量来控制行为仓库地址、分支、应用名全部通过-e参数从宿主机传进来而不是写死在脚本里#!/bin/bash set -e # 环境变量在容器中传入 APP_NAME${APP_NAME:-myapp} BRANCH${BRANCH:-main} REPO_URL${REPO_URL:-gitmygit:group/myapp.git} BASE_DIR/data/code/$APP_NAME LOG_DIR/data/logs/$APP_NAME JAR_NAME$APP_NAME.jar # 检查必要参数 if [ -z $APP_NAME ] || [ -z $REPO_URL ]; then echo APP_NAME and REPO_URL are required exit 1 fi # 拉取最新代码 if [ -d $BASE_DIR/.git ]; then echo git pull: $APP_NAME cd $BASE_DIR git checkout $BRANCH git pull origin $BRANCH else echo git clone: $REPO_URL git clone -b $BRANCH $REPO_URL $BASE_DIR fi # Maven 构建跳过测试加快速度 echo maven build: $APP_NAME cd $BASE_DIR mvn clean package -DskipTests -q # 准备运行产物 mkdir -p $LOG_DIR JAR_FILE$BASE_DIR/target/$JAR_NAME if [ ! -f $JAR_FILE ]; then echo ERROR: jar file not found at $JAR_FILE exit 1 fi cp $JAR_FILE $LOG_DIR/$JAR_NAME # 关闭旧进程 OLD_PID$(pgrep -f java -jar $LOG_DIR/$JAR_NAME || true) if [ -n $OLD_PID ]; then echo stop old process: $OLD_PID kill -15 $OLD_PID # 最多等待 30 秒 for i in $(seq 1 30); do if kill -0 $OLD_PID 2/dev/null; then sleep 1 else break fi done fi # 启动新进程 echo start: $APP_NAME nohup java -Xms256m -Xmx512m -jar $LOG_DIR/$JAR_NAME \ --server.port8080 $LOG_DIR/app.log 21 # 输出整个部署结果 echo deploy finished, pid: $!这个脚本有几个关键设计set -e保证任何一步出错立即退出不会把错误环境继续跑下去。容器的特性决定了你不需要像传统服务那样常驻只要脚本执行完应用就在后台跑起来了容器退出也不会杀掉nohup启动的进程吗这里要特别提醒容器退出时所有在该容器内以 nohup 方式启动的后台进程会随容器一起结束。所以你不能把应用进程直接放容器里进程 ID 1否则脚本执行完容器退出服务也没了。整个部署脚本必须由宿主机通过某种方式触发应用进程要在宿主机或真正的运行容器里启动。这是一个极其容易踩的坑。正确的做法是部署容器脚本只负责“构建 复制 jar”启动 jar 的阶段放在宿主机侧由宿主机执行一个启动命令。或者更规范一点构建容器和运行容器分开构建容器出 jar运行容器负责跑服务。下面这是宿主机侧的一键触发脚本#!/bin/bash # deploy-on-host.sh APP_NAME$1 BRANCH$2 # 构建并准备好 jar docker run --rm \ -e APP_NAME$APP_NAME \ -e BRANCH$BRANCH \ -v /opt/code:/data/code \ -v /opt/logs:/data/logs \ build-env:jdk8-2024.03 \ /bin/bash /data/deploy.sh # 在宿主机上直接启动 JAR_PATH/opt/logs/$APP_NAME/$APP_NAME.jar if [ -f $JAR_PATH ]; then PID$(pgrep -f java -jar $JAR_PATH || true) if [ -n $PID ]; then kill -15 $PID sleep 10 fi nohup java -Xms256m -Xmx512m -jar $JAR_PATH /opt/logs/$APP_NAME/app.log 21 echo $! /opt/logs/$APP_NAME/app.pid fi这种“构建在容器、运行在宿主机”的方式对单机部署最友好。如果你确实想要应用也跑在容器里那就要把运行容器搞成常驻的并且镜像里单独包含 JDK8 运行时和 Spring Boot 的 jar部署过程变成“构建容器出 jar 文件后, docker build 出运行时镜像docker run 更新容器”这个方案放在多阶段构建那一节再展开。3.3 端口、日志、进程管理的经验细节部署脚本中最容易被忽视的其实是进程管理。进程启动最忌讳直接kill -9。Spring Boot 应用关闭时需要执行优雅停机逻辑比如等待数据库连接池释放、把消息队列里的消息处理完kill -15才是正确姿势。脚本里我用kill -15之后循环kill -0等待退出最多等 30 秒这是经过实测比较可靠的时间窗口。端口占用是一个高频报错。脚本里虽然没显式处理server.port冲突但你在实际部署时如果发现BindException: Address already in use可以先查端口ss -lntp | grep 8080通常的原因是上一次部署的进程没杀干净或者日志里显示的进程 PID 已经不存在但端口还被 TIME_WAIT 状态占用。TIME_WAIT 状态的端口一般不会影响新进程绑定真正麻烦的是僵死进程排查时用ps -ef | grep java结合pgrep -f一起看同时检查 PID 文件里的记录。日志处理方面我的建议是用两个文件标准输出和错误输出都重定向到同一个app.log另外把应用的日志框架logback/log4j单独配置到另一个目录。容器环境里日志卷要提前规划好比如挂载/opt/logs到宿主机这样日志能保留方便tail -f盯启动过程。4. 镜像瘦身与多阶段构建把“轻量级”做到极致4.1 先看体积都花在哪里如果镜像构建完你发现体积到了 900MB不用慌先看体积构成。我之前构建过一版结果让我判断一下问题在哪。首先是centos:7占 201MBOpenJDK8 解压后约 400MBMaven 3.8.8 全量发行包解压后约 160MBGit 系列依赖来来回回差不多 100MB。这几项加在一起就到 800MB 了。从体积构成能看出来真正占大头的是 JDK 的完整包——里面包含了大量 Java 编译器、源码 jar、文档但你部署时根本用不到这些。Maven 的发行包同样带了很多用不到的插件。瘦身方向有三个用更小的基础镜像、精简 JDK、去掉不必要的组件。4.2 多阶段构建构建环境与运行镜像分离说到“轻量级”其实要区分两种镜像构建环境镜像和运行环境镜像。如果你只想要一个“能用、够轻量”的部署方案可以用多阶段构建把两者分开。构建阶段用我们前面做好的完整镜像来执行 Maven 编译运行阶段只是从上一阶段把 final jar COPY 到一个精简镜像里。精简运行镜像只需要一个 JRE 或精简后的 JDK不需要 Maven、Git。这样最终交付给线上运行的镜像可能只有 200~300MB比起完整构建环境小得多。来看一个多阶段构建的 Dockerfile 示例# 构建阶段复用前面做好的完整环境镜像 FROM build-env:jdk8-2024.03 AS builder WORKDIR /build COPY . . RUN mvn clean package -DskipTests -q # 运行阶段只用精简 JRE FROM eclipse-temurin:8-jre-jammy WORKDIR /app COPY --frombuilder /build/target/myapp.jar ./myapp.jar EXPOSE 8080 ENTRYPOINT [java, -Xms256m, -Xmx512m, -jar, myapp.jar]这样构建出来的运行镜像能做到 200MB 以内因为上面的基础镜像只有精简 JRE没有 Maven、没有 Git。不过需要注意如果基础镜像换成非 CentOS 系底层 glibc 版本变化可能导致某些依赖 JDK 老版本的库在运行时链接报错这是我在模拟项目里实测过的教训。所以运行镜像尽量选择和构建环境相近的基础或者直接用 JDK8 官方镜像变体兼容性最好。多阶段构建适合的场景是你每次发布单个 Spring Boot 应用想要生成一个干净小体积的最终镜像。如果只是多个项目拼在同一台服务器构建环境镜像 宿主机脚本就够了没必要每项目都搞一个运行镜像。还有个折中方案镜像里不装完整 JDK而是用jlink按需生成精简 JRE但 jlink 裁剪的模块如果配置不到位启动时可能报一些奇怪的 NoClassDefFoundError对不熟悉模块系统的团队不友好我不建议在初期阶段就上这个技术。4.3 资源限制与启动效率调优除了体积运行容器时的资源限制也直接关系“轻量级”体验。宿主机上跑多个应用容器时如果不限制内存某次 Full GC 导致内存飙高可能把整台机器拖垮。我的经验是每个 Spring Boot 容器都加上资源限制docker run -d \ --name myapp-container \ --memory512m \ --cpus1.5 \ -p 8080:8080 \ -v /opt/logs/myapp:/logs \ myapp-runner:latest--memory限制的是容器可用的最大内存一般 Spring Boot 服务给 512MB 到 1GB 就够关键看你的 JVM 参数。--cpus限制的是 CPU 核数对多实例部署很有用避免单个服务把整机计算资源占完。启动效率方面除了镜像分层、拉取速度JVM 启动性能往往被忽略。老旧的 JDK8 服务启动可能要十几秒这个瓶颈更多在应用本身可以先关注 Spring Boot 是否有包扫描过慢的问题。容器层面的--restartunless-stopped是一个好习惯如果你真的用运行容器跑服务加上它会让容器在后台 OOM 时自动拉起避免故障时间过长。5. 高频坑位排查Maven、Git、JDK8 与容器的那些事5.1 Maven 依赖拉不下来、拉得慢这是自动化部署里出镜率最高的问题。依赖拉不下来通常是两个原因网络不通和仓库源不可用。网络不通容易理解。CentOS7 容器默认没有 DNS 配置问题的话一般直接用宿主机 DNS但如果你是自定义网络偶尔会出现解析不到外网域名的情况可以在启动容器时加--dns 8.8.8.8。不过在中国的服务器上用阿里云的 DNS 通常更快一点。仓库源的问题更隐蔽。阿里云镜像解决了大部分中央仓库依赖中央仓库本身在海外访问很慢。但如果你当前环境用的是整套私有制品库而容器里没有做对应的 settings.xml 配置Maven 会尝试去中央仓库找内部依赖自然会报错。我在构建镜像阶段的 settings.xml 里同时保留了中央仓库和私服仓库的 profile并用 mirrorOf 指到阿里云镜像内部依赖走私服中央依赖走镜像运行下来非常稳。Maven 的本地仓库缓存也是需要提前规划好的。默认容器每次启动都是全新/root/.m2第一次构建所有依赖全部重新下载一套稍大的 Spring Boot 项目几百个依赖每次都拉一遍非常浪费时间。正确做法是加一个卷挂载让本地仓库缓存持久化在宿主机上docker run --rm \ -v /opt/maven-repo:/root/.m2/repository \ -v /opt/code:/data/code \ build-env:jdk8-2024.03 \ /bin/bash /data/deploy.sh第一次构建慢一点没关系第二次开始所有依赖命中本地缓存整个 Maven 构建能快非常多。5.2 Git 连接私有仓库与 SSH 密钥处理自动化部署必然涉及从 Git 服务上拉代码。如果你的仓库走的是 HTTPS用户名密码可以放到 URL 里但会暴露在 Docker 环境变量里不够安全。如果走 SSH就要解决容器里的 SSH key 问题。我尝试过把宿主机开发同事的私钥直接 COPY 进容器后来发现每次更新镜像都要重新注入而且私钥一旦泄漏还牵连所有人极其危险。更安全的做法是单独生成一对“部署专用密钥”公钥添加到 Git 仓库的 Deploy Key 里私钥通过运行时挂载进容器docker run --rm \ -v /opt/deploy-keys:/root/.ssh \ -e GIT_SSH_COMMANDssh -i /root/.ssh/deploy_key -o StrictHostKeyCheckingno \ build-env:jdk8-2024.03 \ /bin/bash /data/deploy.shGIT_SSH_COMMAND用来指定私钥路径并关闭首次连接时的主机指纹确认很多自动化脚本死在Host key verification failed就是因为少了后面这个参数。配置完成后可以先跑一次docker run --rm build-env git ls-remote gitgit:mm/xx.git验证连接是否通再走完整部署流程。还有一种情况是 Git 仓库域名走内网 IP容器里 DNS 解析不到。这时候可以在 docker run 时挂载一个--add-host比如--add-host git.intranet:192.168.x.x保证容器内能访问到仓库地址。5.3 JDK8、中文字体与时区问题JDK8 容器化部署最容易出现的经典问题和中文字体相关。如果你的 Spring Boot 项目有验证码、导出 PDF、绘图报表之类功能Java 需要加载字体来渲染中文简单基础镜像里通常没有任何中文字体。解决办法也不能算复杂在构建镜像时安装字体包RUN yum install -y fontconfig dejavu-sans-fonts \ yum clean alldejavu-sans-fonts对中文支持不差很多项目测试下来能正常显示。但如果你的项目要求特定中文字体比如黑体、宋体就需要把字体文件复制到容器的字体目录并刷新缓存这个后续单独拉出一篇讲都不为过。时区问题前面说过镜像里要设置 Asia/Shanghai但容器里光设置TZ环境变量对 Java 8 不一定生效因为 JDK8 默认还是会去系统读取/etc/localtime。稳妥的方式是构建时执行RUN yum install -y tzdata \ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime这样 Java 程序new Date()打印出来的时间才和你的业务时间对得上。别小看这个曾经有项目白天部署完日志时间比实际时间晚八个小时半夜排查问题的时候差点误导判断方向。5.4 容器重启策略与 Spring Boot 的优雅关停如果你采用“运行容器”来承载服务容器的重启策略要好好选。--restartalways和--restartunless-stopped是有区别的前者只要容器停了就重启哪怕是你手动docker stop之后也会立刻拉起来很烦人后者只会在容器意外退出时自动重启手动停止的不会。Spring Boot 应用在容器里被docker stop时Docker 默认会等待 10 秒然后强制 SIGKILL。如果应用正常优雅停机需要超过 10 秒比如正在处理长事务或者等待外部接口响应会直接被干掉导致未完成的任务数据不一致。这个岗位问题在有状态服务里尤其突出。解决办法是给docker stop加一个宽限时间或者在运行容器的启动命令里做信号转发docker stop -t 30 myapp-container-t 30表示最多等 30 秒如果 30 秒内应用进程收到 SIGTERM 后自己退出就成功超时才强杀。实测大多数 Spring Boot 应用 10~15 秒内能完成优雅停机业务负载极端的项目可能需要 30 秒尤其含大数据量导出任务时。我在实际部署里还养成了一个坏习惯排查法如果docker stop卡着半天不结束先看是不是容器主进程变成了子进程比如你用 bash 直接带着 java 启动但没做 PID 1 信号处理。Docker 只给 PID 1 发信号如果 PID 1 是 bash 不是 java信号根本传不到 Java 进程最终只能强杀。容器的启动命令建议直接用真实 Java 作为主进程不要夹在 bash 里# 错误示例 ENTRYPOINT [/bin/bash, -c, java -jar myapp.jar] # 正确示例 ENTRYPOINT [java, -Xms256m, -Xmx512m, -jar, myapp.jar]这个问题在 JDK8 容器时代及其常见只有踩过一次才真正长记性。Spring Boot 官方推荐用exec形式启动 Java 进程正是为了确保信号能正确传达。最后的经验总结做这套“Git Maven JDK8 的 CentOS7 环境镜像 Spring Boot 自动化部署”已经将近一年最大的感受是团队协作效率提升非常明显。新同事入职不再需要花半天配环境拉一个镜像起来就能干活压测、预发、生产环境完全一致A 同学在本地遇到的问题线上基本不会因为环境的差异再出现第二遍。自动化部署过程中真正实现了一条命令完成“拉码 构建 重启”上线窗口从原来的十几分钟压缩到几十秒。最后分享一个我自己在实际操作中总结的小习惯不要追着 tag 用 latest。构建镜像的 tag 我一般固定成jdk8-2024.03这种既包含版本又包含构建日期后续升级 JDK 小版本或者调整 Maven 配置直接生成一个新 tag。这样即使新镜像有问题也能在旧镜像上立刻回滚不会影响正在运行的业务。项目数量越来越多之后我发现这套环境还可以作为 CI 节点的基础镜像把它接到流水线里自动化部署的覆盖范围就又上了一个台阶。希望这方案能帮你省下那些被环境折腾掉的夜晚。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑