使用 Docker 部署 Spring Boot 应用
# 一、简介
# 1. Docker 镜像简介
Docker
镜像是一个轻量级、独立的可执行软件包,它包含运行应用程序所需的一切:代码、运行时、系统工具、系统库和设置。镜像可以用来创建 Docker
容器,容器是镜像的运行实例。Docker
镜像采用分层结构,每个镜像都由一系列只读层组成,这些层叠加在一起形成最终的文件系统。这种分层结构使得镜像的构建、分发和共享非常高效。
Docker
镜像的主要特点:
- 轻量级: 镜像只包含运行应用程序所需的最小文件集,体积小
- 可移植性: 镜像可以在任何支持
Docker
的平台上运行,实现"一次构建,到处运行" - 隔离性: 每个容器都在隔离的环境中运行,互不影响
- 版本控制: 镜像可以进行版本控制,方便回滚和管理
- 可重复性: 镜像可以保证每次构建出的容器都是一致的
# 2. Spring Boot 简介
Spring Boot
是一个用于创建独立的、生产级别的基于 Spring
的应用程序的框架。它简化了 Spring
应用程序的开发过程,通过自动配置和起步依赖,减少了大量的样板代码和配置工作。Spring Boot
内嵌了 Tomcat
、Jetty
或 Undertow
等 Web 服务器,使得应用程序可以直接打包成可执行的 JAR
文件,无需部署到外部应用服务器。
Spring Boot
的主要特点:
- 简化配置: 通过自动配置减少了大量的
XML
配置 - 起步依赖: 提供了一系列预定义的依赖项,简化了项目构建配置
- 内嵌服务器: 内置了 Web 服务器,可以直接运行应用程序
- 生产就绪特性: 提供了健康检查、指标监控等生产环境所需的功能
- 易于集成: 可以轻松集成各种
Spring
项目和其他第三方库
# 3. 使用 Spring Boot 构建 Docker 镜像的优势
将 Spring Boot
应用程序构建成 Docker
镜像具有以下优势:
- 简化部署: 将应用程序及其依赖项打包成一个独立的镜像,简化了部署流程,无需手动配置环境
- 环境一致性: 确保应用程序在开发、测试和生产环境中运行的一致性,避免了"在我机器上可以运行"的问题
- 可移植性: 可以在任何支持
Docker
的平台上运行,提高了应用程序的可移植性 - 资源隔离: 每个容器都在隔离的环境中运行,互不影响,提高了应用程序的稳定性和安全性
- 弹性伸缩: 可以通过
Docker
快速创建和销毁容器,实现应用程序的弹性伸缩 - 持续集成/持续交付(CI/CD): 可以将
Docker
镜像构建集成到CI/CD
流程中,实现自动化构建、测试和部署
# 二、准备工作
# 1. 安装 Docker
Docker 的安装因操作系统而异,请参考 Docker 官方文档进行安装:
- Windows: https://docs.docker.com/desktop/install/windows-install/ (opens new window)
- macOS: https://docs.docker.com/desktop/install/mac-install/ (opens new window)
- Linux: https://docs.docker.com/engine/install/ (opens new window) (选择对应的发行版)
安装完成后,在命令行终端运行以下命令验证安装是否成功:
docker --version
docker run hello-world
如果能看到 Docker 版本信息和 "Hello from Docker!" 的输出,则表示安装成功。
# 2. 安装 JDK
Spring Boot 项目通常需要 Java Development Kit (JDK) 来编译和运行。请确保安装了 JDK 8 或更高版本。你可以从 Oracle 官网、OpenJDK 官网或其他 JDK 发行版提供商处下载并安装 JDK。
安装完成后,在命令行终端运行以下命令验证安装是否成功:
java -version
javac -version
如果能看到 Java 和 javac 的版本信息,则表示安装成功。
# 3. 安装 Maven 或 Gradle
Maven 和 Gradle 是 Java 项目常用的构建工具。你可以选择其中一个来构建 Spring Boot 项目。
Maven:
Gradle:
安装完成后,在命令行终端运行以下命令验证安装是否成功:
# Maven
mvn -version
# Gradle
gradle -version
如果能看到 Maven 或 Gradle 的版本信息,则表示安装成功。
# 4. 创建 Spring Boot 项目
你可以使用 Spring Initializr (https://start.spring.io/ (opens new window)) 来快速创建一个 Spring Boot 项目。
- 访问 Spring Initializr 网站。
- 选择项目类型(Maven 或 Gradle)。
- 选择 Spring Boot 版本。
- 选择 Java 版本。
- 填写项目元数据(Group、Artifact 等)。
- 添加所需的依赖项(例如,"Spring Web" 依赖用于创建 Web 应用程序)。
- 点击 "Generate" 按钮下载项目压缩包。
- 解压压缩包,并使用你喜欢的 IDE(如 IntelliJ IDEA、Eclipse 等)导入项目。
或者,你也可以使用 IDE 内置的 Spring Boot 项目创建向导来创建项目。
创建项目后, 可以简单修改代码,添加一个REST Controller。例如:
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/")
public String hello() {
return "Hello from Spring Boot!";
}
}
这个 HelloController
类定义了一个简单的 REST API,当访问根路径 /
时,返回 "Hello from Spring Boot!"。
现在,你已经完成了构建 Spring Boot Docker 镜像的所有准备工作。
# 三、使用 Dockerfile 构建镜像
# 1. 编写 Dockerfile
Dockerfile 是一个文本文件,其中包含一系列指令,用于告诉 Docker 如何构建镜像。在 Spring Boot 项目的根目录下创建一个名为 Dockerfile
的文件(没有扩展名),然后添加以下内容:
# 1.1 基础镜像选择
选择一个合适的 Java 基础镜像。这里我们选择 OpenJDK 17 作为基础镜像,因为它是一个轻量级的、官方维护的镜像:
FROM openjdk:17-jdk-slim
FROM
指令指定了基础镜像。openjdk:17-jdk-slim
表示使用 OpenJDK 17 的 slim 版本,它只包含运行 Java 应用程序所需的最小组件,可以减小镜像大小。你也可以选择其他适合你的基础镜像。
# 1.2 添加 Spring Boot 应用
将 Spring Boot 应用程序的 JAR 文件添加到镜像中。假设你的 Spring Boot 项目使用 Maven 构建,并且已经通过 mvn package
命令生成了 JAR 文件,通常位于 target/
目录下。
COPY target/*.jar app.jar
COPY
指令将本地文件复制到镜像中。target/*.jar
表示将 target/
目录下所有以 .jar
结尾的文件复制到镜像的根目录下,并重命名为 app.jar
。你可以根据你的实际情况修改路径和文件名。
# 1.3 配置启动命令
指定容器启动时要执行的命令,即运行 Spring Boot 应用程序的命令。
CMD ["java", "-jar", "app.jar"]
CMD
指令指定了容器启动时要执行的命令。java -jar app.jar
表示使用 Java 命令运行 app.jar
文件。
完整的 Dockerfile
示例:
FROM openjdk:17-jdk-slim
COPY target/*.jar app.jar
CMD ["java", "-jar", "app.jar"]
# 2. 优化的 Dockerfile
上面的 Dockerfile
虽然可以工作,但还可以进一步优化。以下是一个更优化的多阶段构建 Dockerfile
:
# 第一阶段:构建阶段
FROM maven:3.8.6-openjdk-17 AS builder
# 设置工作目录
WORKDIR /build
# 首先复制 pom.xml,利用 Docker 缓存
COPY pom.xml .
RUN mvn dependency:go-offline -B
# 复制源代码并构建
COPY src ./src
RUN mvn clean package -DskipTests
# 第二阶段:运行阶段
FROM openjdk:17-jre-slim
# 创建非 root 用户
RUN addgroup --system appgroup && adduser --system appuser --ingroup appgroup
# 设置工作目录
WORKDIR /app
# 从构建阶段复制 jar 文件
COPY /build/target/*.jar app.jar
# 更改文件所有者
RUN chown appuser:appgroup /app/app.jar
# 切换到非 root 用户
USER appuser
# 暴露端口
EXPOSE 8080
# 健康检查
HEALTHCHECK \
CMD curl -f http://localhost:8080/actuator/health || exit 1
# 启动命令
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
优化点说明:
- 多阶段构建:分离构建和运行环境,减小最终镜像大小
- 使用 JRE 而非 JDK:运行时只需要 JRE,进一步减小镜像
- 非 root 用户:提高安全性
- 健康检查:添加容器健康检查
- Docker 缓存优化:先复制
pom.xml
利用依赖缓存
# 3. 构建镜像
在 Dockerfile
所在的目录下,打开命令行终端,运行以下命令构建镜像:
docker build -t springboot-docker-image .
docker build
:构建镜像的命令-t springboot-docker-image
:给镜像打上标签(tag),这里将镜像命名为springboot-docker-image
.
:表示Dockerfile
所在的当前目录
指定版本标签:
docker build -t springboot-docker-image:1.0.0 .
构建过程可能需要一些时间,具体取决于你的网络速度和项目大小。构建完成后,可以使用以下命令查看已构建的镜像:
docker images
# 4. 运行容器
使用以下命令运行刚刚构建的镜像,创建一个 Docker 容器:
基础运行:
docker run -p 8080:8080 springboot-docker-image
后台运行:
docker run -d -p 8080:8080 --name my-spring-app springboot-docker-image
带环境变量运行:
docker run -d -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e JAVA_OPTS="-Xms512m -Xmx1024m" \
--name my-spring-app springboot-docker-image
参数说明:
docker run
:运行容器的命令-d
:后台运行容器-p 8080:8080
:将容器的 8080 端口映射到主机的 8080 端口--name my-spring-app
:给容器指定一个名称-e
:设置环境变量springboot-docker-image
:要运行的镜像名称
查看容器状态:
# 查看运行中的容器
docker ps
# 查看容器日志
docker logs my-spring-app
# 进入容器
docker exec -it my-spring-app /bin/bash
容器启动后,你的 Spring Boot 应用程序将在容器内运行。你可以通过浏览器访问 http://localhost:8080
来验证应用程序是否正常运行。如果一切正常,你将看到应用程序的输出,例如 "Hello from Spring Boot!"。
现在,你已经成功地使用 Dockerfile
构建了 Spring Boot 应用程序的 Docker 镜像,并运行了容器。
# 四、使用 Maven 插件构建镜像
除了使用 Dockerfile,还可以使用 Maven 插件来简化 Docker 镜像的构建过程。常用的 Maven 插件有 docker-maven-plugin
(Spotify)、jib-maven-plugin
(Google),以及 Spring Boot 官方提供的 spring-boot-maven-plugin
。
# 1. 添加 Maven 插件
# 1.1 Jib 插件 (Google)
在 Spring Boot 项目的 pom.xml
文件中,找到 <build>
-> <plugins>
部分,添加 jib-maven-plugin
:
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<to>
<image>your-docker-registry/your-image-name:tag</image>
</to>
</configuration>
</plugin>
<groupId>
和<artifactId>
:指定插件的坐标。<version>
:指定插件的版本。建议使用最新稳定版本。<configuration>
部分对插件进行配置。
# 1.2 Spring Boot Maven 插件
Spring Boot 2.3 及以上版本,spring-boot-maven-plugin
提供了内置的镜像构建支持。它利用了 Cloud Native Buildpacks 来创建镜像,无需编写 Dockerfile。确保你的 Spring Boot 版本 >= 2.3。如果低于这个版本,请考虑升级。
如果你的项目已经使用了spring-boot-maven-plugin
,通常不需要额外添加,因为它已经是 Spring Boot 项目的标准插件。如果你的pom.xml
中没有,可以这样添加:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>build-image</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
这里添加了<executions>
,显式地告诉插件在Maven构建过程(package
)中执行build-image
目标。
# 2. 配置插件
# 2.1 Jib 插件配置
在 <configuration>
部分,你需要配置以下内容:
<to>
:指定目标镜像的名称和标签。<image>
:镜像名称。通常采用仓库地址/镜像名称:标签
的格式。- 如果你要推送到 Docker Hub,仓库地址可以省略,直接写
your-dockerhub-username/your-image-name:tag
。 - 如果你要推送到私有仓库,需要指定完整的仓库地址,例如
my-private-registry.com/my-project/my-image:1.0
。 tag
是镜像的标签,通常用于表示版本,例如latest
、v1.0
等。
- 如果你要推送到 Docker Hub,仓库地址可以省略,直接写
示例:
<configuration>
<to>
<image>mydockerhubuser/myspringbootapp:latest</image>
</to>
</configuration>
这个配置表示构建的镜像名为mydockerhubuser/myspringbootapp
, 标签是latest
其他常用配置(可选):
<from>
:指定基础镜像。默认情况下,Jib 会根据你的项目自动选择一个合适的基础镜像。你也可以手动指定:<from> <image>openjdk:17-jdk-slim</image> </from>
<container>
: 对容器进行配置<container> <ports> <port>8080</port> </ports> <jvmFlags> <jvmFlag>-Xms512m</jvmFlag> <jvmFlag>-Xmx1024m</jvmFlag> </jvmFlags> </container>
# 2.2 Spring Boot Maven 插件配置
spring-boot-maven-plugin
的 build-image
目标会自动检测项目并构建镜像,通常不需要太多配置。但是,你可以通过以下方式自定义构建过程:
指定镜像名称:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <image> <name>your-docker-registry/your-image-name:${project.version}</name> </image> </configuration> </plugin>
这里使用
${project.version}
作为标签,它会自动获取项目的版本号。指定构建器(Builder):
Buildpacks 使用构建器(Builder)来创建镜像。默认情况下,
spring-boot-maven-plugin
会自动选择一个合适的构建器。你也可以手动指定:<configuration> <image> <builder>paketobuildpacks/builder-jammy-base</builder> </image> </configuration>
常用的builder有
paketobuildpacks/builder-jammy-base
,gcr.io/buildpacks/builder:v1
等绑定到 Maven 生命周期
默认情况下,build-image
目标不会自动绑定到 Maven 的任何生命周期阶段。在前面的<executions>
配置中,我们已经手动绑定了build-image
到package
阶段。你也可以选择其他阶段,例如install
。当然,你也可以不绑定,而是使用 mvn spring-boot:build-image
命令。
# 3. 构建镜像
# 3.1 使用 Jib 构建
构建并推送到 Docker Hub 或私有仓库:
mvn compile jib:build
这个命令会先编译项目,然后构建 Docker 镜像,并将其推送到你在
<to>
-><image>
中指定的仓库。在推送之前,你需要确保已经登录到目标仓库(使用docker login
命令)。仅构建镜像到本地 Docker 守护进程(不推送):
mvn compile jib:dockerBuild
这个命令会构建镜像,但不会推送到远程仓库,而是直接加载到本地 Docker 守护进程中。这样你就可以在本地使用
docker run
命令运行镜像了。
# 3.2 使用 Spring Boot Maven 插件构建
mvn spring-boot:build-image
或者,如果你已经将build-image
绑定到了package
阶段
mvn package
这个命令会使用 Buildpacks 构建镜像。默认情况下,镜像会被加载到本地 Docker 守护进程。
推送到远程仓库:
spring-boot-maven-plugin
本身不直接支持推送到远程仓库。你需要先构建镜像到本地,然后使用 docker tag
和 docker push
命令手动推送:
构建镜像:
mvn spring-boot:build-image
标记镜像:
docker tag your-image-name:tag your-docker-registry/your-image-name:tag
推送镜像:
docker push your-docker-registry/your-image-name:tag
# 4. 运行容器
构建完成后,可以使用 docker run
命令运行容器,方式与使用 Dockerfile 构建的镜像相同:
docker run -p 8080:8080 your-docker-registry/your-image-name:tag
或者
docker run -p 8080:8080 mydockerhubuser/myspringbootapp:latest
将 your-docker-registry/your-image-name:tag
替换为你的实际镜像名称和标签。
使用 Maven 插件构建 Docker 镜像的好处是:
- 无需编写 Dockerfile:简化了构建过程(
spring-boot-maven-plugin
和 Jib)。 - 自动优化:Jib 等插件会自动优化镜像的分层,提高构建速度和镜像的缓存利用率。
spring-boot-maven-plugin
使用的 Buildpacks 也会进行优化。 - 与 Maven 集成:可以方便地将镜像构建集成到 Maven 的构建生命周期中。
- 易于推送到仓库:Jib 可以直接将镜像推送到 Docker Hub 或私有仓库。
spring-boot-maven-plugin
需要配合docker
命令手动推送。
选择哪个插件取决于你的具体需求和偏好。如果你希望更简单、更自动化的构建过程,并且不需要推送到远程仓库,spring-boot-maven-plugin
是一个不错的选择。如果你需要更精细的控制、推送到远程仓库,或者希望与其他工具(如 Skaffold)集成,Jib 可能更适合你。
# 五、优化镜像大小:jar和lib分离
在 Spring Boot 项目中,默认情况下会将所有依赖和应用程序代码打包到一个可执行的 fat jar
中。然而,在某些场景下,我们可能希望将应用程序的 jar
和依赖的 lib
分离,以便更好地管理依赖、优化镜像构建流程或减少镜像体积。
以下是实现这一目标的具体步骤,包括如何配置 Spring Boot 项目以及如何编写 Dockerfile 构建镜像。
# 1. Spring Boot 配置:分离 jar
和 lib
Spring Boot 提供了 Maven 和 Gradle 插件来支持将依赖分离为独立的目录结构。以下是基于 Maven 的配置方法:
# (1)修改 pom.xml
在 pom.xml
中启用 spring-boot-maven-plugin
的 layout
属性为 ZIP
或 DIR
,以生成分离的依赖目录。
<build>
<plugins>
<!-- Spring Boot Maven Plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
<layout>ZIP</layout>
<!-- 解决windows命令行窗口中文乱码 -->
<jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
<!-- 不包含任何依赖 -->
<includes>
<include>
<groupId>nothing</groupId>
<artifactId>nothing</artifactId>
</include>
</includes>
</configuration>
</plugin>
<!-- Maven Dependency Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<!-- 复制稳定的依赖到 lib 目录 -->
<execution>
<id>copy-stable-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<includeScope>runtime</includeScope>
<excludeArtifactIds>your-dynamic-artifact-id-1,your-dynamic-artifact-id-2</excludeArtifactIds>
</configuration>
</execution>
<!-- 复制动态的依赖到 dynamic-lib 目录 -->
<execution>
<id>copy-dynamic-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/dynamic-lib</outputDirectory>
<includeArtifactIds>your-dynamic-artifact-id-1,your-dynamic-artifact-id-2</includeArtifactIds>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
关键配置解析:
(1)复制稳定依赖:
<outputDirectory>${project.build.directory}/lib</outputDirectory>
:指定稳定的依赖输出到target/lib
目录。<includeScope>runtime</includeScope>
:只包含运行时所需的依赖。<excludeArtifactIds>
:排除经常变动的 JAR 文件(通过artifactId
指定)。
(2)复制动态依赖:
<outputDirectory>${project.build.directory}/dynamic-lib</outputDirectory>
:指定动态的依赖输出到target/dynamic-lib
目录。<includeArtifactIds>
:只包含经常变动的 JAR 文件(通过artifactId
指定)。
# 示例说明:
假设项目中有两个经常变动的 JAR 文件,分别为 module-a
和 module-b
,则可以在 includeArtifactIds
中指定它们:
<includeArtifactIds>module-a,module-b</includeArtifactIds>
# (2)执行打包命令
运行以下命令,生成分离的 lib
和 dynamic-lib
目录:
mvn clean package
执行后,target
目录中将包含以下内容:
lib/
:存放稳定的第三方依赖。dynamic-lib/
:存放经常变动的 JAR 文件。your-app.jar
:不包含依赖的应用程序主 JAR。
# (3)验证启动方式
使用以下命令验证分离后的应用是否可以正常启动:
java -Dloader.path=target/lib,target/dynamic-lib -jar target/your-app.jar
-Dloader.path=target/lib,target/dynamic-lib
指定了依赖的路径。
# 2. Dockerfile 构建镜像
在 Dockerfile 中,我们将应用程序的 jar
和 lib
目录分别拷贝到容器中,并通过正确的启动命令运行应用。
# 示例 Dockerfile
# 使用官方 OpenJDK 镜像作为基础镜像
FROM openjdk:17-jdk-slim AS builder
# 设置工作目录
WORKDIR /app
# 将 lib、dynamic-lib 目录和主 JAR 文件拷贝到镜像中
COPY target/lib /app/lib
COPY target/dynamic-lib /app/dynamic-lib
COPY target/your-app.jar /app/your-app.jar
# 设置环境变量(可选)
ENV JAVA_OPTS=""
# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -Dloader.path=/app/lib,/app/dynamic-lib -jar /app/your-app.jar"]
# 3. 使用Docker构建Maven项目
如果不想在本地环境中执行Maven构建,也可以使用Docker进行项目构建。得到构建后的组件(jar和lib),再打包到镜像中。
# 第一阶段:构建阶段
FROM maven:3.8.6-openjdk-17 AS builder
# 设置工作目录
WORKDIR /build
# 拷贝 Maven 配置文件和源码
COPY pom.xml .
COPY src ./src
RUN mvn clean package
# 第二阶段:运行阶段
FROM openjdk:17-jdk-slim
# 设置工作目录
WORKDIR /app
# 拷贝稳定的 lib 依赖
COPY /build/target/lib /app/lib
# 拷贝动态的 dynamic-lib 依赖
COPY /build/target/dynamic-lib /app/dynamic-lib
# 拷贝主 JAR 文件
COPY /build/target/your-app.jar /app/your-app.jar
# 设置启动命令
ENV JAVA_OPTS=""
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -Dloader.path=/app/lib,/app/dynamic-lib -jar /app/your-app.jar"]
# 4. 运行和验证
# (1)构建镜像
在项目根目录下运行以下命令构建镜像:
docker build -t your-app-image .
# (2)运行容器
使用以下命令启动容器:
docker run -d -p 8080:8080 --name your-app-container your-app-image
# (3)验证应用
访问应用的健康检查端点或其他接口,确保应用正常运行。例如:
curl http://localhost:8080/actuator/health
# 5. 参考
可以参考如下资源:
- SpringBoot 将 jar 包和 lib 依赖分离,Dockerfile 构建镜像 (opens new window) 文中使用了maven-jar-plugin插件来配合实现jar和lib分离。注意:如果mainClass改名字,一定要注意同步到插件配置上
注意:
- 上述分离的方式适用于jar包,不适用于war包。因为war的打包规则特殊,使用
loader.path
配置并不起作用。
# 六、使用 Docker Compose 部署
Docker Compose
是一个用于定义和运行多容器 Docker 应用程序的工具。通过一个 YAML 文件,你可以配置应用程序的服务、网络和存储卷,然后使用一个命令就可以创建并启动所有服务。
# 1. 创建 docker-compose.yml 文件
在项目根目录创建 docker-compose.yml
文件:
version: '3.8'
services:
# Spring Boot 应用服务
app:
build:
context: .
dockerfile: Dockerfile
image: springboot-docker-image:latest
container_name: spring-boot-app
restart: unless-stopped
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/mydb
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_PASSWORD=password
depends_on:
- mysql
- redis
networks:
- app-network
# MySQL 数据库服务
mysql:
image: mysql:8.0
container_name: mysql-db
restart: unless-stopped
environment:
- MYSQL_DATABASE=mydb
- MYSQL_ROOT_PASSWORD=password
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-network
# Redis 缓存服务
redis:
image: redis:7-alpine
container_name: redis-cache
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- app-network
volumes:
mysql_data:
redis_data:
networks:
app-network:
driver: bridge
# 2. 生产环境配置
创建 docker-compose.prod.yml
文件用于生产环境:
version: '3.8'
services:
app:
environment:
- SPRING_PROFILES_ACTIVE=prod
- JAVA_OPTS=-Xms1g -Xmx2g -XX:+UseG1GC
deploy:
resources:
limits:
memory: 2g
cpus: '1.5'
reservations:
memory: 1g
cpus: '1'
mysql:
environment:
- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password
secrets:
- mysql_root_password
volumes:
- mysql_data:/var/lib/mysql
- ./conf/mysql.cnf:/etc/mysql/conf.d/mysql.cnf:ro
secrets:
mysql_root_password:
file: ./secrets/mysql_root_password.txt
# 3. 常用命令
# 启动所有服务
docker-compose up -d
# 查看服务状态
docker-compose ps
# 查看服务日志
docker-compose logs app
# 停止所有服务
docker-compose down
# 重新构建并启动
docker-compose up --build -d
# 使用生产环境配置
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 扩展应用实例
docker-compose up --scale app=3 -d
# 4. 健康检查和监控
添加健康检查配置到 docker-compose.yml
:
services:
app:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
# 七、生产环境最佳实践
# 1. 安全配置
使用非 root 用户:
RUN addgroup --system appgroup && adduser --system appuser --ingroup appgroup
USER appuser
敏感信息管理:
# 使用 Docker Secrets
echo "your_secret_password" | docker secret create mysql_password -
# 在 docker-compose.yml 中引用
services:
app:
secrets:
- mysql_password
网络安全:
# 创建自定义网络
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # 内部网络,无法访问外网
# 2. 资源限制
services:
app:
deploy:
resources:
limits:
memory: 2g
cpus: '1.5'
reservations:
memory: 1g
cpus: '1'
# 3. 日志管理
services:
app:
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "3"
# 4. 监控配置
添加监控服务到 docker-compose.yml
:
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
# 八、常见问题和故障排除
# 1. 常见错误
端口占用问题:
# 查看端口占用
netstat -tulpn | grep :8080
# 修改端口映射
docker run -p 8081:8080 springboot-docker-image
内存不足:
# 查看容器资源使用情况
docker stats
# 增加内存限制
docker run -m 2g springboot-docker-image
镜像构建失败:
# 清理 Docker 缓存
docker system prune -a
# 查看构建详细信息
docker build --no-cache -t springboot-docker-image .
# 2. 调试技巧
进入容器调试:
# 进入运行中的容器
docker exec -it container_name /bin/bash
# 查看容器内文件
docker exec container_name ls -la /app
# 查看进程
docker exec container_name ps aux
网络调试:
# 查看容器网络
docker network ls
# 测试容器间连通性
docker exec container1 ping container2
# 3. 性能优化
JVM 参数优化:
ENV JAVA_OPTS="-Xms1g -Xmx2g -XX:+UseG1GC -XX:+PrintGCDetails"
镜像层优化:
# 合并 RUN 命令
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*
# 九、总结
本文介绍了如何将 Spring Boot 应用打包成 Docker 镜像,并对构建过程中的关键步骤和优化方法进行了详细讲解。
通过本文的学习,你应该能够:
- 编写和优化
Dockerfile
来构建 Spring Boot 应用的 Docker 镜像 - 使用
docker build
、docker run
等命令构建和运行镜像 - 使用 Maven 插件(
jib-maven-plugin
或spring-boot-maven-plugin
)简化镜像构建过程 - 使用
Docker Compose
管理多容器应用 - 掌握生产环境部署的最佳实践和安全配置
- 解决常见的 Docker 部署问题
将 Spring Boot 应用容器化是现代应用部署的重要一步。Docker 提供了轻量级、可移植、可扩展的容器化解决方案,结合 Spring Boot 的快速开发特性,可以大大提高应用的开发、部署和运维效率。
祝你变得更强!