Deploying to the Cloud

Spring Boot 的可执行 jar 已针对大多数流行的云平台即服务 (PaaS) 供应商做好准备。这些供应商倾向于要求你 “bring your own container”。它们管理应用程序进程(具体来说不是 Java 应用程序),因此它们需要一个中间层,该层将 your 应用程序调整为 cloud’s 的正在运行的进程概念。 两个流行的云供应商 Heroku 和 Cloud Foundry 采用 “buildpack” 方法。构建包将部署的代码包装在为 start 应用程序所需的一切内容中。它可能是 JDK 和对 java 的调用、嵌入式 Web 服务器或成熟的应用程序服务器。构建包是可插拔的,但理想情况下你应该能够用尽可能少的自定义来完成它。这减少了不受你控制的功能的使用量。它最大程度地减少了开发环境和生产环境之间的差异。 理想情况下,您的应用程序(例如 Spring Boot 可执行 jar 文件)拥有在其中运行所需的一切。 在本节中,我们了解了如何让 “Getting Started” 部分中的 application that we developed 在云中启动并运行。

Cloud Foundry

Cloud Foundry 提供了在未指定其他构建包时发挥作用的默认构建包。Cloud Foundry Java buildpack 非常支持 Spring 应用程序,包括 Spring Boot。您可以部署独立的可执行 jar 应用程序以及传统的 .war 打包应用程序。

在构建应用程序(例如,使用 mvn clean package)并获得 installed the cf command line tool 之后,使用 cf push 命令部署应用程序,替换为已编译 .jar 的路径。务必在推送应用程序之前拥有 logged in with your cf command line client。以下行显示使用 cf push 命令部署应用程序:

$ cf push acloudyspringtime -p target/demo-0.0.1-SNAPSHOT.jar

在前面的示例中,我们用 acloudyspringtime 替换您作为应用程序名称给定的值 cf

请参阅 cf push documentation 了解更多选项。如果在同一目录中存在 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/ 中找到它。

Binding to Services

默认情况下,有关正在运行的应用程序的元数据以及服务连接信息会作为环境变量公开给应用程序(例如:$VCAP_SERVICES)。这种架构决策是由于 Cloud Foundry 的多语言(任何语言和平台都可以作为构建包得到支持)性质。进程范围的环境变量与语言无关。

环境变量并不总是最简单的 API,因此 Spring Boot 会自动提取它们并将数据展平到可以通过 Spring 的 Environment 抽象访问的属性中,如下面的示例所示:

所有 Cloud Foundry 的属性都以 vcap 为前缀。您可以使用 vcap 属性来访问应用程序信息(例如应用程序的公共 URL)和服务信息(例如数据库凭证)。有关完整详细信息,请参阅 CloudFoundryVcapEnvironmentPostProcessor Javadoc。

Java CFEnv 项目更适合配置 DataSource 等任务。

Kubernetes

Spring Boot 通过检查环境中的 "*_SERVICE_HOST""*_SERVICE_PORT" 变量自动检测 Kubernetes 部署环境。您可以使用 configprop:spring.main.cloud-platform[] 配置属性覆盖此检测。

Spring Boot 可以通过 manage the state of your application 帮助您在 HTTP Kubernetes Probes using Actuator 中包装和导出它。

Kubernetes Container Lifecycle

当 Kubernetes 删除应用程序实例时,关闭过程同时涉及多个子系统:关闭挂钩、注销服务、从负载平衡器中移除实例……由于这种关闭处理并行发生(并且由于分布式系统的性质),在该窗口期间,流量可能会路由到一个已经开始其关闭处理的 Pod。

您可以在 preStop 处理程序中配置一个睡眠执行,以避免将请求路由到已经开始关闭的 Pod。此睡眠时间应足够长,让新的请求停止路由到 Pod,并且其持续时间会根据部署而异。preStop 处理程序可以通过按照如下方法在 Pod 的配置文件中使用 PodSpec 进行配置:

spec:
  containers:
  - name: "example-container"
    image: "example-image"
    lifecycle:
      preStop:
        exec:
          command: ["sh", "-c", "sleep 10"]

