开发完成的应用最终需要部署到生产环境,让真正的用户能够访问和使用。部署不仅仅是把应用文件复制到服务器那么简单,它涉及到环境配置、资源管理、监控告警、日志收集、性能优化等多个方面。一个部署不当的应用可能会遇到性能问题、稳定性问题、安全问题,甚至无法正常启动。
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 deploy
eb 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: 3
maxSurge指定在更新过程中可以超过期望副本数的最大数量,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: 512M
limits设置资源上限,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=30s
server.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=tomcat
key-store指定证书文件路径,key-store-password指定证书密码(应该通过环境变量设置),key-store-type指定证书类型,key-alias指定证书别名。
对于云平台部署,通常使用负载均衡器来处理HTTPS,应用本身仍然使用HTTP。这种方式简化了证书管理,因为证书只需要在负载均衡器上配置一次,而不需要在每个应用实例上配置。
部署Spring Boot应用时,应该遵循一些最佳实践来确保部署的成功和应用的稳定运行。这些实践来自于实际项目中的经验总结,能够帮助你避免常见的部署问题。
部署是一个持续的过程,不是一次性的任务。你应该建立自动化的部署流程,使用CI/CD工具(如Jenkins、GitLab CI、GitHub Actions)来自动化构建、测试和部署过程。这样能够减少人为错误,提高部署效率,确保每次部署都是一致和可重复的。
这部分我们已经深入掌握了如何部署Spring Boot应用。你学会了如何打包应用、如何容器化部署、如何部署到云平台、如何配置生产环境、如何实现健康检查和监控、以及如何遵循部署最佳实践。这些知识将帮助你成功将应用部署到生产环境,并确保应用的稳定运行。
在下一个部分中,我们将深入学习响应式编程的高级特性,包括背压处理、操作符组合、性能优化等内容,进一步提升你的响应式编程技能。