前言

在当下的企业级 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 技术栈组合的核心优势

  1. SpringBoot:开箱即用,内嵌 Tomcat/Jetty 等 Web 容器,打包后为单一可执行 JAR 包,无需额外部署 Web 容器,适配 Docker 容器化的单进程部署模型,是 Java 微服务开发的事实标准。
  2. Docker:通过分层镜像技术实现应用与运行环境的强绑定,彻底解决环境依赖问题;容器级别的资源隔离,避免多应用之间的相互干扰;镜像的标准化特性,让应用的部署、回滚、扩缩容变得极其简单。
  3. AlmaLinux:100% 二进制兼容 RHEL,继承了 RHEL 的企业级稳定性与安全性,同时完全免费开源,长期支持周期长达 10 年,是 CentOS 停服后生产环境的首选替代系统,完美适配 Docker 引擎的运行,兼容所有 RHEL 体系的运维操作。

1.3 前置网络与权限确认

在开始部署前,需完成以下前置检查,避免后续操作出现网络或权限问题:

  1. 网络互通确认:本地电脑与 AlmaLinux 虚拟机处于同一网段,可通过ip addr命令在 AlmaLinux 终端获取服务器 IP 地址,本地可正常 ping 通该 IP。
  2. SSH 服务确认:AlmaLinux 已开启 sshd 服务,22 端口(SSH 默认端口)已在防火墙中开放,可通过 SSH 工具正常连接服务器。
  3. Docker 权限确认:当前登录用户具备 Docker 操作权限,可通过docker --version命令正常输出版本信息,Docker 服务处于正常运行状态。

第二章 JAR 包从本地到 AlmaLinux 虚拟机的高效传输方案

本文采用的核心部署思路为:先将本地打包完成的 JAR 包传输至 AlmaLinux 服务器,再在服务器内完成 Dockerfile 编写、镜像构建与容器运行。该思路的核心优势在于:所有构建操作均在服务器环境完成,完全贴近生产部署流程,避免本地与服务器的环境异构问题,同时减少本地环境的依赖,操作流程更可控。

针对 JAR 包的传输,本文优先讲解基于 FinalShell 的一站式传输方案,同时补充其他常用传输方案的对比与适用场景,读者可根据自身习惯选择。

2.1 FinalShell 一站式连接与文件传输方案

FinalShell 是国内开发人员最常用的 SSH 工具之一,其核心优势在于集成了 SSH 终端与 SFTP 可视化文件传输功能,无需在多个工具之间切换,可同时完成服务器连接、文件传输、命令执行、文件编辑全流程操作,完美适配本次部署需求。

2.1.1 服务器连接配置
  1. 打开 FinalShell,点击左侧导航栏的「新建」按钮,选择「SSH 连接」;
  2. 在弹出的配置窗口中,填写核心连接信息:
    • 名称:自定义连接名称,如「AlmaLinux-SpringBoot 部署」,方便后续识别;
    • 主机:AlmaLinux 服务器的 IP 地址(通过ip addr命令获取);
    • 端口:默认 22(SSH 服务默认端口,若有修改请填写实际端口);
    • 用户名:AlmaLinux 的登录用户名(建议使用 root 用户,避免权限不足的问题);
    • 密码:对应用户名的登录密码;
  3. 点击「确定」保存配置,双击新建的连接,即可完成 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 包可视化传输
  1. 在 FinalShell 左侧的本地文件面板中,切换到 SpringBoot 项目的target目录,找到打包完成的可执行 JAR 包(如hello-spring-0.0.1-SNAPSHOT.jar);
  2. 直接选中该 JAR 包,拖拽到右侧服务器的springboot-demo目录中,FinalShell 会自动完成文件传输,底部会显示传输进度;
  3. 传输完成后,在 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 的共享文件夹功能,实现本地与虚拟机的文件实时同步,适合需要频繁修改、传输文件的场景:

  1. 在 VMware 中,选中对应的虚拟机,点击「虚拟机」→「设置」→「选项」→「共享文件夹」;
  2. 选择「总是启用」,点击「添加」,选择本地存放 JAR 包的项目目录,完成共享文件夹配置;
  3. 在 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 会在所有只读镜像层之上,添加一个可读写的容器层,容器的所有修改都会记录在容器层中,不会影响底层的只读镜像层。

