跳到主要内容

部署到云端

DeepSeek V3 中英对照 Deploying to the Cloud

部署到云端

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
shell
备注

在前面的示例中,我们将 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
none

恭喜!应用程序现已上线!

在你的应用程序上线后,你可以使用 cf apps 命令来验证已部署应用程序的状态,如下例所示:

$ cf apps
Getting applications in ...
OK

name requested state instances memory disk urls
...
acloudyspringtime started 1/1 512M 1G acloudyspringtime.cfapps.io
...
shell

一旦 Cloud Foundry 确认您的应用程序已部署完成,您应该能够在给定的 URI 中找到该应用程序。在前面的示例中,您可以在 https://acloudyspringtime.cfapps.io/ 找到它。

绑定到服务

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

环境变量并不总是提供最便捷的 API,因此 Spring Boot 会自动提取它们,并将数据扁平化为可以通过 Spring 的 Environment 抽象访问的属性,如下例所示:

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");
}

// ...

}
java

所有 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,其持续时间因部署而异。preStop 处理程序可以通过在 Pod 的配置文件中使用 PodSpec 来配置,如下所示:

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

一旦 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
none

Spring Boot 将 -D 参数作为属性提供,这些属性可以通过 Spring 的 Environment 实例访问。server.port 配置属性被传递给嵌入的 Tomcat、Jetty 或 Undertow 实例,这些实例在启动时会使用该端口。$PORT 环境变量是由 Heroku PaaS 分配给我们的。

这应该包含了你所需的一切。对于 Heroku 部署来说,最常见的部署工作流程是将代码 git push 到生产环境,如下例所示:

$ git push heroku main
shell

这将导致以下结果:

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
none

你的应用程序现在应该在 Heroku 上运行了。更多详细信息,请参阅将 Spring Boot 应用程序部署到 Heroku

OpenShift 部署指南

OpenShift 提供了许多资源,描述了如何部署 Spring Boot 应用程序,包括:

亚马逊网络服务(AWS)

Amazon Web Services 提供了多种方式来安装基于 Spring Boot 的应用程序,既可以作为传统的 Web 应用程序(war),也可以作为带有嵌入式 Web 服务器的可执行 jar 文件。可选的安装方式包括:

  • AWS Elastic Beanstalk

  • AWS CodeDeploy

  • AWS OPSWorks

  • AWS CloudFormation

  • AWS 容器注册表

每种服务都有不同的功能和定价模型。在本文档中,我们将介绍如何使用 AWS Elastic Beanstalk。

AWS Elastic Beanstalk

正如官方 Elastic Beanstalk Java 指南 中所述,部署 Java 应用程序有两种主要选择。您可以使用“Tomcat 平台”或“Java SE 平台”。

使用 Tomcat 平台

该选项适用于生成 war 文件的 Spring Boot 项目。无需特殊配置,您只需按照官方指南操作即可。

使用 Java SE 平台

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

server.port=5000
properties
提示

上传二进制文件而非源代码

默认情况下,Elastic Beanstalk 会上传源代码并在 AWS 中进行编译。然而,最佳实践是直接上传二进制文件。为此,请在你的 .elasticbeanstalk/config.yml 文件中添加类似以下内容的行:

deploy:
artifact: target/demo-0.0.1-SNAPSHOT.jar
xml
提示

通过设置环境类型降低成本

默认情况下,Elastic Beanstalk 环境是负载均衡的。负载均衡器会带来显著的成本。为了避免这一成本,可以将环境类型设置为“单实例”,如 Amazon 文档 中所述。你也可以通过使用 CLI 和以下命令来创建单实例环境:

eb create -s
none

总结

这是访问 AWS 最简单的方式之一,但还有更多内容需要介绍,例如如何将 Elastic Beanstalk 集成到任何 CI / CD 工具中,使用 Elastic Beanstalk Maven 插件代替 CLI 等。有一篇博客文章更详细地涵盖了这些主题。

CloudCaptain 与 Amazon Web Services

CloudCaptain 的工作原理是将你的 Spring Boot 可执行 jar 或 war 文件转换为一个最小的虚拟机镜像,该镜像可以原封不动地部署在 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
shell

请参阅 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/
none

你的应用程序现在应该在 AWS 上运行了。

请参阅关于在 EC2 上部署 Spring Boot 应用的博客文章,以及CloudCaptain Spring Boot 集成的文档,以获取使用 Maven 构建并运行应用程序的入门指南。

Azure

这篇入门指南将引导您将 Spring Boot 应用程序部署到 Azure Spring CloudAzure 应用服务

Google 云

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"
yaml

你可以通过将项目 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>
xml

然后使用 mvn appengine:deploy 进行部署(你需要先进行身份验证,否则构建会失败)。