部署到云端
Spring Boot 的可执行 jar 包已为大多数流行的云 PaaS(平台即服务)提供商准备就绪。 这些提供商通常要求你“自带容器”。 它们管理的是应用程序进程(而非特指 Java 应用程序),因此需要一个中间层,将你的应用程序适配到云的运行进程概念中。
两个流行的云提供商 Heroku 和 Cloud Foundry 采用了“buildpack”方法。
Buildpack 会将部署的代码包裹在启动应用程序所需的一切环境中。
它可能是一个 JDK 和对 java 的调用、一个嵌入式 Web 服务器,或是一个功能齐全的应用程序服务器。
Buildpack 是可插拔的,但理想情况下,你应该能够尽可能少地对其进行自定义。
这减少了不受你控制的功能的占用空间。
它最大限度地减少了开发和生产环境之间的差异。
理想情况下,您的应用程序(例如 Spring Boot 可执行 JAR)应将运行所需的一切内容都打包在其中。
在本节中,我们将了解将“入门”部分中开发的应用程序在云中运行所需的步骤。
安全注意事项
仅当应用程序从受信任的HTTP代理接收流量时,才应启用对 转发头 的支持,并且只能直接从受信任的网络访问。
在云平台上运行时,会自动启用转发头。
这建立在平台将确保每个应用实例只能直接从受信任的网络访问的假设之上。
如果您的特定平台不符合此条件,请将 spring.forward-headers-strategy 设置为 none。
Cloud Foundry
Cloud Foundry 提供了默认的构建包,如果未指定其他构建包,则会使用这些构建包。
Cloud Foundry Java 构建包 对 Spring 应用程序(包括 Spring Boot)有很好的支持。
您可以部署独立的可执行 jar 应用程序以及传统的 .war 打包应用程序。
在您构建好应用程序(例如,使用 mvn clean package)并已安装 命令行工具 cf 后,通过使用 cf push 命令部署您的应用程序,并将编译后的 .jar 的路径替换掉。
在推送应用程序之前,请确保已通过 命令行客户端 cf 登录。
以下行显示了使用 cf push 命令部署应用程序的方法:
$ cf push acloudyspringtime -p target/demo-0.0.1-SNAPSHOT.jar
在前面的示例中,我们将 acloudyspringtime 替换为您为应用程序名称提供的任何值 cf。 |
查看 cf push 文档 以获取更多选项。
如果同一目录中存在 Cloud Foundry manifest.yml 文件,则会被视为有效。
此时,cf 开始上传您的应用程序,生成的输出类似于以下示例:
Uploading acloudyspringtime... OK
Preparing to start acloudyspringtime... OK
-----> Downloaded app package (8.9M)
-----> Java Buildpack Version: v3.12 (offline) | https://github.com/cloudfoundry/java-buildpack.git#6f25b7e
-----> Downloading Open Jdk JRE
Expanding Open Jdk JRE to .java-buildpack/open_jdk_jre (1.6s)
-----> Downloading Open JDK Like Memory Calculator 2.0.2_RELEASE from https://java-buildpack.cloudfoundry.org/memory-calculator/trusty/x86_64/memory-calculator-2.0.2_RELEASE.tar.gz (found in cache)
Memory Settings: -Xss349K -Xmx681574K -XX:MaxMetaspaceSize=104857K -Xms681574K -XX:MetaspaceSize=104857K
-----> Downloading Container Certificate Trust Store 1.0.0_RELEASE from https://java-buildpack.cloudfoundry.org/container-certificate-trust-store/container-certificate-trust-store-1.0.0_RELEASE.jar (found in cache)
Adding certificates to .java-buildpack/container_certificate_trust_store/truststore.jks (0.6s)
-----> Downloading Spring Auto Reconfiguration 1.10.0_RELEASE from https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-1.10.0_RELEASE.jar (found in cache)
Checking status of app 'acloudyspringtime'...
0 of 1 instances running (1 starting)
...
0 of 1 instances running (1 starting)
...
0 of 1 instances running (1 starting)
...
1 of 1 instances running (1 running)
App started
恭喜!应用程序现已上线!
应用程序上线后,您可以使用 cf apps 命令来验证已部署应用程序的状态,如下例所示:
$ cf apps
Getting applications in ...
OK
name requested state instances memory disk urls
...
acloudyspringtime started 1/1 512M 1G acloudyspringtime.cfapps.io
...
一旦 Cloud Foundry 确认您的应用程序已部署,您应该能够在给定的 URI 上找到该应用程序。
在前面的示例中,您可以在 https://acloudyspringtime.cfapps.io/ 找到它。
绑定到服务
默认情况下,有关正在运行的应用程序的元数据以及服务连接信息会作为环境变量暴露给应用程序(例如:$VCAP_SERVICES)。
这一架构决策是由于 Cloud Foundry 的多语言特性(任何语言和平台都可以作为 buildpack 得到支持)。
进程范围的环境变量与语言无关。
环境变量并不总能提供最易用的 API,因此 Spring Boot 会自动提取它们,并将数据扁平化为可通过 Spring 的 Environment 抽象访问的属性,如下例所示:
-
Java
-
Kotlin
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class MyBean implements EnvironmentAware {
private String instanceId;
@Override
public void setEnvironment(Environment environment) {
this.instanceId = environment.getProperty("vcap.application.instance_id");
}
// ...
}
import org.springframework.context.EnvironmentAware
import org.springframework.core.env.Environment
import org.springframework.stereotype.Component
@Component
class MyBean : EnvironmentAware {
private var instanceId: String? = null
override fun setEnvironment(environment: Environment) {
instanceId = environment.getProperty("vcap.application.instance_id")
}
// ...
}
所有 Cloud Foundry 属性均以 vcap 为前缀。
您可以使用 vcap 属性来访问应用程序信息(例如应用程序的公共 URL)和服务信息(例如数据库凭据)。
有关完整详细信息,请参阅 CloudFoundryVcapEnvironmentPostProcessor API 文档。
| Java CFEnv 项目更适合用于配置 DataSource 等任务。 |
Kubernetes
Spring Boot 通过检查环境中是否存在 "*_SERVICE_HOST" 和 "*_SERVICE_PORT" 变量来自动检测 Kubernetes 部署环境。
你可以使用 spring.main.cloud-platform 配置属性来覆盖此检测。
Spring Boot 帮助您 管理应用程序的状态 并通过 使用 Actuator 的 HTTP Kubernetes 探针导出它。
Kubernetes 容器生命周期
当Kubernetes删除一个应用实例时,关闭过程涉及多个子系统同时进行:关闭钩子、服务注销、将实例从负载均衡器中移除…… 由于此关闭处理是并行发生的(并且由于分布式系统的特性),在一段时间内,流量可能会被路由到已经开始关闭处理的Pod。
你可以在 preStop 处理程序中配置一个睡眠执行,以避免请求被路由到已经开始关闭的 Pod。 这个睡眠时间应该足够长,以确保新请求不再被路由到该 Pod,其持续时间会根据不同的部署而变化。 延迟应至少与处理当前进行中的请求所需最长时间相同。 你不应仅依赖 Spring Boot 的优雅关闭周期,因为在应用程序关闭期间,平台将无法获取任何存活状态数据。
如果您使用的是 Kubernetes 1.32 或更高版本,可以通过在 Pod 的配置文件中使用 PodSpec 来配置 preStop 处理程序,如下所示:
spec:
containers:
- name: "example-container"
image: "example-image"
lifecycle:
preStop:
sleep:
seconds: 10
如果您尚未使用 Kubernetes 1.32,可以使用 exec 命令来调用 sleep。
spec:
containers:
- name: "example-container"
image: "example-image"
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"]
| 容器需要具备 shell 才能正常工作。 |
预停止钩子完成之后,将向容器发送SIGTERM,并开始优雅关闭,以允许任何剩余的请求完成。
当 Kubernetes 向 Pod 发送 SIGTERM 信号时,它会等待一段指定的时间,称为终止宽限期(默认为 30 秒)。
如果在宽限期结束后容器仍在运行,它们将收到 SIGKILL 信号并被强制移除。
如果 Pod 的关闭时间超过 30 秒(这可能是因为你增加了 spring.lifecycle.timeout-per-shutdown-phase),请确保通过在 Pod YAML 中设置 terminationGracePeriodSeconds 选项来增加终止宽限期。 |
Heroku
Heroku 是另一个流行的 PaaS 平台。
要自定义 Heroku 构建,您需要提供一个 Procfile,它提供了部署应用程序所需的指令。
Heroku 会为 Java 应用程序分配一个 port,然后确保到外部 URI 的路由正常工作。
您必须将应用程序配置为在正确的端口上监听。
以下示例显示了我们的入门级 REST 应用程序的 Procfile:
web: java -Dserver.port=$PORT -jar target/demo-0.0.1-SNAPSHOT.jar
Spring Boot 会将 -D 参数作为可以从 Spring Environment 实例访问的属性提供。
server.port 配置属性会被传递给嵌入式的 Tomcat 或 Jetty 实例,该实例在启动时会使用此端口。
$PORT 环境变量是由 Heroku PaaS 分配给我们的。
这应该能满足您的所有需求。
Heroku 部署最常见的部署工作流是将代码git push到生产环境,如下例所示:
$ git push heroku main
这将导致以下结果:
Initializing repository, done.
Counting objects: 95, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (78/78), done.
Writing objects: 100% (95/95), 8.66 MiB | 606.00 KiB/s, done.
Total 95 (delta 31), reused 0 (delta 0)
-----> Java app detected
-----> Installing OpenJDK... done
-----> Installing Maven... done
-----> Installing settings.xml... done
-----> Executing: mvn -B -DskipTests=true clean install
[INFO] Scanning for projects...
Downloading: https://repo.spring.io/...
Downloaded: https://repo.spring.io/... (818 B at 1.8 KB/sec)
....
Downloaded: https://s3pository.heroku.com/jvm/... (152 KB at 595.3 KB/sec)
[INFO] Installing /tmp/build_0c35a5d2-a067-4abc-a232-14b1fb7a8229/target/...
[INFO] Installing /tmp/build_0c35a5d2-a067-4abc-a232-14b1fb7a8229/pom.xml ...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 59.358s
[INFO] Finished at: Fri Mar 07 07:28:25 UTC 2014
[INFO] Final Memory: 20M/493M
[INFO] ------------------------------------------------------------------------
-----> Discovering process types
Procfile declares types -> web
-----> Compressing... done, 70.4MB
-----> Launching... done, v6
https://agile-sierra-1405.herokuapp.com/ deployed to Heroku
To [email protected]:agile-sierra-1405.git
* [new branch] main -> main
您的应用程序现在应该已在 Heroku 上运行。 有关更多详细信息,请参阅 将 Spring Boot 应用程序部署到 Heroku。
OpenShift
OpenShift 提供了许多描述如何部署 Spring Boot 应用程序的资源,包括:
亚马逊云科技 (AWS)
Amazon Web Services 为运行基于 Spring Boot 的应用程序提供了多种选项,这些选项可以是容器、传统的 Web 应用程序(war)或自包含的可执行 jar 文件。 流行的选项包括:
-
亚马逊弹性容器服务 (ECS)
-
AWS Elastic Beanstalk
Amazon Elastic Container Service (ECS)
官方 Amazon ECS 开发者指南 提供了该平台功能的全面概述,并包含 入门指南,引导您完成启动容器所需的步骤。
| Spring Boot 应用程序可以使用在 容器镜像 中描述的技术打包到 Docker 容器中。 |
除了开发者指南之外,AWS 还提供了用于通过 AWS Fargate 在 Amazon ECS 上部署容器化 Java 服务的 主题指南。
Spring Boot 通过检查环境中的 AWS_EXECUTION_ENV 变量来自动检测 AWS ECS 部署环境。
您可以使用 spring.main.cloud-platform 配置属性覆盖此检测。 |
AWS Elastic Beanstalk
如官方 Elastic Beanstalk Java 指南 所述,部署 Java 应用程序有两种主要选项。 您可以使用“Tomcat 平台”或“Java SE 平台”。
使用 Tomcat 平台
此选项适用于生成 war 文件的 Spring Boot 项目。 遵循官方指南和 在 Tomcat 上使用 Java 教程。
| 为Spring Boot应用程序创建可部署的war文件在传统部署中进行了描述。 |
CloudCaptain 与 Amazon Web Services
CloudCaptain 通过将您的 Spring Boot 可执行 jar 或 war 包转换为最小的 VM 镜像来工作,该镜像可以原封不动地部署在 VirtualBox 或 AWS 上。 CloudCaptain 提供与 Spring Boot 的深度集成,并利用您 Spring Boot 配置文件中的信息自动配置端口和健康检查 URL。 CloudCaptain 利用这些信息来生成镜像,以及配置其预置的所有资源(实例、安全组、弹性负载均衡器等)。
在您创建了一个 CloudCaptain 账户,将其连接到您的 AWS 账户,安装了最新版本的 CloudCaptain 客户端,并确保应用程序已通过 Maven 或 Gradle 构建(例如使用 mvn clean package),您可以使用类似的命令将您的 Spring Boot 应用程序部署到 AWS:
$ boxfuse run myapp-1.0.jar -env=prod
查看 boxfuse run 文档 以获取更多选项。
如果当前目录中存在 boxfuse.conf 文件,则会被认为是有效的。
默认情况下,CloudCaptain 在启动时会激活一个名为 boxfuse 的 Spring 配置文件。
如果您的可执行 jar 或 war 包含一个 application-boxfuse.properties 文件,CloudCaptain 将基于其中包含的属性进行配置。 |
此时,CloudCaptain 为您的应用程序创建镜像,上传该镜像,并在 AWS 上配置和启动所需的资源,生成的输出类似于以下示例:
Fusing Image for myapp-1.0.jar ...
Image fused in 00:06.838s (53937 K) -> axelfontaine/myapp:1.0
Creating axelfontaine/myapp ...
Pushing axelfontaine/myapp:1.0 ...
Verifying axelfontaine/myapp:1.0 ...
Creating Elastic IP ...
Mapping myapp-axelfontaine.boxfuse.io to 52.28.233.167 ...
Waiting for AWS to create an AMI for axelfontaine/myapp:1.0 in eu-central-1 (this may take up to 50 seconds) ...
AMI created in 00:23.557s -> ami-d23f38cf
Creating security group boxfuse-sg_axelfontaine/myapp:1.0 ...
Launching t2.micro instance of axelfontaine/myapp:1.0 (ami-d23f38cf) in eu-central-1 ...
Instance launched in 00:30.306s -> i-92ef9f53
Waiting for AWS to boot Instance i-92ef9f53 and Payload to start at https://52.28.235.61/ ...
Payload started in 00:29.266s -> https://52.28.235.61/
Remapping Elastic IP 52.28.233.167 to i-92ef9f53 ...
Waiting 15s for AWS to complete Elastic IP Zero Downtime transition ...
Deployment completed successfully. axelfontaine/myapp:1.0 is up and running at https://myapp-axelfontaine.boxfuse.io/
您的应用程序现在应该在 AWS 上运行。
查看博客文章 在EC2上部署Spring Boot应用 以及 CloudCaptain Spring Boot集成文档,以开始使用Maven构建运行该应用。
Azure
此 入门指南 将引导您将 Spring Boot 应用程序部署到 Azure Spring Cloud 或 Azure 应用服务。
Google Cloud
Google Cloud 有几种选项可用于启动 Spring Boot 应用程序。 最易于入门的是 App Engine,但你也可以找到在 Container Engine 中以容器方式运行 Spring Boot 的方法,或者在 Compute Engine 的虚拟机上运行。
将您的第一个应用部署到 App Engine 标准环境,请按照 此教程 操作。
另外,App Engine Flex 要求您创建一个 app.yaml 文件来描述您的应用所需的资源。
通常,您将此文件放在 src/main/appengine 中,它应类似于以下文件:
service: "default"
runtime: "java17"
env: "flex"
handlers:
- url: "/.*"
script: "this field is required, but ignored"
manual_scaling:
instances: 1
health_check:
enable_health_check: false
env_variables:
ENCRYPT_KEY: "your_encryption_key_here"
您可以通过在构建配置中添加项目 ID 来部署应用程序(例如,使用 Maven 插件),如下例所示:
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>2.4.4</version>
<configuration>
<project>myproject</project>
</configuration>
</plugin>
然后使用 mvn appengine:deploy 进行部署(您需要先进行身份验证,否则构建会失败)。