该机制带来了两个核心优势:

  1. 镜像复用:多个镜像可以共享底层的镜像层,极大减少了磁盘占用,比如多个 SpringBoot 镜像可以共享同一个 JDK 基础镜像层;
  2. 构建缓存:若 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 源路径 目标路径

  1. 源路径规则

    • 必须是构建上下文内的相对路径,不能是宿主机的绝对路径;
    • 若源路径是单个文件,直接填写文件名即可(前提是文件在构建上下文根目录);
    • 支持通配符匹配,比如COPY *.jar /app/,复制构建上下文中所有的 jar 包;
    • 若源路径是目录,会复制目录下的所有内容,而不是目录本身。
  2. 目标路径规则

    • 必须是容器内的绝对路径,或者是基于 WORKDIR 设置的工作目录的相对路径;
    • 若目标路径不存在,Docker 会自动创建该路径的所有层级目录;
    • 若目标路径以/结尾,Docker 会将其识别为目录,源文件会被复制到该目录下;若不以/结尾,会被识别为文件,源文件会被重命名为该文件名。
关于/app/app.jar的深度解释

很多新手会疑问,为什么要将 JAR 包复制到/app/app.jar,而不是直接复制原文件名?核心原因有三点:

  1. 简化启动命令:原 JAR 包名通常包含项目名、版本号、SNAPSHOT 后缀,名称冗长,重命名为简单的app.jar后,启动命令无需修改,避免因版本号更新导致的启动命令修改;
  2. 统一规范:无论原 JAR 包的名称是什么,容器内都统一为app.jar,形成标准化的镜像结构,方便后续的运维与自动化部署;
  3. 避免路径错误:固定的文件名,避免因文件名大小写、版本号写错导致的启动失败,降低出错概率。
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 指令不会自动实现宿主机与容器的端口映射,仅仅是一个声明性的指令。它的核心作用有两个:

  1. 给镜像的使用者提供说明,告诉用户该容器内的应用监听了哪个端口,方便用户配置端口映射;
  2. 配合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 格式,核心原因如下:

  1. exec 格式 vs shell 格式

    • exec 格式(推荐):ENTRYPOINT ["java", "-jar", "/app/app.jar"],命令以 JSON 数组的格式编写,命令的每个部分为数组的一个元素;
    • shell 格式(不推荐):ENTRYPOINT java -jar /app/app.jar,直接编写 shell 命令。
  2. exec 格式的核心优势

    • exec 格式会让 Java 进程成为容器的 1 号进程,能够接收系统发送的 SIGTERM 等信号,当执行docker stop命令时,Java 进程能收到信号,执行 SpringBoot 的优雅关闭逻辑,完成资源释放、数据保存等操作,避免数据丢失;
    • shell 格式会先启动一个 sh shell 进程,sh 进程作为 1 号进程,再启动 Java 进程作为子进程,当执行docker stop时,sh 进程收到信号,但不会传递给 Java 子进程,导致 Java 进程无法优雅关闭,超过超时时间后会被强制杀死,可能导致数据丢失、业务异常。
  3. ENTRYPOINT vs CMD

    • ENTRYPOINT 指定的命令,不会被docker run命令的末尾参数覆盖,只会被--entrypoint参数覆盖,适合固定的启动命令;
    • CMD 指定的命令,会被docker run命令的末尾参数覆盖,适合作为启动命令的默认参数。

对于 SpringBoot 项目来说,启动命令是固定的java -jar,不需要被覆盖,因此使用 ENTRYPOINT 指令是最佳选择。