预停止挂钩完成后,将向容器发送 SIGTERM 并 graceful shutdown 开始,允许任何剩余的在进行中的请求完成。

当 Kubernetes 向 Pod 发送 SIGTERM 信号时,它会等待一个称为终止宽限期的指定时间(默认值为 30 秒)。如果容器在宽限期后仍在运行,则会向它们发送 SIGKILL 信号并强制删除。如果 Pod 需要超过 30 秒才能关闭,这可能是因为您增加了 configprop:spring.lifecycle.timeout-per-shutdown-phase[],请确保通过在 Pod YAML 中设置 terminationGracePeriodSeconds 选项来增加终止宽限期。

Heroku

Heroku 是另一个流行的 PaaS 平台。要定制 Heroku 构建,您提供一个 Procfile,它提供了部署应用程序所需的咒语。Heroku 为要使用的 Java 应用程序分配一个 port,然后确保路由到外部 URI 正常工作。

您必须配置您的应用程序以侦听正确的端口。以下示例显示我们 starter 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

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 Tomcat Platform

此选项适用于生成 war 文件的 Spring Boot 项目。不需要特殊配置。您只需要遵循官方指南。

Using the Java SE Platform

此选项适用于生成 jar 文件并运行嵌入式 Web 容器的 Spring Boot 项目。Elastic Beanstalk 环境在端口 80 上运行 nginx 实例,以代理实际应用程序,该应用程序在端口 5000 上运行。要配置它,请在您的 application.properties 文件中添加以下行:

server:
  port: 5000
Upload binaries instead of sources

默认情况下,Elastic Beanstalk 会上传源文件并在 AWS 中编译它们。但是,最好上传二进制文件。为此,在您的 .elasticbeanstalk/config.yml 文件中添加类似以下内容的行:

deploy:
	artifact: target/demo-0.0.1-SNAPSHOT.jar
Reduce costs by setting the environment type

默认情况下,Elastic Beanstalk 环境会被平衡负载。负载均衡器有着非常大的成本。若要避免这种成本,请将环境类型设置为 “Single instance”,如 the Amazon documentation 所述。你还可以使用 CLI 和以下命令创建单一实例环境:

eb create -s

Summary

这是进入 AWS 的最简单方法之一,但还有更多内容需要介绍,比如如何将 Elastic Beanstalk 集成到任何 CI/CD 工具中,使用 Elastic Beanstalk Maven 插件代替 CLI,等等。有一个 blog post 更详细地涵盖这些主题。

CloudCaptain and Amazon Web Services

CloudCaptain 通过将 Spring Boot 可执行 jar 或 war 转换成一个最小化 VM 映像,该映像可以在 VirtualBox 或 AWS 上部署而不做任何更改。CloudCaptain 与 Spring Boot 深度集成,并使用 Spring Boot 配置文件中的信息来自动配置端口和运行状况检查 URL。CloudCaptain 将此信息用于它生成的映像,以及用于它配置的所有资源(比如实例、安全组、弹性负载均衡器)。

一旦你创建一个 CloudCaptain account,将其连接到你的 AWS 账户,安装 CloudCaptain Client 的最新版本,并确保应用程序已经由 Maven 或 Gradle 构建(比如使用 mvn clean package),就可以使用类似于以下命令将你的 Spring Boot 应用程序部署到 AWS:

$ boxfuse run myapp-1.0.jar -env=prod

请参见 boxfuse run documentation 了解更多选项。如果当前目录中存在 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 上启动并运行。

请参见 deploying Spring Boot apps on EC2 上的博客文章,以及 documentation for the CloudCaptain Spring Boot integration,以使用 Maven 构建开始运行该应用程序。

Azure

Getting Started guide 指导你将 Spring Boot 应用程序部署到 Azure Spring CloudAzure App Service

Google Cloud

Google Cloud 有很多选项可以用来启动 Spring Boot 应用程序。最容易上手的可能是 App Engine,但你还可以找到一些方法在装有容器引擎的容器中或在装有计算引擎的虚拟机上运行 Spring Boot。

要将你的第一个应用程序部署到 App Engine 标准环境,请遵循 this tutorial

或者,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 部署(此之前需要验证,否则,构建会失败)。