部署到云平台
Spring Boot 的可执行 jar 文件已经为大多数流行的云 PaaS(平台即服务)提供商做好了准备。 这些提供商通常要求您"自带容器"。 他们管理应用程序进程(而不是专门管理 Java 应用程序),因此需要一个中间层来将您的应用程序适配到云平台对运行进程的概念。
两个流行的云提供商,Heroku 和 Cloud Foundry,采用"构建包"(buildpack)方法。
构建包将您的部署代码包装在启动应用程序所需的任何内容中。
它可能是一个 JDK 和一个 java
调用,一个嵌入式 Web 服务器,或者一个完整的应用程序服务器。
构建包是可插拔的,但理想情况下,您应该尽可能少地对其进行自定义。
这减少了不在您控制范围内的功能占用空间。
它最小化了开发和生产环境之间的差异。
理想情况下,您的应用程序(如 Spring Boot 可执行 jar)包含运行所需的所有内容。
在本节中,我们将了解如何将 我们在"入门"部分开发的应用程序在云平台上运行。
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 的多语言(任何语言和平台都可以作为构建包支持)性质。
进程范围的环境变量是语言无关的。
环境变量并不总是提供最简单的 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 Probes导出它。
Kubernetes 容器生命周期
当 Kubernetes 删除应用程序实例时,关闭过程涉及多个子系统并发:关闭钩子、注销服务、从负载均衡器中移除实例… 由于这种关闭处理是并行发生的(并且由于分布式系统的性质),存在一个时间窗口,在此期间流量可能被路由到已经开始其关闭处理的 pod。
您可以在 preStop 处理程序中配置睡眠执行,以避免请求被路由到已经开始关闭的 pod。 这个睡眠时间应该足够长,以便新请求停止被路由到 pod,其持续时间将因部署而异。
如果您使用的是 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 才能工作。
一旦 pre-stop 钩子完成,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 或 Undertow 实例,然后它在启动时使用该端口。
$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 git@heroku.com:agile-sierra-1405.git
* [new branch] main -> main
您的应用程序现在应该在 Heroku 上运行了。 有关更多详细信息,请参阅 将 Spring Boot 应用程序部署到 Heroku。
OpenShift
OpenShift 有许多资源描述如何部署 Spring Boot 应用程序,包括:
Amazon Web Services (AWS)
Amazon Web Services 提供了多种方式来安装基于 Spring Boot 的应用程序,既可以作为传统 Web 应用程序(war),也可以作为带有嵌入式 Web 服务器的可执行 jar 文件。 选项包括:
-
AWS Elastic Beanstalk
-
AWS Code Deploy
-
AWS OPS Works
-
AWS Cloud Formation
-
AWS Container Registry
每个都有不同的功能和定价模型。 在本文档中,我们描述了使用 AWS Elastic Beanstalk 的方法。
AWS Elastic Beanstalk
如官方 Elastic Beanstalk Java 指南中所述,部署 Java 应用程序有两个主要选项。 您可以使用"Tomcat 平台"或"Java SE 平台"。
使用 Java SE 平台
此选项适用于生成 jar 文件并运行嵌入式 Web 容器的 Spring Boot 项目。
Elastic Beanstalk 环境在端口 80 上运行 nginx 实例,以代理在端口 5000 上运行的实际应用程序。
要配置它,请在 application.properties
文件中添加以下行:
-
Properties
-
YAML
server.port=5000
server:
port: 5000
默认情况下,Elastic Beanstalk 上传源代码并在 AWS 中编译它们。
但是,最好上传二进制文件。
为此,请在 .elasticbeanstalk/config.yml
文件中添加类似于以下内容的行:
deploy:
artifact: target/demo-0.0.1-SNAPSHOT.jar
默认情况下,Elastic Beanstalk 环境是负载均衡的。 负载均衡器有显著的成本。 要避免这种成本,请将环境类型设置为"单实例",如 Amazon 文档中所述。 您还可以使用 CLI 和以下命令创建单实例环境:
eb create -s
总结
这是进入 AWS 的最简单方法之一,但还有更多内容需要涵盖,例如如何将 Elastic Beanstalk 集成到任何 CI/CD 工具中,使用 Elastic Beanstalk Maven 插件而不是 CLI,等等。 有一篇 博客文章更详细地介绍了这些主题。
CloudCaptain 和 Amazon Web Services
CloudCaptain 通过将您的 Spring Boot 可执行 jar 或 war 转换为最小的 VM 镜像来工作,该镜像可以不变地部署在 VirtualBox 或 AWS 上。 CloudCaptain 具有对 Spring Boot 的深度集成,并使用来自 Spring Boot 配置文件的信息自动配置端口和健康检查 URL。 CloudCaptain 利用这些信息来生成镜像以及它配置的所有资源(实例、安全组、弹性负载均衡器等)。
一旦您创建了 CloudCaptain 账户,将其连接到您的 AWS 账户,安装了最新版本的 CloudCaptain Client,并确保应用程序已由 Maven 或 Gradle 构建(例如,使用 mvn clean package
),您就可以使用类似于以下命令将 Spring Boot 应用程序部署到 AWS:
$ boxfuse run myapp-1.0.jar -env=prod
有关更多选项,请参阅 boxfuse run
文档。
如果当前目录中存在 boxfuse.conf
文件,则会考虑该文件。
提示:默认情况下,CloudCaptain 在启动时激活名为 boxfuse
的 Spring profile。
如果您的可执行 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 App Service。
Google Cloud
Google Cloud 有几种选项可用于启动 Spring Boot 应用程序。 最容易上手的是 App Engine,但您也可以找到在 Container Engine 中使用容器运行 Spring Boot 或在 Compute Engine 上使用虚拟机运行 Spring Boot 的方法。
要将您的第一个应用程序部署到 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
部署(您需要先进行身份验证,否则构建将失败)。