最佳实践

  • 必须使用 ENTRYPOINT 指令的 exec 格式编写启动命令,禁止使用 shell 格式;
  • 若需要添加 JVM 启动参数,直接在数组中添加即可,比如ENTRYPOINT ["java", "-Xms512m", "-Xmx512m", "-jar", "/app/app.jar"]
  • 启动命令中的 JAR 包路径,必须与 COPY 指令中的目标路径完全一致,避免出现文件找不到的错误。

3.3 Dockerfile 编写的避坑指南

  1. 禁止在源路径中使用宿主机绝对路径:COPY 指令只能访问构建上下文内的文件,宿主机绝对路径会导致构建失败,必须使用相对路径。
  2. 注意文件名大小写:Linux 系统严格区分大小写,COPY 指令中的源文件名必须与服务器中的文件名完全一致,包括大小写,否则会报错找不到文件。
  3. 不要在根目录执行构建:若在/根目录执行docker build,会将整个宿主机的文件系统作为构建上下文,导致构建速度极慢,甚至出现磁盘占满的问题,必须在专属的项目目录执行构建。
  4. 不要用 ADD 复制 JAR 包:ADD 指令不会解压 JAR 包,反而会降低 Dockerfile 的可读性,优先使用 COPY 指令。
  5. 不要省略 WORKDIR 指令:必须设置专属的工作目录,避免文件散落在系统根目录,降低维护难度。
  6. 禁止用 shell 格式编写启动命令:必须使用 exec 格式,保证 Java 进程能优雅关闭,避免业务异常。

第四章 Docker 镜像构建与容器全生命周期管理

完成 Dockerfile 的编写后,就可以执行镜像构建、容器启动的核心操作了。本章将详细讲解 Docker 镜像构建命令、容器运行命令的参数详解,以及容器全生命周期的管理命令,同时解答命令中的核心疑问。

4.1 Docker 镜像构建命令详解

4.1.1 标准构建命令

在 AlmaLinux 的项目目录(存放 JAR 包与 Dockerfile 的目录)中,执行以下标准构建命令:

bash

运行

docker build -t hello-spring:1.0 .

这是 Docker 镜像构建的核心命令,我们逐参数拆解每一部分的含义与作用:

  1. docker build:Docker 镜像构建的核心指令,用于读取 Dockerfile,执行其中的指令,完成镜像的构建。
  2. -t hello-spring:1.0-t参数用于给构建完成的镜像打标签(Tag),标签的格式为镜像名:版本号
    • 镜像名:建议与 SpringBoot 项目名保持一致,见名知意,方便后续识别与管理;
    • 版本号:建议使用语义化版本号,如 1.0、1.1,不要使用默认的latest标签。latest标签是浮动的,每次构建都会覆盖,会导致版本不可控,无法实现精准回滚,生产环境禁止使用latest标签;
    • 标签的核心作用:镜像 ID 是随机生成的哈希值,无法直观识别镜像的内容与版本,标签为镜像提供了可读的名称与版本,是镜像管理、版本控制的核心。
  3. 最后的英文句号.:指定 Docker 构建的上下文目录,也就是当前目录。这是新手最容易漏写的部分,漏写会直接报错docker buildx build requires 1 argument,因为 Docker 不知道从哪里读取 Dockerfile 与构建所需的文件。
4.1.2 常用可选参数
  1. -f 参数:指定非默认名称的 Dockerfile:若你的 Dockerfile 不是默认的Dockerfile名称(比如多环境部署时,分为Dockerfile-devDockerfile-prod),可以通过-f参数指定要使用的 Dockerfile 文件:

    bash

    运行

    docker build -t hello-spring:1.0-prod -f Dockerfile-prod .
    
  2. --no-cache 参数:强制不使用构建缓存:若修改了 Dockerfile 中的指令,但构建时依然复用了之前的缓存,导致修改不生效,可以通过--no-cache参数强制重新构建每一层镜像:

    bash

    运行

    docker build -t hello-spring:1.0 --no-cache .
    
  3. --build-arg 参数:传递构建时的环境变量:若需要在构建时传递动态参数,比如 JDK 版本、环境名称,可以通过该参数传递,实现构建的动态化。
