部署到云端
Spring Boot 的可执行 jar 已经为大多数流行的云 PaaS(平台即服务)提供商做好了准备。 这些提供商通常要求你“自带容器”。 他们管理应用进程(而不是专门的 Java 应用),因此需要一个中间层,将你的应用适配为云端对运行进程的定义。
两个流行的云服务提供商,Heroku 和 Cloud Foundry,采用了“buildpack”方式。
buildpack 会将你部署的代码包装成启动应用所需的内容。
它可能是一个 JDK 和对 java
的调用、一个嵌入式 Web 服务器,或一个完整的应用服务器。
buildpack 是可插拔的,但理想情况下你应尽量少自定义它。
这样可以减少你无法控制的功能范围,最小化开发和生产环境之间的差异。
理想情况下,你的应用(如 Spring Boot 可执行 jar)应包含运行所需的一切。
本节将介绍如何将“入门”部分开发的 应用 部署到云端。
Cloud Foundry
Cloud Foundry 提供了默认的 buildpacks,如果未指定其他 buildpack,则会进入使用。
Cloud Foundry Java buildpack 对 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 上。
在前面的示例中,你可以找到它 at https://acloudyspringtime.cfapps.io/
。
Binding to Services
默认情况下,运行应用程序的元数据以及服务连接信息作为环境变量暴露给应用程序(例如:$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 文档,了解完整详细信息。
提示:https://github.com/pivotal-cf/java-cfenv/[Java CFEnv] 项目是配置 DataSource 的更好选择。
Kubernetes
Spring Boot 自动检测 Kubernetes 部署环境,通过检查环境中的 "*_SERVICE_HOST"
和 "*_SERVICE_PORT"
变量来实现。
你可以使用 spring.main.cloud-platform
配置属性覆盖此检测。
Spring Boot 帮助你 管理应用程序的状态 并使用 HTTP Kubernetes Probes using Actuator 导出它。
Kubernetes Container Lifecycle
当 Kubernetes 删除应用程序实例时,关闭过程涉及多个子系统并发:关闭钩子、注销服务、从负载均衡器中删除实例… 因为此关闭处理发生在并行(和分布式系统性质)中,所以在关闭过程中存在流量可以路由到正在关闭的 pod 的窗口。
你可以配置一个 sleep 执行在 preStop 处理程序中,以避免请求被路由到已经启动关闭过程的 pod。 此睡眠应足够长,以便新请求停止被路由到 pod 并持续时间将根据部署而变化。
如果你使用 Kubernetes 1.32 或更高版本,你可以使用 PodSpec 在 pod 的配置文件中配置 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 将被发送到容器并 graceful shutdown 将开始,允许任何剩余的 in-flight 请求完成。
注意:当 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 上运行。 有关更多详细信息,请参阅 Deploying Spring Boot Applications to 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 guide,部署 Java 应用程序有两种主要方法。 你可以使用 “Tomcat Platform” 或 “Java SE platform”。
Using the Java SE Platform
此选项适用于生成 jar 文件并运行嵌入式 Web 容器的 Spring Boot 项目。
Elastic Beanstalk 环境在端口 80 上运行 nginx 实例来代理实际应用程序,在端口 5000 上运行。
要配置它,请在 application.properties
文件中添加以下行:
-
Properties
-
YAML
server.port=5000
server:
port: 5000
Upload binaries instead of sources
默认情况下,Elastic Beanstalk 在 AWS 上上传源代码并编译它们。
但是最好上传二进制文件。
为此,请在
|
Reduce costs by setting the environment type
默认情况下,Elastic Beanstalk 环境是负载均衡的。 负载均衡器有显著的成本。 要避免这种成本,请将环境类型设置为 “Single instance”,如 Amazon 文档中所述。 你也可以使用 CLI 和以下命令创建单实例环境:
|
Summary
这是最简单的方法之一,但还有更多需要覆盖的内容,例如如何将 Elastic Beanstalk 集成到任何 CI / CD 工具中,使用 Elastic Beanstalk Maven 插件而不是 CLI,以及其他。 有一个 博客文章详细介绍了这些主题。
CloudCaptain and 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。
此时,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
此 Getting Started guide 带你通过将 Spring Boot 应用程序部署到 Azure Spring Cloud 或 Azure App Service。
Google Cloud
Google Cloud 有许多选项可以用来启动 Spring Boot 应用程序。 最容易开始的是 App 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
(你需要先进行身份验证,否则构建失败)。