从 JAR 包到容器化:SpringBoot 项目在 AlmaLinux 上的 Docker 全流程部署实战与深度优化
前言
在当下的企业级 Java 后端开发中,SpringBoot 已经成为绝对的主流框架,其 “约定大于配置” 的设计理念、内嵌 Web 容器的特性,让开发人员可以快速搭建可独立运行的生产级应用,最终打包为单一可执行 JAR 包,极大简化了开发流程。
而 Docker 容器化技术的出现,则彻底解决了困扰行业多年的 “在我电脑上能跑,线上跑不起来” 的环境异构问题。通过将应用及其依赖、运行环境打包为标准化的 Docker 镜像,实现了 “一次构建,到处运行” 的跨平台部署能力,同时提供了强隔离、轻量级、易运维的优势,成为微服务架构的核心基础设施。
在服务器操作系统层面,随着 CentOS 停服,AlmaLinux 凭借 100% 兼容 RHEL 二进制、企业级稳定性、免费开源、长期支持的特性,成为了 CentOS 的主流替代方案,广泛应用于生产环境的服务部署。
本文将基于真实的生产级部署流程,从本地打包完成的 SpringBoot JAR 包出发,全程以 AlmaLinux 为服务器环境,详细讲解 SpringBoot 项目 Docker 容器化部署的全流程,包括 JAR 包高效传输、Dockerfile 深度编写与最佳实践、镜像构建、容器全生命周期管理、高频踩坑实录与解决方案、镜像深度瘦身与性能优化等核心内容,为 Java 开发人员提供一套可直接落地、兼顾稳定性与高效性的容器化部署方案。
本文默认读者已在 AlmaLinux 虚拟机中完成 Docker 环境的安装,全程不涉及 Docker 与 Linux 系统的基础安装步骤,仅聚焦 SpringBoot、Docker、AlmaLinux 三者结合的部署核心内容。
第一章 前置准备与核心环境说明
1.1 核心环境与版本说明
本文所有操作均基于以下稳定环境,读者可根据自身环境对应调整:
- 本地开发环境:已完成开发并打包成功的 SpringBoot 可执行 JAR 包,JDK 版本为 Microsoft OpenJDK 17.0.18,SpringBoot 版本 3.x(兼容 2.x 版本)
- 服务器环境:VMware 虚拟机中部署的 AlmaLinux 9.x 系统,已完成 Docker 引擎的安装与启动,具备 root 操作权限
- 工具环境:FinalShell(集成 SSH 终端与 SFTP 文件传输功能,是 Windows 环境下连接 Linux 服务器的首选工具之一)
- 核心部署目标:将本地 SpringBoot JAR 包传输至 AlmaLinux 服务器,在服务器内完成 Dockerfile 编写、镜像构建、容器启动全流程,实现应用的容器化部署,并解决部署过程中的各类问题,完成镜像与运行性能的优化。
1.2 技术栈组合的核心优势
- SpringBoot:开箱即用,内嵌 Tomcat/Jetty 等 Web 容器,打包后为单一可执行 JAR 包,无需额外部署 Web 容器,适配 Docker 容器化的单进程部署模型,是 Java 微服务开发的事实标准。
- Docker:通过分层镜像技术实现应用与运行环境的强绑定,彻底解决环境依赖问题;容器级别的资源隔离,避免多应用之间的相互干扰;镜像的标准化特性,让应用的部署、回滚、扩缩容变得极其简单。
- AlmaLinux:100% 二进制兼容 RHEL,继承了 RHEL 的企业级稳定性与安全性,同时完全免费开源,长期支持周期长达 10 年,是 CentOS 停服后生产环境的首选替代系统,完美适配 Docker 引擎的运行,兼容所有 RHEL 体系的运维操作。
1.3 前置网络与权限确认
在开始部署前,需完成以下前置检查,避免后续操作出现网络或权限问题:
- 网络互通确认:本地电脑与 AlmaLinux 虚拟机处于同一网段,可通过
ip addr命令在 AlmaLinux 终端获取服务器 IP 地址,本地可正常 ping 通该 IP。 - SSH 服务确认:AlmaLinux 已开启 sshd 服务,22 端口(SSH 默认端口)已在防火墙中开放,可通过 SSH 工具正常连接服务器。
- Docker 权限确认:当前登录用户具备 Docker 操作权限,可通过
docker --version命令正常输出版本信息,Docker 服务处于正常运行状态。
第二章 JAR 包从本地到 AlmaLinux 虚拟机的高效传输方案
本文采用的核心部署思路为:先将本地打包完成的 JAR 包传输至 AlmaLinux 服务器,再在服务器内完成 Dockerfile 编写、镜像构建与容器运行。该思路的核心优势在于:所有构建操作均在服务器环境完成,完全贴近生产部署流程,避免本地与服务器的环境异构问题,同时减少本地环境的依赖,操作流程更可控。
针对 JAR 包的传输,本文优先讲解基于 FinalShell 的一站式传输方案,同时补充其他常用传输方案的对比与适用场景,读者可根据自身习惯选择。
2.1 FinalShell 一站式连接与文件传输方案
FinalShell 是国内开发人员最常用的 SSH 工具之一,其核心优势在于集成了 SSH 终端与 SFTP 可视化文件传输功能,无需在多个工具之间切换,可同时完成服务器连接、文件传输、命令执行、文件编辑全流程操作,完美适配本次部署需求。
2.1.1 服务器连接配置
- 打开 FinalShell,点击左侧导航栏的「新建」按钮,选择「SSH 连接」;
- 在弹出的配置窗口中,填写核心连接信息:
- 名称:自定义连接名称,如「AlmaLinux-SpringBoot 部署」,方便后续识别;
- 主机:AlmaLinux 服务器的 IP 地址(通过
ip addr命令获取); - 端口:默认 22(SSH 服务默认端口,若有修改请填写实际端口);
- 用户名:AlmaLinux 的登录用户名(建议使用 root 用户,避免权限不足的问题);
- 密码:对应用户名的登录密码;
- 点击「确定」保存配置,双击新建的连接,即可完成 SSH 登录,同时右侧会自动打开 SFTP 可视化文件面板,左侧为本地文件目录,右侧为服务器文件目录。
2.1.2 服务器项目目录创建
为了避免文件混乱,建议在服务器内创建专属的项目目录,用于存放 JAR 包与 Dockerfile 文件。在 FinalShell 的 SSH 终端中执行以下命令:
bash
运行
# 创建专属项目目录,目录名可自定义
mkdir -p ~/springboot-demo
# 进入该目录,后续所有操作均在该目录下完成
cd ~/springboot-demo
执行完成后,在右侧 SFTP 面板中刷新目录,即可看到新建的springboot-demo目录,双击进入该目录,作为 JAR 包的目标传输目录。
2.1.3 JAR 包可视化传输
- 在 FinalShell 左侧的本地文件面板中,切换到 SpringBoot 项目的
target目录,找到打包完成的可执行 JAR 包(如hello-spring-0.0.1-SNAPSHOT.jar); - 直接选中该 JAR 包,拖拽到右侧服务器的
springboot-demo目录中,FinalShell 会自动完成文件传输,底部会显示传输进度; - 传输完成后,在 SSH 终端中执行
ls命令,若能看到 JAR 包的文件名,即说明传输成功。
2.1.4 关键注意事项
Linux 系统严格区分文件名大小写,而 Windows 系统不区分,因此传输完成后,务必确认服务器中的 JAR 包文件名与本地完全一致,包括大小写、版本号、后缀名,避免后续 Dockerfile 编写时出现文件找不到的问题。
2.2 其他传输方案对比与补充
除了 FinalShell 可视化传输外,还有两种常用的传输方案,适用于不同的使用场景,这里做详细对比与说明:
2.2.1 SCP 命令行传输方案
SCP 是 Linux 系统自带的基于 SSH 协议的文件传输命令,无需安装额外工具,适合习惯命令行操作的用户,核心命令格式如下:
bash
运行
# 命令格式:scp 本地JAR包绝对路径 服务器用户名@服务器IP:服务器目标目录
# 示例(Windows CMD/PowerShell中执行)
scp "D:\JavaProject\hello-spring\target\hello-spring-0.0.1-SNAPSHOT.jar" root@192.168.1.100:~/springboot-demo/
- 执行命令后,输入服务器登录密码,即可完成文件传输;
- 优势:无需安装额外工具,纯命令行操作,适合自动化脚本部署;
- 劣势:无可视化界面,需要准确填写文件路径,对新手不够友好。
2.2.2 VMware 共享文件夹方案
若使用 VMware 虚拟机部署 AlmaLinux,可通过 VMware 的共享文件夹功能,实现本地与虚拟机的文件实时同步,适合需要频繁修改、传输文件的场景:
- 在 VMware 中,选中对应的虚拟机,点击「虚拟机」→「设置」→「选项」→「共享文件夹」;
- 选择「总是启用」,点击「添加」,选择本地存放 JAR 包的项目目录,完成共享文件夹配置;
- 在 AlmaLinux 中,VMware 共享文件夹默认挂载在
/mnt/hgfs/共享文件夹名路径下,直接进入该路径即可访问本地的 JAR 包,无需手动传输;
- 优势:文件实时同步,无需重复传输,适合开发调试阶段频繁更新 JAR 包的场景;
- 劣势:仅适用于 VMware 虚拟机环境,物理服务器无法使用,配置相对复杂。
2.2.3 三种方案适用场景对比
表格
| 传输方案 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| FinalShell 可视化传输 | 一站式操作,可视化界面,简单易上手,支持文件编辑与命令执行 | 需要安装 FinalShell 工具 | 绝大多数新手用户、手动部署场景 |
| SCP 命令行传输 | 无需额外工具,纯命令行,支持自动化脚本 | 无可视化界面,路径要求严格 | 习惯命令行的用户、自动化部署场景 |
| VMware 共享文件夹 | 文件实时同步,无需重复传输 | 仅支持 VMware 虚拟机,配置复杂 | 本地虚拟机开发调试、频繁更新 JAR 包的场景 |
第三章 Dockerfile 的深度编写与最佳实践
Dockerfile 是构建 Docker 镜像的文本文件,包含了一条条构建镜像所需的指令和说明,Docker 引擎会通过读取 Dockerfile 中的指令,自动完成镜像的构建。对于 SpringBoot 项目来说,Dockerfile 的编写直接决定了镜像的体积、稳定性、兼容性,是容器化部署的核心环节。
本章将基于 SpringBoot 项目的特性,深度讲解 Dockerfile 的标准编写规范,逐行拆解每一条指令的作用、底层原理与最佳实践,同时解答部署过程中最常见的路径、指令选型等核心疑问。
3.1 Docker 镜像的分层原理
在讲解 Dockerfile 编写之前,必须先理解 Docker 镜像的核心底层原理 ——UnionFS 联合文件系统与分层镜像机制:Docker 镜像是由一系列只读的镜像层组成的,Dockerfile 中的每一条指令,都会生成一个新的镜像层,每一层只记录相对于上一层的修改内容。当启动容器时,Docker 会在所有只读镜像层之上,添加一个可读写的容器层,容器的所有修改都会记录在容器层中,不会影响底层的只读镜像层。
该机制带来了两个核心优势:
- 镜像复用:多个镜像可以共享底层的镜像层,极大减少了磁盘占用,比如多个 SpringBoot 镜像可以共享同一个 JDK 基础镜像层;
- 构建缓存:若 Dockerfile 中的指令没有修改,构建时会直接复用之前的缓存层,极大加快构建速度。
理解分层原理,是编写高效、精简的 Dockerfile 的核心基础,后续的所有最佳实践,都是基于该原理展开的。
3.2 SpringBoot 项目 Dockerfile 标准编写与逐行拆解
针对 SpringBoot 项目,我们先给出一个符合生产级最佳实践的标准 Dockerfile 模板,再逐行拆解每一条指令的作用、原理与编写规范:
dockerfile
# 1. 指定基础镜像:JRE 17 精简版,与本地开发JDK版本保持一致
FROM openjdk:17-jre-slim
# 2. 设置容器内的工作目录
WORKDIR /app
# 3. 复制服务器中的JAR包到容器内,并重命名为app.jar
COPY hello-spring-0.0.1-SNAPSHOT.jar /app/app.jar
# 4. 声明容器暴露的端口,与SpringBoot配置的端口保持一致
EXPOSE 8080
# 5. 设置容器启动命令,exec格式,保证Java进程优雅关闭
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
3.2.1 FROM 指令:基础镜像选型
FROM 是 Dockerfile 的第一条指令,用于指定构建镜像的基础镜像,后续所有指令都将基于该基础镜像执行。对于 SpringBoot 项目来说,基础镜像的选型直接决定了镜像的体积、兼容性、稳定性,是 Dockerfile 编写的第一个核心决策点。
核心选型原则 1:JDK vs JRE
很多新手会直接使用openjdk:17作为基础镜像,但这是一个典型的错误选型:
- JDK(Java Development Kit):Java 开发工具包,包含了 Java 编译器、调试工具、开发文档等完整的开发环境,同时包含了运行 Java 程序的 JRE,体积通常在 470MB 以上;
- JRE(Java Runtime Environment):Java 运行时环境,仅包含了运行 Java 程序所需的核心类库、Java 虚拟机等组件,无任何开发工具,体积仅为 JDK 的 1/3~1/2。
SpringBoot 项目打包完成的 JAR 包,是已经编译完成的可执行文件,运行时仅需要 JRE 环境即可,无需完整的 JDK。使用 JDK 作为基础镜像,会引入大量不需要的开发工具,导致镜像体积过大,浪费磁盘空间与传输带宽,同时增加了安全攻击面。
最佳实践:SpringBoot 项目必须使用 JRE 镜像作为基础镜像,禁止使用完整 JDK 镜像。
核心选型原则 2:基础系统镜像选型
同版本的 JRE 镜像,会基于不同的 Linux 系统发行版构建,体积与兼容性差异极大,主流的选型有以下三种:
表格
| 基础镜像类型 | 镜像示例 | 体积 | 优势 | 劣势 |
|---|---|---|---|---|
| Debian 完整版 | openjdk:17-jre | ~400MB | 兼容性最好,包含完整的系统依赖 | 体积过大,冗余内容多 |
| Debian 精简版(slim) | openjdk:17-jre-slim | ~200MB | 兼容性良好,裁剪了不必要的系统依赖,体积适中 | 体积仍有优化空间 |
| Alpine 极简版 | openjdk:17-jre-alpine | ~80MB | 体积极小,仅包含 Linux 核心组件,安全攻击面小 | 基于 musl libc 实现,极少数老旧 Java 类库可能存在兼容性问题 |
最佳实践:
- 绝大多数场景下,优先使用
openjdk:17-jre-slim镜像,兼顾兼容性与体积,适合生产环境; - 若对镜像体积有极致要求,且确认项目中的 Java 类库兼容 musl libc,可使用
openjdk:17-jre-alpine镜像; - 国内部署时,若官方镜像源出现限流、无法拉取的问题,可使用国内云厂商的镜像,如华为云 SWR 镜像仓库、阿里云 ACR 镜像仓库中的同版本镜像,或阿里开源的 Dragonwell JDK 镜像(国内访问稳定,100% 兼容 OpenJDK)。
核心选型原则 3:版本一致性
基础镜像的 JDK 大版本,必须与本地开发、编译 SpringBoot 项目的 JDK 大版本保持一致。比如本地使用 JDK 17 编译的项目,必须使用 JRE 17 的基础镜像,避免因版本差异导致的类版本不兼容、功能异常等问题。
3.2.2 WORKDIR 指令:工作目录设置
WORKDIR 指令用于在容器内创建并设置工作目录,后续所有指令(如 COPY、ENTRYPOINT 等)的执行,都会以该目录作为当前工作目录。
很多新手会省略 WORKDIR 指令,直接将文件复制到根目录,这是一个非常不好的习惯:
- 根目录会存放大量系统文件,应用文件与系统文件混杂,不利于后续的维护与问题排查;
- 所有路径都需要写完整的绝对路径,增加了编写难度,容易出现路径错误;
- 若使用相对路径,会出现路径不可控的问题,增加了运维风险。
最佳实践:
- 必须通过 WORKDIR 指令设置专属的应用工作目录,如
/app、/opt/app等,避免将文件存放在根目录或 /root 目录; - WORKDIR 指令会自动创建不存在的目录,无需提前通过 RUN mkdir 命令创建目录,减少镜像层数;
- 设置 WORKDIR 后,后续的指令可使用基于该目录的相对路径,简化编写,降低出错概率。
3.2.3 COPY 指令:文件复制与路径规则
COPY 指令是 Dockerfile 中最常用的指令之一,用于将构建上下文中的文件 / 目录,复制到容器内的指定路径。这也是新手最容易出错的指令,绝大多数的构建失败问题,都源于 COPY 指令的路径规则理解错误。
核心前提:Docker 构建上下文
在讲解 COPY 指令之前,必须先理解Docker 构建上下文的概念,这是理解 COPY 路径规则的核心:当我们执行docker build -t hello-spring:1.0 .命令时,命令最后的英文句号.,就是指定的构建上下文目录。Docker 在执行构建时,会将该目录下的所有文件、子目录打包,传输给 Docker 引擎,后续的 COPY 指令,只能访问该构建上下文内的文件,绝对无法访问构建上下文之外的宿主机文件。
这就是新手最常见的错误:在 COPY 指令中填写宿主机的绝对路径,比如COPY /root/springboot-demo/hello-spring.jar /app/,构建时会直接报错no such file or directory。因为 Docker 引擎只会在构建上下文目录中查找/root/springboot-demo/hello-spring.jar,也就是查找构建上下文目录/root/springboot-demo/hello-spring.jar,这个路径显然不存在。
COPY 指令的格式与路径规则
COPY 指令的标准格式为:COPY 源路径 目标路径
-
源路径规则:
- 必须是构建上下文内的相对路径,不能是宿主机的绝对路径;
- 若源路径是单个文件,直接填写文件名即可(前提是文件在构建上下文根目录);
- 支持通配符匹配,比如
COPY *.jar /app/,复制构建上下文中所有的 jar 包; - 若源路径是目录,会复制目录下的所有内容,而不是目录本身。
-
目标路径规则:
- 必须是容器内的绝对路径,或者是基于 WORKDIR 设置的工作目录的相对路径;
- 若目标路径不存在,Docker 会自动创建该路径的所有层级目录;
- 若目标路径以
/结尾,Docker 会将其识别为目录,源文件会被复制到该目录下;若不以/结尾,会被识别为文件,源文件会被重命名为该文件名。
关于/app/app.jar的深度解释
很多新手会疑问,为什么要将 JAR 包复制到/app/app.jar,而不是直接复制原文件名?核心原因有三点:
- 简化启动命令:原 JAR 包名通常包含项目名、版本号、SNAPSHOT 后缀,名称冗长,重命名为简单的
app.jar后,启动命令无需修改,避免因版本号更新导致的启动命令修改; - 统一规范:无论原 JAR 包的名称是什么,容器内都统一为
app.jar,形成标准化的镜像结构,方便后续的运维与自动化部署; - 避免路径错误:固定的文件名,避免因文件名大小写、版本号写错导致的启动失败,降低出错概率。
COPY vs ADD:到底该用哪个?
这是 Dockerfile 编写中最经典的疑问,很多人会纠结是否用 ADD 替代 COPY,这里给出明确的结论与底层区别:
表格
| 特性 | COPY | ADD |
|---|---|---|
| 核心功能 | 仅复制本地文件 / 目录到容器内 | 复制文件 + 两个额外功能:1. 自动解压 tar 系列压缩包;2. 支持从 URL 下载文件 |
| 对 JAR 包的处理 | 原样复制,不会解压 | 原样复制,不会解压(仅对 tar/tar.gz/tgz 等格式生效,JAR 包 / ZIP 包不会被解压) |
| 语义清晰度 | 功能单一,语义清晰,无任何隐式行为 | 功能复杂,存在隐式行为,可读性差 |
| 官方推荐 | Docker 官方推荐,绝大多数场景优先使用 | 仅在需要自动解压 tar 压缩包的场景使用 |
最佳实践:
- 除非你需要自动解压 tar 系列的压缩包,否则永远使用 COPY 指令,禁止使用 ADD 指令;
- ADD 指令的解压功能仅对 tar 系列压缩包生效,对 JAR 包无效,不要试图用 ADD 指令解压 JAR 包;
- 禁止使用 ADD 指令的 URL 下载功能,会导致镜像层数增加,且无法校验文件完整性,应该用 RUN wget/curl 命令替代。
3.2.4 EXPOSE 指令:端口声明
EXPOSE 指令用于声明容器运行时监听的网络端口,与 SpringBoot 项目中配置的服务端口保持一致(默认 8080)。
这里有一个新手最容易混淆的误区:EXPOSE 指令不会自动实现宿主机与容器的端口映射,仅仅是一个声明性的指令。它的核心作用有两个:
- 给镜像的使用者提供说明,告诉用户该容器内的应用监听了哪个端口,方便用户配置端口映射;
- 配合
docker run -P(大写 P)命令,Docker 会自动将宿主机的随机端口,映射到 EXPOSE 声明的容器端口。
真正实现端口映射的,是docker run命令中的-p(小写 p)参数,我们会在第四章详细讲解。
最佳实践:
- 必须在 Dockerfile 中通过 EXPOSE 指令声明应用的监听端口,提升镜像的可读性与可维护性;
- EXPOSE 仅声明应用实际使用的端口,不要声明多余的端口,减少安全攻击面。
3.2.5 ENTRYPOINT 指令:容器启动命令
ENTRYPOINT 指令用于指定容器启动时执行的命令,对于 SpringBoot 项目来说,就是启动 Java 进程的java -jar命令。
Docker 中提供了 ENTRYPOINT 和 CMD 两个指令用于设置启动命令,这里给出 SpringBoot 项目的选型结论:优先使用 ENTRYPOINT 指令的 exec 格式,禁止使用 shell 格式,核心原因如下:
-
exec 格式 vs shell 格式:
- exec 格式(推荐):
ENTRYPOINT ["java", "-jar", "/app/app.jar"],命令以 JSON 数组的格式编写,命令的每个部分为数组的一个元素; - shell 格式(不推荐):
ENTRYPOINT java -jar /app/app.jar,直接编写 shell 命令。
- exec 格式(推荐):
-
exec 格式的核心优势:
- exec 格式会让 Java 进程成为容器的 1 号进程,能够接收系统发送的 SIGTERM 等信号,当执行
docker stop命令时,Java 进程能收到信号,执行 SpringBoot 的优雅关闭逻辑,完成资源释放、数据保存等操作,避免数据丢失; - shell 格式会先启动一个 sh shell 进程,sh 进程作为 1 号进程,再启动 Java 进程作为子进程,当执行
docker stop时,sh 进程收到信号,但不会传递给 Java 子进程,导致 Java 进程无法优雅关闭,超过超时时间后会被强制杀死,可能导致数据丢失、业务异常。
- exec 格式会让 Java 进程成为容器的 1 号进程,能够接收系统发送的 SIGTERM 等信号,当执行
-
ENTRYPOINT vs CMD:
- ENTRYPOINT 指定的命令,不会被
docker run命令的末尾参数覆盖,只会被--entrypoint参数覆盖,适合固定的启动命令; - CMD 指定的命令,会被
docker run命令的末尾参数覆盖,适合作为启动命令的默认参数。
- ENTRYPOINT 指定的命令,不会被
对于 SpringBoot 项目来说,启动命令是固定的java -jar,不需要被覆盖,因此使用 ENTRYPOINT 指令是最佳选择。
最佳实践:
- 必须使用 ENTRYPOINT 指令的 exec 格式编写启动命令,禁止使用 shell 格式;
- 若需要添加 JVM 启动参数,直接在数组中添加即可,比如
ENTRYPOINT ["java", "-Xms512m", "-Xmx512m", "-jar", "/app/app.jar"]; - 启动命令中的 JAR 包路径,必须与 COPY 指令中的目标路径完全一致,避免出现文件找不到的错误。
3.3 Dockerfile 编写的避坑指南
- 禁止在源路径中使用宿主机绝对路径:COPY 指令只能访问构建上下文内的文件,宿主机绝对路径会导致构建失败,必须使用相对路径。
- 注意文件名大小写:Linux 系统严格区分大小写,COPY 指令中的源文件名必须与服务器中的文件名完全一致,包括大小写,否则会报错找不到文件。
- 不要在根目录执行构建:若在
/根目录执行docker build,会将整个宿主机的文件系统作为构建上下文,导致构建速度极慢,甚至出现磁盘占满的问题,必须在专属的项目目录执行构建。 - 不要用 ADD 复制 JAR 包:ADD 指令不会解压 JAR 包,反而会降低 Dockerfile 的可读性,优先使用 COPY 指令。
- 不要省略 WORKDIR 指令:必须设置专属的工作目录,避免文件散落在系统根目录,降低维护难度。
- 禁止用 shell 格式编写启动命令:必须使用 exec 格式,保证 Java 进程能优雅关闭,避免业务异常。
第四章 Docker 镜像构建与容器全生命周期管理
完成 Dockerfile 的编写后,就可以执行镜像构建、容器启动的核心操作了。本章将详细讲解 Docker 镜像构建命令、容器运行命令的参数详解,以及容器全生命周期的管理命令,同时解答命令中的核心疑问。
4.1 Docker 镜像构建命令详解
4.1.1 标准构建命令
在 AlmaLinux 的项目目录(存放 JAR 包与 Dockerfile 的目录)中,执行以下标准构建命令:
bash
运行
docker build -t hello-spring:1.0 .
这是 Docker 镜像构建的核心命令,我们逐参数拆解每一部分的含义与作用:
- docker build:Docker 镜像构建的核心指令,用于读取 Dockerfile,执行其中的指令,完成镜像的构建。
- -t hello-spring:1.0:
-t参数用于给构建完成的镜像打标签(Tag),标签的格式为镜像名:版本号。- 镜像名:建议与 SpringBoot 项目名保持一致,见名知意,方便后续识别与管理;
- 版本号:建议使用语义化版本号,如 1.0、1.1,不要使用默认的
latest标签。latest标签是浮动的,每次构建都会覆盖,会导致版本不可控,无法实现精准回滚,生产环境禁止使用latest标签; - 标签的核心作用:镜像 ID 是随机生成的哈希值,无法直观识别镜像的内容与版本,标签为镜像提供了可读的名称与版本,是镜像管理、版本控制的核心。
- 最后的英文句号
.:指定 Docker 构建的上下文目录,也就是当前目录。这是新手最容易漏写的部分,漏写会直接报错docker buildx build requires 1 argument,因为 Docker 不知道从哪里读取 Dockerfile 与构建所需的文件。
4.1.2 常用可选参数
- -f 参数:指定非默认名称的 Dockerfile:若你的 Dockerfile 不是默认的
Dockerfile名称(比如多环境部署时,分为Dockerfile-dev、Dockerfile-prod),可以通过-f参数指定要使用的 Dockerfile 文件:bash
运行
docker build -t hello-spring:1.0-prod -f Dockerfile-prod . - --no-cache 参数:强制不使用构建缓存:若修改了 Dockerfile 中的指令,但构建时依然复用了之前的缓存,导致修改不生效,可以通过
--no-cache参数强制重新构建每一层镜像:bash
运行
docker build -t hello-spring:1.0 --no-cache . - --build-arg 参数:传递构建时的环境变量:若需要在构建时传递动态参数,比如 JDK 版本、环境名称,可以通过该参数传递,实现构建的动态化。
4.1.3 构建结果验证
构建命令执行完成后,若终端输出Successfully built 镜像ID、Successfully tagged hello-spring:1.0,即说明镜像构建成功。执行以下命令,查看构建完成的镜像:
bash
运行
docker images | grep hello-spring
若能看到对应的镜像名称、版本号、镜像 ID、体积信息,即说明镜像构建成功。
4.2 Docker 容器启动命令与参数详解
镜像构建完成后,就可以基于该镜像创建并启动容器,实现 SpringBoot 应用的运行。标准的容器启动命令如下:
bash
运行
docker run -d -p 8080:8080 --name spring-demo hello-spring:1.0
我们逐参数拆解每一部分的含义、作用与最佳实践,同时解答用户最关心的--name spring-demo的由来问题。
4.2.1 核心参数拆解
- docker run:Docker 容器运行的核心命令,用于基于指定的镜像,创建并启动一个容器。
- -d:后台运行容器(守护进程模式),执行命令后仅返回容器 ID,不会占用当前终端。
- 若不添加
-d参数,容器会在前台运行,终端会输出容器的日志,一旦关闭终端,容器就会停止运行; - SpringBoot 是 Web 服务,需要持续后台运行,因此必须添加
-d参数。
- 若不添加
- -p 8080:8080:端口映射参数,格式为
宿主机端口:容器端口,实现宿主机与容器的端口映射。- 容器拥有独立的网络命名空间,外部网络无法直接访问容器内的端口,必须通过宿主机的端口映射,才能实现外部对容器内应用的访问;
- 宿主机端口:可以自定义,只要是宿主机未被占用的端口即可,比如 80、8081;
- 容器端口:必须与 Dockerfile 中 EXPOSE 声明的端口、SpringBoot 配置的端口保持一致;
- 示例:若想通过宿主机的 80 端口访问应用,可修改为
-p 80:8080。
- --name spring-demo:给启动的容器指定自定义名称。
- 这里的
spring-demo是完全自定义的,没有任何强制的绑定规则,只要符合 Docker 的命名规范(仅支持字母、数字、下划线、连字符、点号,不能有空格与特殊字符,且不能与已有的容器名重复)即可; - 若不添加
--name参数,Docker 会自动给容器生成一个随机的名称(如sad_bohr、laughing_turing),无法直观识别容器的用途,后续的管理操作需要使用容器 ID,非常不方便; - 自定义名称的核心作用:为容器提供可读的标识,后续的停止、重启、删除、查看日志等所有管理操作,都可以直接使用该名称,无需记忆随机的容器 ID,极大提升运维效率。
- 命名最佳实践:建议与镜像名、项目名关联,见名知意,比如
hello-spring-app、spring-demo-v1,避免使用无意义的名称。
- 这里的
- hello-spring:1.0:容器基于的镜像名称与版本号,必须与之前构建镜像时的标签完全一致,Docker 会基于该镜像创建容器。
4.2.2 生产级常用可选参数
- --restart=always:容器重启策略,设置容器退出时(包括服务器重启、Docker 服务重启)自动重启。
- 生产环境必须添加该参数,保证应用的高可用性,避免因服务器重启、异常退出导致的服务不可用;
- 可选的重启策略:
no(默认,不自动重启)、on-failure(仅异常退出时重启)、always(无论什么原因退出,都自动重启)、unless-stopped(除非手动停止,否则自动重启)。
- -v 参数:数据卷挂载:将宿主机的目录 / 文件,挂载到容器内的目录,实现数据的持久化。
- 容器的文件系统是临时的,容器删除后,容器内的所有数据都会丢失。对于 SpringBoot 应用的日志文件、配置文件、上传的文件等需要持久化保存的数据,必须通过数据卷挂载到宿主机,保证容器删除后数据不会丢失;
- 示例:将宿主机的日志目录挂载到容器内,
-v /home/logs:/app/logs。
- -e 参数:设置环境变量:向容器内注入环境变量,常用于 SpringBoot 的配置参数注入,无需修改镜像即可切换环境配置。
- 示例:指定 SpringBoot 的运行环境为生产环境,
-e "SPRING_PROFILES_ACTIVE=prod"。
- 示例:指定 SpringBoot 的运行环境为生产环境,
- --memory/--cpus 参数:资源限制:限制容器的最大内存使用量、CPU 核心数,避免容器占用过多的宿主机资源,保证宿主机与其他容器的稳定运行。
- 示例:限制容器最大内存 1G,最多使用 2 个 CPU 核心,
--memory=1g --cpus=2。
- 示例:限制容器最大内存 1G,最多使用 2 个 CPU 核心,
4.3 容器全生命周期管理命令
容器启动后,需要通过一系列命令完成容器的状态查看、日志查看、停止、重启、删除等全生命周期管理,以下是核心常用命令:
-
查看运行中的容器:
bash
运行
docker ps输出内容包括容器 ID、镜像、启动命令、创建时间、运行状态、端口映射、容器名称,可确认容器是否正常运行。
-
查看所有容器(包括已停止的):
bash
运行
docker ps -a可查看所有容器的状态,包括已停止、创建未启动的容器,用于排查容器启动失败的问题。
-
查看容器日志:
bash
运行
# 查看容器的完整日志 docker logs spring-demo # 实时查看容器日志(常用,排查启动问题) docker logs -f spring-demo # 查看容器最后100行日志 docker logs --tail=100 spring-demo这是 SpringBoot 应用部署中最常用的命令,应用启动失败、接口异常等问题,都需要通过容器日志排查原因。
-
停止运行中的容器:
bash
运行
docker stop spring-demo执行后,会向容器发送 SIGTERM 信号,等待应用优雅关闭,超时后强制停止容器。
-
启动已停止的容器:
bash
运行
docker start spring-demo用于启动已停止的容器,无需重新创建。
-
重启容器:
bash
运行
docker restart spring-demo等价于先执行 stop,再执行 start,用于重启应用,加载新的配置。
-
删除容器:
bash
运行
# 必须先停止容器,才能删除 docker stop spring-demo docker rm spring-demo容器删除后,容器内的所有未挂载数据都会丢失,操作前请确认数据已持久化。
-
进入容器内部:
bash
运行
docker exec -it spring-demo bash进入容器的终端,可查看容器内的文件结构、配置文件、运行状态,用于排查问题。
第五章 部署过程中的高频踩坑实录与解决方案
在 SpringBoot 项目的 Docker 部署过程中,会遇到各种各样的报错问题,绝大多数问题都是新手容易踩的共性坑。本章将基于真实的部署过程,整理高频出现的报错,详细讲解每个报错的现象、原因分析、解决方案与避坑建议,读者可直接对照解决自己的问题。
5.1 报错:docker buildx build requires 1 argument
- 现象:执行 docker build 命令后,终端报错,提示需要 1 个参数,构建直接失败。
- 原因分析:docker build 命令的最后,漏写了指定构建上下文的英文句号
.,Docker 不知道从哪里读取 Dockerfile 与构建所需的文件,因此报错缺少参数。 - 解决方案:在构建命令的最后,添加英文句号
.,指定当前目录为构建上下文,正确命令如下:bash
运行
docker build -t hello-spring:1.0 . - 避坑建议:永远记住,docker build 命令必须指定构建上下文,哪怕是当前目录,也必须写
.,这是新手最容易犯的低级错误。
5.2 报错:429 Too Many Requests 拉取基础镜像失败
- 现象:执行 docker build 命令时,拉取基础镜像阶段报错,返回 429 状态码,提示请求次数过多,镜像拉取失败。
- 原因分析:使用的 Docker 镜像源触发了服务器的限流规则,尤其是 Docker Hub 官方镜像源,国内访问时经常会出现限流问题;部分第三方镜像源(如个人维护的镜像源)也会因访问量过大,出现 429 限流。
- 解决方案:
- 更换国内稳定镜像源:修改 Docker 的镜像源配置,使用国内大厂维护的稳定镜像源,避免限流问题。编辑
/etc/docker/daemon.json文件,添加以下配置:json
保存后执行以下命令,重启 Docker 服务使配置生效:{ "registry-mirrors": [ "https://hub-mirror.c.163.com", "https://mirror.baidubce.com", "https://docker.mirrors.ustc.edu.cn" ] }bash
运行
sudo systemctl daemon-reload sudo systemctl restart docker - 手动从国内镜像源拉取镜像:从国内云厂商的镜像仓库拉取对应的基础镜像,然后给镜像打标签,与 Dockerfile 中的 FROM 指令保持一致,构建时就不会再重新拉取镜像。
- 更换国内可访问的基础镜像:将 Dockerfile 中的基础镜像,更换为阿里云、华为云等国内厂商维护的镜像,避免官方镜像源的限流问题。
- 更换国内稳定镜像源:修改 Docker 的镜像源配置,使用国内大厂维护的稳定镜像源,避免限流问题。编辑
- 避坑建议:国内服务器部署 Docker 项目,必须提前配置国内稳定的镜像源,不要直接使用 Docker Hub 官方源,避免限流、网络超时等问题。
5.3 报错:lookup xxx.com on 223.5.5.5:53: no such host DNS 解析失败
- 现象:拉取镜像时,终端报错无法解析镜像源的域名,提示
no such host,镜像拉取失败。 - 原因分析:AlmaLinux 系统的 DNS 服务器配置异常,无法正常解析镜像源的域名,导致无法访问镜像源服务器。
- 解决方案:
- 临时修改 DNS 配置:编辑
/etc/resolv.conf文件,添加公共 DNS 服务器地址,配置如下:plaintext
保存后立即生效,无需重启服务。nameserver 223.5.5.5 nameserver 223.6.6.6 nameserver 8.8.8.8 nameserver 114.114.114.114 - 永久修改 DNS 配置:临时修改的 DNS 配置,在服务器重启后会失效,可通过修改 NetworkManager 配置实现永久生效。编辑
/etc/NetworkManager/NetworkManager.conf文件,在[main]段下添加dns=none,保存后重启 NetworkManager 服务:systemctl restart NetworkManager。 - 验证 DNS 解析:配置完成后,通过
nslookup 镜像源域名或ping 镜像源域名命令,验证域名是否能正常解析出 IP 地址。
- 临时修改 DNS 配置:编辑
- 避坑建议:服务器部署前,务必先确认 DNS 解析正常,避免后续拉取镜像、访问外部服务时出现解析失败的问题。
5.4 报错:unable to delete 镜像ID (must be forced) - image is referenced in multiple repositories
- 现象:执行
docker rmi 镜像ID命令删除镜像时,报错镜像被多个仓库引用,无法删除。 - 原因分析:同一个镜像 ID,被多个不同的镜像标签(repository:tag)引用,比如同一个 JDK 镜像,既有
openjdk:17-jre-slim标签,又有华为云镜像仓库的标签,直接删除镜像 ID 时,Docker 无法确定要删除哪个标签,因此报错。 - 解决方案:先删除多余的镜像标签,再删除镜像。比如先删除华为云的镜像标签:
bash
运行
当镜像的最后一个标签被删除后,镜像文件会被自动删除。docker rmi swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/openjdk:17 - 避坑建议:删除镜像前,先通过
docker images命令查看镜像的标签数量,先删除标签,再删除镜像 ID,不要直接通过镜像 ID 删除镜像。
5.5 报错:Conflict. The container name "/xxx" is already in use by container
- 现象:执行 docker run 命令启动容器时,报错容器名称已被使用,容器启动失败。
- 原因分析:Docker 的容器名称是全局唯一的,已经存在一个同名的容器(哪怕是已停止的容器),无法创建同名的新容器。
- 解决方案:
- 方案一:删除旧的同名容器:先停止并删除旧的同名容器,再启动新容器:
bash
运行
docker stop spring-demo docker rm spring-demo docker run -d -p 8080:8080 --name spring-demo hello-spring:1.0 - 方案二:更换新的容器名称:给新容器指定一个不同的名称,比如
spring-demo-v2,避免名称冲突。
- 方案一:删除旧的同名容器:先停止并删除旧的同名容器,再启动新容器:
- 避坑建议:容器命名要规范,不同版本、不同环境的容器,使用不同的名称,避免名称重复;更新应用时,先清理旧的容器,再启动新容器。
5.6 问题:本地浏览器无法访问 SpringBoot 应用
- 现象:容器启动成功,状态为 Up,但本地浏览器通过
服务器IP:端口访问应用时,无法打开页面,提示超时或无法访问。 - 原因分析与解决方案:这是部署中最常见的问题,通常由以下 4 个原因导致,可按顺序排查:
- 防火墙未开放端口:AlmaLinux 的 firewalld 防火墙,默认拦截了外部对端口的访问,需要开放对应的端口:
bash
运行
临时测试可直接关闭防火墙:# 永久开放8080端口 sudo firewall-cmd --add-port=8080/tcp --permanent # 重新加载防火墙配置 sudo firewall-cmd --reloadsudo systemctl stop firewalld。 - 端口映射错误或端口被占用:
- 检查 docker run 命令中的端口映射是否写反,格式必须是
宿主机端口:容器端口,不能写反; - 检查宿主机端口是否被其他程序占用,执行
netstat -tulpn | grep 8080命令,查看端口是否被占用,若被占用,更换宿主机端口。
- 检查 docker run 命令中的端口映射是否写反,格式必须是
- SpringBoot 应用启动失败:容器虽然处于运行状态,但 SpringBoot 应用可能因配置错误、依赖缺失等问题启动失败,通过
docker logs -f 容器名命令查看容器日志,排查应用启动的报错信息,修复问题后重启容器。 - 网络互通问题:确认本地电脑能正常 ping 通服务器的 IP 地址,若无法 ping 通,检查虚拟机的网络模式(建议使用桥接模式,与本地处于同一网段),确认网络配置正常。
- 防火墙未开放端口:AlmaLinux 的 firewalld 防火墙,默认拦截了外部对端口的访问,需要开放对应的端口:
- 避坑建议:容器启动后,先在服务器终端内通过
curl http://localhost:8080命令测试应用是否正常,若服务器内可正常访问,再从本地浏览器访问,可快速缩小排查范围。
第六章 SpringBoot Docker 镜像的深度瘦身与性能优化
很多新手在完成部署后,会发现构建的 SpringBoot 镜像体积非常大,动辄几百 MB,不仅占用大量磁盘空间,还会导致镜像传输、部署速度变慢,同时存在不必要的性能损耗。本章将详细讲解 SpringBoot Docker 镜像的深度瘦身方案,以及容器运行的性能优化最佳实践,实现镜像体积从几百 MB 降到几十 MB,同时提升应用的运行性能与稳定性。
6.1 镜像体积过大的核心原因分析
SpringBoot 镜像体积过大,通常由以下 4 个核心原因导致,我们的瘦身方案也将针对这些原因逐一解决:
- 基础镜像选型错误:使用了完整的 JDK 镜像,而不是精简的 JRE 镜像,引入了大量不需要的开发工具,占用了大量空间;
- 基础系统镜像冗余:使用了 Debian/Ubuntu 完整版系统镜像,包含了大量不必要的系统依赖与工具,镜像体积过大;
- 未使用多阶段构建:将源码、编译工具、依赖缓存都打包到了最终的镜像中,引入了大量运行时不需要的文件;
- 未清理构建缓存:构建过程中产生的缓存文件、临时文件,未在构建完成后清理,残留到了镜像中。
6.2 镜像瘦身的可落地方案
6.2.1 基础镜像选型优化(最直接、最高效的瘦身方案)
这是最直接、最高效的瘦身方案,无需修改项目代码,仅通过更换基础镜像,就能实现镜像体积的大幅缩减。我们在第三章已经详细讲解了基础镜像的选型,这里给出不同选型的体积对比,以及最佳实践:
表格
| 基础镜像 | 镜像体积 | 瘦身效果 | 兼容性 |
|---|---|---|---|
| openjdk:17(完整 JDK) | ~470MB | 基准线 | 最好 |
| openjdk:17-jre-slim | ~200MB | 缩减 57% | 良好 |
| openjdk:17-jre-alpine | ~80MB | 缩减 83% | 良好(极少数场景兼容问题) |
| 阿里 Dragonwell 17-jre-alpine | ~70MB | 缩减 85% | 国内访问稳定,兼容性好 |
最佳实践:
- 生产环境优先使用
openjdk:17-jre-slim,兼顾兼容性与体积,无任何兼容风险; - 若对镜像体积有极致要求,可使用
openjdk:17-jre-alpine,同时测试项目中的所有第三方类库,确认无兼容性问题; - 国内部署优先选择国内云厂商维护的镜像,避免官方镜像源的限流与网络问题。
6.2.2 Docker 多阶段分层构建(终极瘦身方案)
Docker 多阶段构建,是实现镜像极致瘦身的终极方案,其核心原理是:将镜像的构建过程与运行过程分离,第一阶段(构建阶段)使用完整的 JDK/Maven 镜像,完成项目的源码编译、依赖下载、JAR 包打包;第二阶段(运行阶段)仅使用极简的 JRE 镜像,只从第一阶段复制最终生成的可执行 JAR 包,最终的镜像中,仅包含运行应用所需的内容,没有任何编译工具、源码、依赖缓存,实现镜像体积的极致缩减。
针对 SpringBoot 项目的多阶段构建 Dockerfile 标准模板如下:
dockerfile
# 第一阶段:构建阶段,使用完整的Maven+JDK镜像,完成项目编译打包
FROM maven:3.8.8-openjdk-17 AS builder
# 设置构建阶段的工作目录
WORKDIR /app
# 复制pom.xml文件,提前下载依赖,利用Docker构建缓存
COPY pom.xml .
RUN mvn dependency:go-offline
# 复制项目源码,执行打包命令,跳过单元测试
COPY src ./src
RUN mvn clean package -DskipTests
# 第二阶段:运行阶段,使用极简的JRE镜像,仅包含运行环境
FROM openjdk:17-jre-alpine
# 设置容器工作目录
WORKDIR /app
# 从构建阶段,仅复制最终生成的可执行JAR包到运行阶段
COPY --from=builder /app/target/hello-spring-0.0.1-SNAPSHOT.jar /app/app.jar
# 声明端口与启动命令
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
该多阶段构建方案,最终生成的镜像体积仅为 80MB 左右,若配合 JRE 定制化,可进一步缩减到 20MB 以内,同时完美利用 Docker 的构建缓存,若 pom.xml 文件没有修改,不会重复下载依赖,极大加快构建速度。
6.2.3 其他进阶瘦身技巧
- SpringBoot 分层 Jar 特性:SpringBoot 2.3 + 版本支持分层 Jar 特性,将应用的依赖包、资源文件、应用代码分为不同的层,配合 Docker 的构建缓存,可进一步加快构建速度,同时减小镜像体积。
- Jlink 定制化 JRE:通过 JDK 自带的 jlink 工具,根据项目实际使用的 JDK 模块,定制化生成仅包含所需模块的 JRE,可将 JRE 的体积从几十 MB 缩减到十几 MB,实现镜像体积的极致缩减。
- 清理镜像缓存:在构建过程中,若使用 RUN 命令安装依赖,需在同一条命令中清理缓存文件,避免缓存残留到镜像层中,比如:
dockerfile
RUN apt-get update && apt-get install -y xxx && rm -rf /var/lib/apt/lists/*
6.3 SpringBoot 容器运行的性能优化
除了镜像瘦身,我们还需要对容器的运行参数、JVM 参数进行优化,提升应用的运行性能与稳定性,避免出现 OOM、资源占用过高等问题。
6.3.1 容器环境的 JVM 参数优化
Java 10 + 版本已经原生支持容器环境的资源限制,会自动识别容器的内存、CPU 限制,自动分配 JVM 堆内存,无需手动设置复杂的参数。针对容器环境,推荐的 JVM 参数优化如下:
dockerfile
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75.0", "-Xms512m", "-Xmx1g", "-jar", "/app/app.jar"]
-XX:+UseContainerSupport:开启容器环境支持,让 JVM 自动识别容器的资源限制,Java 10 + 版本默认开启;-XX:MaxRAMPercentage=75.0:设置 JVM 最大堆内存占容器内存的比例,推荐设置为 75%,预留 25% 的内存给系统与堆外内存,避免 OOM;-Xms/-Xmx:设置 JVM 的初始堆内存与最大堆内存,建议将-Xms与-Xmx设置为相同的值,避免堆内存动态扩容带来的性能损耗。
6.3.2 容器资源限制优化
生产环境必须为容器设置资源限制,避免容器占用过多的宿主机资源,导致宿主机与其他容器不稳定,推荐的启动参数如下:
bash
运行
docker run -d -p 8080:8080 --name spring-demo --memory=1g --cpus=2 --restart=always hello-spring:1.0
--memory=1g:限制容器的最大内存使用量为 1GB,若超过该限制,容器会被 OOM 杀死;--cpus=2:限制容器最多使用 2 个 CPU 核心,避免容器占用过多 CPU 资源。
6.3.3 健康检查与可用性优化
在 Dockerfile 中添加 HEALTHCHECK 健康检查指令,让 Docker 定期检查 SpringBoot 应用的健康状态,若应用不健康,自动重启容器,提升应用的可用性:
dockerfile
# 健康检查:每30秒执行一次,超时3秒,连续3次失败则标记为不健康
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD curl -f http://localhost:8080/actuator/health || exit 1
该配置需要 SpringBoot 项目集成spring-boot-starter-actuator监控依赖,暴露健康检查端点,生产环境推荐开启。
6.3.4 日志优化
SpringBoot 应用的日志,建议直接输出到控制台,而不是容器内的文件,配合 Docker 的日志驱动,实现日志的集中管理,同时避免容器内日志文件过大,导致磁盘占满。可通过 logback 配置,将日志输出到控制台,同时设置日志滚动策略,避免日志量过大。
第七章 部署结果验证与线上运维最佳实践
7.1 部署结果的多维度验证
完成容器启动后,需要通过多维度的验证,确认应用部署成功,运行状态正常,避免出现 “容器在跑,应用不可用” 的问题。
-
容器运行状态验证:执行
docker ps命令,查看容器的状态,若容器状态为Up,且端口映射正确,说明容器启动成功。 -
应用可用性验证:
- 首先在 AlmaLinux 服务器终端内,执行
curl http://localhost:8080/hello命令(替换为你的测试接口),若能正常返回接口数据,说明应用在容器内正常运行; - 然后在本地电脑的浏览器中,通过
http://服务器IP:8080/hello访问接口,若能正常返回数据,说明外部网络可正常访问应用,部署成功。
- 首先在 AlmaLinux 服务器终端内,执行
-
日志验证:执行
docker logs --tail=100 容器名命令,查看 SpringBoot 的启动日志,确认无报错信息,接口访问有正常的日志输出,应用启动完成。 -
资源占用验证:执行
docker stats 容器名命令,查看容器的 CPU、内存占用情况,确认资源占用在合理范围内,无异常的内存泄漏、CPU 占用过高等问题。
7.2 生产级线上运维最佳实践
-
严格的版本管理:生产环境禁止使用
latest标签,每个发布版本都使用明确的语义化版本号,如 1.0.0、1.1.0,每个版本对应唯一的镜像标签,方便版本追溯与回滚,避免版本不可控的问题。 -
数据持久化:对于应用的日志文件、配置文件、用户上传的文件、数据库数据等需要持久化保存的内容,必须通过 Docker 数据卷挂载到宿主机,避免容器删除后数据丢失。同时,定期对挂载的数据进行备份,保证数据安全。
-
高可用配置:生产环境必须设置
--restart=always重启策略,保证容器异常退出、服务器重启后,应用能自动恢复;对于核心业务,建议使用 Docker Compose 或 Kubernetes 实现多实例部署,避免单点故障。 -
安全加固:禁止使用 root 用户运行容器,在 Dockerfile 中创建普通用户,通过
USER指令切换到普通用户启动应用,最小化容器的权限,降低安全攻击面;同时,定期更新基础镜像,修复镜像中的安全漏洞。 -
监控与告警:对接 Prometheus+Grafana 监控体系,监控容器的 CPU、内存、磁盘、网络等资源指标,以及 SpringBoot 应用的 JVM 指标、接口响应时间、错误率等业务指标,设置对应的告警规则,出现异常时及时通知运维人员,提前发现并解决问题。
-
镜像管理:搭建私有的镜像仓库,如 Harbor,用于存储企业内部的镜像,避免使用公共镜像仓库带来的安全风险;同时,对镜像进行安全扫描,清理无用的镜像,释放磁盘空间。
总结与展望
本文从本地打包完成的 SpringBoot JAR 包出发,基于 AlmaLinux 服务器环境,完整讲解了 SpringBoot 项目 Docker 容器化部署的全流程,包括 JAR 包高效传输、Dockerfile 深度编写与最佳实践、镜像构建、容器全生命周期管理、高频踩坑实录与解决方案、镜像深度瘦身与性能优化、线上运维最佳实践等核心内容,形成了一套完整的、可直接落地的生产级部署方案。
SpringBoot+Docker+AlmaLinux 的技术组合,完美结合了 SpringBoot 的开发便捷性、Docker 的环境一致性、AlmaLinux 的企业级稳定性,是当下 Java 后端应用部署的最佳实践之一。通过容器化部署,我们彻底解决了环境异构的问题,实现了应用的快速部署、弹性扩缩容、高效运维,极大提升了开发与运维的效率。
更多推荐




所有评论(0)