4.1.3 构建结果验证

构建命令执行完成后,若终端输出Successfully built 镜像IDSuccessfully 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 核心参数拆解
  1. docker run:Docker 容器运行的核心命令,用于基于指定的镜像,创建并启动一个容器。
  2. -d:后台运行容器(守护进程模式),执行命令后仅返回容器 ID,不会占用当前终端。
    • 若不添加-d参数,容器会在前台运行,终端会输出容器的日志,一旦关闭终端,容器就会停止运行;
    • SpringBoot 是 Web 服务,需要持续后台运行,因此必须添加-d参数。
  3. -p 8080:8080:端口映射参数,格式为宿主机端口:容器端口,实现宿主机与容器的端口映射。
    • 容器拥有独立的网络命名空间,外部网络无法直接访问容器内的端口,必须通过宿主机的端口映射,才能实现外部对容器内应用的访问;
    • 宿主机端口:可以自定义,只要是宿主机未被占用的端口即可,比如 80、8081;
    • 容器端口:必须与 Dockerfile 中 EXPOSE 声明的端口、SpringBoot 配置的端口保持一致;
    • 示例:若想通过宿主机的 80 端口访问应用,可修改为-p 80:8080
  4. --name spring-demo:给启动的容器指定自定义名称。
    • 这里的spring-demo是完全自定义的,没有任何强制的绑定规则,只要符合 Docker 的命名规范(仅支持字母、数字、下划线、连字符、点号,不能有空格与特殊字符,且不能与已有的容器名重复)即可;
    • 若不添加--name参数,Docker 会自动给容器生成一个随机的名称(如sad_bohrlaughing_turing),无法直观识别容器的用途,后续的管理操作需要使用容器 ID,非常不方便;
    • 自定义名称的核心作用:为容器提供可读的标识,后续的停止、重启、删除、查看日志等所有管理操作,都可以直接使用该名称,无需记忆随机的容器 ID,极大提升运维效率。
    • 命名最佳实践:建议与镜像名、项目名关联,见名知意,比如hello-spring-appspring-demo-v1,避免使用无意义的名称。
  5. hello-spring:1.0:容器基于的镜像名称与版本号,必须与之前构建镜像时的标签完全一致,Docker 会基于该镜像创建容器。
4.2.2 生产级常用可选参数
  1. --restart=always:容器重启策略,设置容器退出时(包括服务器重启、Docker 服务重启)自动重启。
    • 生产环境必须添加该参数,保证应用的高可用性,避免因服务器重启、异常退出导致的服务不可用;
    • 可选的重启策略:no(默认,不自动重启)、on-failure(仅异常退出时重启)、always(无论什么原因退出,都自动重启)、unless-stopped(除非手动停止,否则自动重启)。
  2. -v 参数:数据卷挂载:将宿主机的目录 / 文件,挂载到容器内的目录,实现数据的持久化。
    • 容器的文件系统是临时的,容器删除后,容器内的所有数据都会丢失。对于 SpringBoot 应用的日志文件、配置文件、上传的文件等需要持久化保存的数据,必须通过数据卷挂载到宿主机,保证容器删除后数据不会丢失;
    • 示例:将宿主机的日志目录挂载到容器内,-v /home/logs:/app/logs
  3. -e 参数:设置环境变量:向容器内注入环境变量,常用于 SpringBoot 的配置参数注入,无需修改镜像即可切换环境配置。
    • 示例:指定 SpringBoot 的运行环境为生产环境,-e "SPRING_PROFILES_ACTIVE=prod"
  4. --memory/--cpus 参数:资源限制:限制容器的最大内存使用量、CPU 核心数,避免容器占用过多的宿主机资源,保证宿主机与其他容器的稳定运行。
    • 示例:限制容器最大内存 1G,最多使用 2 个 CPU 核心,--memory=1g --cpus=2

4.3 容器全生命周期管理命令

