开发完成的应用最终需要部署到生产环境,让真正的用户能够访问和使用。部署不仅仅是把应用文件复制到服务器那么简单,它涉及到环境配置、资源管理、监控告警、日志收集、性能优化等多个方面。一个部署不当的应用可能会遇到性能问题、稳定性问题、安全问题,甚至无法正常启动。
Spring Boot的"fat jar"特性让部署变得相对简单,你只需要一个JAR文件就能运行整个应用,不需要配置外部应用服务器,不需要管理复杂的类路径。但这并不意味着部署就是简单的java -jar命令,生产环境的部署需要考虑很多因素,包括如何配置数据库连接、如何设置日志级别、如何管理敏感信息、如何实现零停机部署、如何监控应用状态等。
这节课我们将深入学习如何部署Spring Boot应用,包括打包应用、传统服务器部署、容器化部署、云平台部署、生产环境配置、健康检查和监控等内容。通过这些知识的学习,你将能够将应用成功部署到生产环境,并确保应用的稳定运行。
在部署应用之前,我们需要为生产环境准备合适的配置。生产环境的配置与开发环境有很大不同,数据库连接信息、API密钥、日志级别等都需要针对生产环境进行优化。Spring Boot的Profile机制让你能够为不同环境维护不同的配置文件,这是管理多环境配置的最佳实践。
在src/main/resources目录下创建application-prod.properties文件,这个文件将包含生产环境的配置。在IDE中,找到src/main/resources目录,右键点击,选择新建文件,输入文件名application-prod.properties,然后将内容设置为:
spring.application.name=my-spring-boot-app
server.port=8080
server.servlet.context-path=/
spring.datasource.url=${DB_URL:jdbc:mysql://localhost:3306/mydb}
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.format_sql=false
logging.level.root=WARN
logging.level.com.example.myapp=INFO
logging.file.name=/var/log/myapp/application.log
logging.file.max-size=100MB
logging.file.max-history=30
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=when-authorized
management.metrics.export.prometheus.enabled=true
spring.h2.console.enabled=false生产环境配置使用环境变量来引用敏感信息,如数据库密码、API密钥等。${DB_URL:jdbc:mysql://localhost:3306/mydb}表示使用DB_URL环境变量,如果不存在则使用默认值。这种方式避免了将敏感信息硬编码在配置文件中,提高了安全性。
spring.jpa.hibernate.ddl-auto=validate确保Hibernate只验证数据库schema,不会自动创建或修改表结构,这对于生产环境是必需的。logging.file.name指定日志文件的路径,logging.file.max-size和logging.file.max-history配置日志文件的滚动策略,当日志文件达到100MB时会创建新文件,保留最近30天的日志。
management.endpoints.web.exposure.include只暴露必要的Actuator端点,减少安全风险。management.metrics.export.prometheus.enabled=true启用Prometheus指标导出,便于监控系统收集指标数据。
Spring Boot应用可以打包成可执行的JAR文件,这个JAR文件包含了应用的所有依赖和嵌入式服务器,可以直接运行。Maven的spring-boot-maven-plugin插件会自动处理打包过程,你只需要运行Maven命令即可。
在项目根目录下打开终端,执行以下命令来构建应用:
mvn clean package这个命令会清理之前的构建产物,编译源代码,运行测试,然后打包应用。如果测试失败,打包过程会中断,这确保了只有通过测试的代码才会被部署。打包完成后,在target目录下会生成一个可执行的JAR文件,文件名格式为<artifactId>-<version>.jar,例如my-spring-boot-app-0.0.1-SNAPSHOT.jar。
如果你想跳过测试(不推荐,但在某些情况下可能需要),可以使用-DskipTests参数:
mvn clean package -DskipTests构建完成后,你可以通过以下命令运行应用:
java -jar target/my-spring-boot-app-0.0.1-SNAPSHOT.jar应用会使用默认配置启动。如果你想使用生产环境配置,可以通过环境变量或命令行参数来激活生产Profile:
java -jar -Dspring.profiles.active=prod target/my-spring-boot-app-0.0.1-SNAPSHOT.jar或者通过环境变量:
export SPRING_PROFILES_ACTIVE=prod
java -jar target/my-spring-boot-app-0.0.1-SNAPSHOT.jar这种方式让你能够在不同环境中使用相同的JAR文件,只需要改变激活的Profile即可。

默认情况下,Spring Boot会创建一个包含所有依赖的"fat jar",这个文件可能会很大(通常几十MB到几百MB)。虽然这对于大多数场景已经足够,但在某些资源受限的环境中,你可能需要优化JAR文件的大小。
Spring Boot提供了分层JAR的功能,它能够将依赖和应用代码分离,这样在更新应用时只需要重新部署应用代码层,而不需要重新下载依赖层。在pom.xml中配置分层JAR:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin
启用分层后,Maven会创建一个layers.idx文件,定义JAR文件的分层结构。默认情况下,依赖被分为几个层:依赖层(dependencies)、Spring Boot加载器层(spring-boot-loader)、快照依赖层(snapshot-dependencies)、应用层(application)。这种分层结构让Docker等容器技术能够更好地利用层缓存,加快镜像构建和部署速度。
如果你需要进一步减小JAR文件大小,可以考虑排除不必要的依赖,或者使用Spring Boot的瘦JAR(thin jar)功能,但这需要外部提供依赖管理,增加了部署的复杂性。
容器化部署已经成为现代应用部署的标准方式,它提供了环境一致性、资源隔离、易于扩展等优势。Docker是最流行的容器化平台,Spring Boot应用可以很容易地容器化。
在项目根目录下创建Dockerfile文件。在IDE中,找到项目根目录(与pom.xml同级),右键点击,选择新建文件,输入文件名Dockerfile(注意没有扩展名),然后将内容设置为:
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/my-spring-boot-app-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]这个Dockerfile使用Eclipse Temurin的Java 17 JRE Alpine镜像作为基础镜像,Alpine Linux是一个轻量级的Linux发行版,能够显著减小镜像大小。WORKDIR设置工作目录,COPY将JAR文件复制到容器中,EXPOSE声明容器监听的端口,ENTRYPOINT指定容器启动时执行的命令。
构建Docker镜像:
docker build -t my-spring-boot-app:latest .这个命令会在当前目录下查找Dockerfile,构建Docker镜像并标记为my-spring-boot-app:latest。构建完成后,你可以通过以下命令运行容器:
docker run -d -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e DB_URL=jdbc:mysql://host.docker.internal:3306/mydb \
-e DB_USERNAME=myuser \
-e DB_PASSWORD=mypassword \
--name myapp \
my-spring-boot-app:latest-d参数让容器在后台运行,-p 8080:8080将容器的8080端口映射到主机的8080端口,-e设置环境变量,--name指定容器名称。host.docker.internal是Docker提供的特殊主机名,用于从容器内访问主机上的服务。
上面的Dockerfile虽然能够工作,但不够优化。每次代码变更都需要重新复制整个JAR文件,无法利用Docker的层缓存。我们可以使用多阶段构建来优化镜像构建过程。
更新Dockerfile使用多阶段构建:
FROM maven:3.9-eclipse-temurin-17-alpine AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=build /app/target/my-spring-boot-app-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]多阶段构建分为两个阶段:构建阶段使用Maven镜像编译和打包应用,运行阶段使用JRE镜像运行应用。这种方式的好处是最终镜像只包含运行应用所需的文件,不包含构建工具,能够显著减小镜像大小。
更进一步,我们可以利用Spring Boot的分层JAR来优化镜像构建:
FROM maven:3.9-eclipse-temurin-17-alpine AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=build /app/target/my-spring-boot-app-0.0.1-SNAPSHOT.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]java -Djarmode=layertools -jar app.jar extract会提取JAR文件的分层内容,然后使用JarLauncher启动应用。这种方式让Docker能够更好地利用层缓存,当只有应用代码变更时,只需要重新构建应用层,依赖层可以复用缓存。
在实际部署中,应用通常需要与其他服务协作,比如数据库、缓存、消息队列等。Docker Compose允许你定义和运行多容器应用,简化了服务编排的过程。
在项目根目录下创建docker-compose.yml文件:
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_URL=jdbc:mysql://db:3306/mydb
- DB_USERNAME=myuser
- DB_PASSWORD=mypassword
depends_on:
- db
networks:
- app-network
db:
这个Docker Compose配置定义了三个服务:应用服务、数据库服务和Prometheus监控服务。depends_on指定服务之间的依赖关系,确保数据库在应用之前启动。networks定义服务之间的网络,让它们能够相互通信。volumes定义数据卷,用于持久化数据库数据。
使用以下命令启动所有服务:
docker-compose up -d-d参数让服务在后台运行。使用docker-compose down停止所有服务,使用docker-compose logs -f app查看应用日志。
云平台提供了托管的服务,让你能够专注于应用开发,而不需要管理底层基础设施。主流的云平台(如AWS、Azure、Google Cloud、阿里云等)都提供了Spring Boot应用的部署方案。
以AWS为例,你可以使用Elastic Beanstalk来部署Spring Boot应用。Elastic Beanstalk会自动处理负载均衡、自动扩展、监控等任务,你只需要上传JAR文件即可。
创建.ebextensions/application.config文件来配置Elastic Beanstalk:
option_settings:
aws:elasticbeanstalk:application:environment:
SPRING_PROFILES_ACTIVE: prod
DB_URL: jdbc:mysql://your-rds-endpoint:3306/mydb
DB_USERNAME: ${DB_USERNAME}
DB_PASSWORD: ${DB_PASSWORD}
aws:elasticbeanstalk:container:java:
JVMOptions: "-Xmx512m -Xms256m"安装EB CLI后,你可以使用以下命令部署应用:
eb init
eb create production-env
eb deployeb init初始化Elastic Beanstalk项目,eb create创建新环境,eb deploy部署应用。Elastic Beanstalk会自动处理部署过程,包括创建EC2实例、配置负载均衡器、设置监控等。
对于Kubernetes部署,你需要创建Kubernetes清单文件。创建k8s/deployment.yaml文件:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-spring-boot-app
spec:
replicas: 3
selector:
matchLabels:
app: my-spring-boot-app
template:
metadata:
labels:
app: my-spring-boot-app
spec:
containers:
-
这个部署配置创建了3个应用副本,配置了资源限制、健康检查等。livenessProbe用于检测容器是否存活,如果健康检查失败,Kubernetes会重启容器。readinessProbe用于检测容器是否准备好接收流量,只有通过就绪检查的容器才会被加入到负载均衡中。
创建k8s/service.yaml文件来暴露服务:
apiVersion: v1
kind: Service
metadata:
name: my-spring-boot-app-service
spec:
selector:
app: my-spring-boot-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer使用以下命令部署到Kubernetes:
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
生产环境中的应用需要持续监控,以便及时发现问题并采取行动。Spring Boot Actuator提供了健康检查端点,Kubernetes等平台可以使用这些端点来监控应用状态。
我们已经在前面的课程中配置了Actuator,现在让我们为生产环境优化健康检查配置。在application-prod.properties中添加:
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true这些配置启用了Kubernetes的liveness和readiness探针支持。/actuator/health/liveness端点用于liveness探针,如果应用无法恢复,Kubernetes会重启容器。/actuator/health/readiness端点用于readiness探针,如果应用还没有准备好,Kubernetes会从负载均衡中移除该容器。
创建自定义健康检查指示器来监控数据库连接:
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
private final DataSource dataSource;
public DatabaseHealthIndicator(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Health health() {
try (Connection connection = dataSource.getConnection()) {
if (connection.isValid
这个健康检查指示器测试数据库连接是否可用,如果连接失败,健康检查会返回DOWN状态,Kubernetes会认为应用不健康并采取相应的行动。
生产环境中的日志管理非常重要,它能够帮助你诊断问题、监控应用行为、进行安全审计。Spring Boot默认使用Logback作为日志实现,你可以通过配置文件来自定义日志行为。
在application-prod.properties中配置日志:
logging.level.root=WARN
logging.level.com.example.myapp=INFO
logging.file.name=/var/log/myapp/application.log
logging.file.max-size=100MB
logging.file.max-history=30
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level %msg%n这些配置设置了日志级别、日志文件路径、文件滚动策略和日志格式。在生产环境中,通常将根日志级别设置为WARN,只记录警告和错误,减少日志量。应用包的日志级别设置为INFO,记录重要的业务事件。
对于容器化部署,日志应该输出到标准输出和标准错误,这样容器编排平台(如Kubernetes)可以自动收集日志。更新配置:
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n这样日志会同时输出到控制台和文件,容器平台可以从标准输出收集日志。
在生产环境中,应用更新不应该导致服务中断。零停机部署(Zero-downtime Deployment)是一种部署策略,它确保在部署新版本时,旧版本继续提供服务,直到新版本完全就绪。
对于Kubernetes,滚动更新(Rolling Update)是实现零停机部署的标准方式。更新deployment.yaml:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
replicas: 3maxSurge指定在更新过程中可以超过期望副本数的最大数量,maxUnavailable指定在更新过程中可以不可用的最大副本数。maxUnavailable: 0确保始终有足够的副本提供服务,实现零停机。
对于传统服务器部署,可以使用蓝绿部署(Blue-Green Deployment)策略。准备两套完全相同的环境(蓝环境和绿环境),当前使用蓝环境提供服务,部署新版本到绿环境,测试通过后切换流量到绿环境,蓝环境作为备用。
实现蓝绿部署需要负载均衡器的支持。在切换流量之前,确保新版本通过了健康检查,然后逐步将流量从旧版本切换到新版本。如果新版本出现问题,可以快速切回旧版本。
在生产环境中,应该为应用配置适当的资源限制,防止应用消耗过多资源影响其他服务。对于容器化部署,可以在Docker或Kubernetes中配置资源限制。
在docker-compose.yml中配置资源限制:
services:
app:
deploy:
resources:
limits:
cpus: '1'
memory: 1G
reservations:
cpus: '0.5'
memory: 512Mlimits设置资源上限,reservations设置资源预留。如果应用尝试使用超过限制的资源,容器会被限制或终止。
对于Kubernetes,资源限制在Deployment中配置:
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"requests指定容器需要的最小资源,Kubernetes调度器会确保节点有足够的资源。limits指定容器可以使用的最大资源,超过限制的容器可能会被终止。
JVM参数也应该根据资源限制进行调整。在application-prod.properties中配置:
spring.jvm.arguments=-Xmx768m -Xms512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200这些参数设置最大堆内存为768MB(略小于容器内存限制,为其他进程留出空间),初始堆内存为512MB,使用G1垃圾收集器,最大GC暂停时间为200毫秒。
监控是生产环境运维的重要组成部分,它能够帮助你了解应用的运行状态、性能指标、错误率等。Spring Boot Actuator提供了丰富的指标,你可以将这些指标导出到监控系统(如Prometheus、InfluxDB等)。
我们已经配置了Prometheus指标导出,现在需要配置Prometheus来收集这些指标。创建prometheus.yml文件:
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'spring-boot-app'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['app:8080']这个配置告诉Prometheus每15秒从应用的/actuator/prometheus端点收集指标。Prometheus会将这些指标存储在自己的时间序列数据库中,你可以使用PromQL查询语言来查询和分析这些指标。
创建Grafana仪表板来可视化指标。虽然具体的仪表板配置超出了本文的范围,但你可以使用Spring Boot的默认指标来创建基本的监控仪表板,包括JVM内存使用、HTTP请求统计、数据库连接池状态等。
配置告警规则来及时发现问题。在Prometheus中创建alerts.yml文件:
groups:
- name: spring_boot_alerts
rules:
- alert: HighErrorRate
expr: rate(http_server_requests_seconds_count{status=~"5.."}[5m]) > 0.1
for: 5m
annotations:
summary: "高错误率"
description: "应用错误率超过10%"
- alert: HighMemoryUsage
expr: jvm_memory_used_bytes / jvm_memory_max_bytes > 0.9
for: 5m
这些告警规则会在错误率过高或内存使用过高时触发告警,让你能够及时发现问题并采取行动。

当应用需要更新或维护时,应该优雅地关闭应用,确保正在处理的请求能够完成,而不是被强制中断。Spring Boot支持优雅关闭,你只需要配置关闭超时时间即可。
在application-prod.properties中配置:
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30sserver.shutdown=graceful启用优雅关闭,spring.lifecycle.timeout-per-shutdown-phase设置关闭超时时间为30秒。当应用收到关闭信号(如SIGTERM)时,Spring Boot会停止接受新请求,等待正在处理的请求完成,最多等待30秒。如果30秒后仍有请求未完成,应用会强制关闭。
对于Kubernetes,需要在Deployment中配置优雅关闭:
spec:
template:
spec:
terminationGracePeriodSeconds: 30
containers:
- name: app
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]terminationGracePeriodSeconds设置Pod终止的宽限期,preStop钩子在容器终止前执行,给应用一些时间来完成优雅关闭。
在生产环境中,应该使用HTTPS来加密客户端和服务器之间的通信,保护数据在传输过程中的安全。Spring Boot支持HTTPS,你需要配置SSL证书。
将SSL证书文件(通常是.p12或.jks格式)放在src/main/resources目录下,然后在application-prod.properties中配置:
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=${SSL_KEYSTORE_PASSWORD}
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=tomcatkey-store指定证书文件路径,key-store-password指定证书密码(应该通过环境变量设置),key-store-type指定证书类型,key-alias指定证书别名。
对于云平台部署,通常使用负载均衡器来处理HTTPS,应用本身仍然使用HTTP。这种方式简化了证书管理,因为证书只需要在负载均衡器上配置一次,而不需要在每个应用实例上配置。
部署Spring Boot应用时,应该遵循一些最佳实践来确保部署的成功和应用的稳定运行。这些实践来自于实际项目中的经验总结,能够帮助你避免常见的部署问题。
部署是一个持续的过程,不是一次性的任务。你应该建立自动化的部署流程,使用CI/CD工具(如Jenkins、GitLab CI、GitHub Actions)来自动化构建、测试和部署过程。这样能够减少人为错误,提高部署效率,确保每次部署都是一致和可重复的。
这部分我们已经深入掌握了如何部署Spring Boot应用。你学会了如何打包应用、如何容器化部署、如何部署到云平台、如何配置生产环境、如何实现健康检查和监控、以及如何遵循部署最佳实践。这些知识将帮助你成功将应用部署到生产环境,并确保应用的稳定运行。
在下一个部分中,我们将深入学习响应式编程的高级特性,包括背压处理、操作符组合、性能优化等内容,进一步提升你的响应式编程技能。