容器启动后,需要通过一系列命令完成容器的状态查看、日志查看、停止、重启、删除等全生命周期管理,以下是核心常用命令:

  1. 查看运行中的容器

    bash

    运行

    docker ps
    

    输出内容包括容器 ID、镜像、启动命令、创建时间、运行状态、端口映射、容器名称,可确认容器是否正常运行。

  2. 查看所有容器(包括已停止的)

    bash

    运行

    docker ps -a
    

    可查看所有容器的状态,包括已停止、创建未启动的容器,用于排查容器启动失败的问题。

  3. 查看容器日志

    bash

    运行

    # 查看容器的完整日志
    docker logs spring-demo
    # 实时查看容器日志(常用,排查启动问题)
    docker logs -f spring-demo
    # 查看容器最后100行日志
    docker logs --tail=100 spring-demo
    

    这是 SpringBoot 应用部署中最常用的命令,应用启动失败、接口异常等问题,都需要通过容器日志排查原因。

  4. 停止运行中的容器

    bash

    运行

    docker stop spring-demo
    

    执行后,会向容器发送 SIGTERM 信号,等待应用优雅关闭,超时后强制停止容器。

  5. 启动已停止的容器

    bash

    运行

    docker start spring-demo
    

    用于启动已停止的容器,无需重新创建。

  6. 重启容器

    bash

    运行

    docker restart spring-demo
    

    等价于先执行 stop,再执行 start,用于重启应用,加载新的配置。

  7. 删除容器

    bash

    运行

    # 必须先停止容器,才能删除
    docker stop spring-demo
    docker rm spring-demo
    

    容器删除后,容器内的所有未挂载数据都会丢失,操作前请确认数据已持久化。

  8. 进入容器内部

    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 限流。
  • 解决方案
    1. 更换国内稳定镜像源:修改 Docker 的镜像源配置,使用国内大厂维护的稳定镜像源,避免限流问题。编辑/etc/docker/daemon.json文件,添加以下配置:

      json

      {
        "registry-mirrors": [
          "https://hub-mirror.c.163.com",
          "https://mirror.baidubce.com",
          "https://docker.mirrors.ustc.edu.cn"
        ]
      }
      
      保存后执行以下命令,重启 Docker 服务使配置生效:

      bash

      运行

      sudo systemctl daemon-reload
      sudo systemctl restart docker
      
    2. 手动从国内镜像源拉取镜像:从国内云厂商的镜像仓库拉取对应的基础镜像,然后给镜像打标签,与 Dockerfile 中的 FROM 指令保持一致,构建时就不会再重新拉取镜像。
    3. 更换国内可访问的基础镜像:将 Dockerfile 中的基础镜像,更换为阿里云、华为云等国内厂商维护的镜像,避免官方镜像源的限流问题。
  • 避坑建议:国内服务器部署 Docker 项目,必须提前配置国内稳定的镜像源,不要直接使用 Docker Hub 官方源,避免限流、网络超时等问题。

5.3 报错:lookup xxx.com on 223.5.5.5:53: no such host DNS 解析失败

  • 现象:拉取镜像时,终端报错无法解析镜像源的域名,提示no such host,镜像拉取失败。
  • 原因分析:AlmaLinux 系统的 DNS 服务器配置异常,无法正常解析镜像源的域名,导致无法访问镜像源服务器。
  • 解决方案
    1. 临时修改 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
      
      保存后立即生效,无需重启服务。
    2. 永久修改 DNS 配置:临时修改的 DNS 配置,在服务器重启后会失效,可通过修改 NetworkManager 配置实现永久生效。编辑/etc/NetworkManager/NetworkManager.conf文件,在[main]段下添加dns=none,保存后重启 NetworkManager 服务:systemctl restart NetworkManager
    3. 验证 DNS 解析:配置完成后,通过nslookup 镜像源域名ping 镜像源域名命令,验证域名是否能正常解析出 IP 地址。
  • 避坑建议:服务器部署前,务必先确认 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 的容器名称是全局唯一的,已经存在一个同名的容器(哪怕是已停止的容器),无法创建同名的新容器。
  • 解决方案
    1. 方案一:删除旧的同名容器:先停止并删除旧的同名容器,再启动新容器:

      bash

      运行

      docker stop spring-demo
      docker rm spring-demo
      docker run -d -p 8080:8080 --name spring-demo hello-spring:1.0
      
    2. 方案二:更换新的容器名称:给新容器指定一个不同的名称,比如spring-demo-v2,避免名称冲突。
  • 避坑建议:容器命名要规范,不同版本、不同环境的容器,使用不同的名称,避免名称重复;更新应用时,先清理旧的容器,再启动新容器。

5.6 问题:本地浏览器无法访问 SpringBoot 应用

  • 现象:容器启动成功,状态为 Up,但本地浏览器通过服务器IP:端口访问应用时,无法打开页面,提示超时或无法访问。
  • 原因分析与解决方案:这是部署中最常见的问题,通常由以下 4 个原因导致,可按顺序排查:
    1. 防火墙未开放端口:AlmaLinux 的 firewalld 防火墙,默认拦截了外部对端口的访问,需要开放对应的端口:

      bash

      运行

      # 永久开放8080端口
      sudo firewall-cmd --add-port=8080/tcp --permanent
      # 重新加载防火墙配置
      sudo firewall-cmd --reload
      
      临时测试可直接关闭防火墙:sudo systemctl stop firewalld
    2. 端口映射错误或端口被占用
      • 检查 docker run 命令中的端口映射是否写反,格式必须是宿主机端口:容器端口,不能写反;
      • 检查宿主机端口是否被其他程序占用,执行netstat -tulpn | grep 8080命令,查看端口是否被占用,若被占用,更换宿主机端口。
    3. SpringBoot 应用启动失败:容器虽然处于运行状态,但 SpringBoot 应用可能因配置错误、依赖缺失等问题启动失败,通过docker logs -f 容器名命令查看容器日志,排查应用启动的报错信息,修复问题后重启容器。
    4. 网络互通问题:确认本地电脑能正常 ping 通服务器的 IP 地址,若无法 ping 通,检查虚拟机的网络模式(建议使用桥接模式,与本地处于同一网段),确认网络配置正常。
  • 避坑建议:容器启动后,先在服务器终端内通过curl http://localhost:8080命令测试应用是否正常,若服务器内可正常访问,再从本地浏览器访问,可快速缩小排查范围。

第六章 SpringBoot Docker 镜像的深度瘦身与性能优化

很多新手在完成部署后,会发现构建的 SpringBoot 镜像体积非常大,动辄几百 MB,不仅占用大量磁盘空间,还会导致镜像传输、部署速度变慢,同时存在不必要的性能损耗。本章将详细讲解 SpringBoot Docker 镜像的深度瘦身方案,以及容器运行的性能优化最佳实践,实现镜像体积从几百 MB 降到几十 MB,同时提升应用的运行性能与稳定性。

6.1 镜像体积过大的核心原因分析

SpringBoot 镜像体积过大,通常由以下 4 个核心原因导致,我们的瘦身方案也将针对这些原因逐一解决:

  1. 基础镜像选型错误:使用了完整的 JDK 镜像,而不是精简的 JRE 镜像,引入了大量不需要的开发工具,占用了大量空间;
  2. 基础系统镜像冗余:使用了 Debian/Ubuntu 完整版系统镜像,包含了大量不必要的系统依赖与工具,镜像体积过大;
  3. 未使用多阶段构建:将源码、编译工具、依赖缓存都打包到了最终的镜像中,引入了大量运行时不需要的文件;
  4. 未清理构建缓存:构建过程中产生的缓存文件、临时文件,未在构建完成后清理,残留到了镜像中。

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 其他进阶瘦身技巧
  1. SpringBoot 分层 Jar 特性:SpringBoot 2.3 + 版本支持分层 Jar 特性,将应用的依赖包、资源文件、应用代码分为不同的层,配合 Docker 的构建缓存,可进一步加快构建速度,同时减小镜像体积。
  2. Jlink 定制化 JRE:通过 JDK 自带的 jlink 工具,根据项目实际使用的 JDK 模块,定制化生成仅包含所需模块的 JRE,可将 JRE 的体积从几十 MB 缩减到十几 MB,实现镜像体积的极致缩减。
  3. 清理镜像缓存:在构建过程中,若使用 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 部署结果的多维度验证

完成容器启动后,需要通过多维度的验证,确认应用部署成功,运行状态正常,避免出现 “容器在跑,应用不可用” 的问题。

  1. 容器运行状态验证:执行docker ps命令,查看容器的状态,若容器状态为Up,且端口映射正确,说明容器启动成功。

  2. 应用可用性验证

    • 首先在 AlmaLinux 服务器终端内,执行curl http://localhost:8080/hello命令(替换为你的测试接口),若能正常返回接口数据,说明应用在容器内正常运行;
    • 然后在本地电脑的浏览器中,通过http://服务器IP:8080/hello访问接口,若能正常返回数据,说明外部网络可正常访问应用,部署成功。
  3. 日志验证:执行docker logs --tail=100 容器名命令,查看 SpringBoot 的启动日志,确认无报错信息,接口访问有正常的日志输出,应用启动完成。

  4. 资源占用验证:执行docker stats 容器名命令,查看容器的 CPU、内存占用情况,确认资源占用在合理范围内,无异常的内存泄漏、CPU 占用过高等问题。

7.2 生产级线上运维最佳实践

  1. 严格的版本管理:生产环境禁止使用latest标签,每个发布版本都使用明确的语义化版本号,如 1.0.0、1.1.0,每个版本对应唯一的镜像标签,方便版本追溯与回滚,避免版本不可控的问题。

  2. 数据持久化:对于应用的日志文件、配置文件、用户上传的文件、数据库数据等需要持久化保存的内容,必须通过 Docker 数据卷挂载到宿主机,避免容器删除后数据丢失。同时,定期对挂载的数据进行备份,保证数据安全。

  3. 高可用配置:生产环境必须设置--restart=always重启策略,保证容器异常退出、服务器重启后,应用能自动恢复;对于核心业务,建议使用 Docker Compose 或 Kubernetes 实现多实例部署,避免单点故障。

  4. 安全加固:禁止使用 root 用户运行容器,在 Dockerfile 中创建普通用户,通过USER指令切换到普通用户启动应用,最小化容器的权限,降低安全攻击面;同时,定期更新基础镜像,修复镜像中的安全漏洞。

  5. 监控与告警:对接 Prometheus+Grafana 监控体系,监控容器的 CPU、内存、磁盘、网络等资源指标,以及 SpringBoot 应用的 JVM 指标、接口响应时间、错误率等业务指标,设置对应的告警规则,出现异常时及时通知运维人员,提前发现并解决问题。

  6. 镜像管理:搭建私有的镜像仓库,如 Harbor,用于存储企业内部的镜像,避免使用公共镜像仓库带来的安全风险;同时,对镜像进行安全扫描,清理无用的镜像,释放磁盘空间。


总结与展望

本文从本地打包完成的 SpringBoot JAR 包出发,基于 AlmaLinux 服务器环境,完整讲解了 SpringBoot 项目 Docker 容器化部署的全流程,包括 JAR 包高效传输、Dockerfile 深度编写与最佳实践、镜像构建、容器全生命周期管理、高频踩坑实录与解决方案、镜像深度瘦身与性能优化、线上运维最佳实践等核心内容,形成了一套完整的、可直接落地的生产级部署方案。

SpringBoot+Docker+AlmaLinux 的技术组合,完美结合了 SpringBoot 的开发便捷性、Docker 的环境一致性、AlmaLinux 的企业级稳定性,是当下 Java 后端应用部署的最佳实践之一。通过容器化部署,我们彻底解决了环境异构的问题,实现了应用的快速部署、弹性扩缩容、高效运维,极大提升了开发与运维的效率。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