部署到云平台
部署到云端
Spring Boot 的可执行 jar 文件已为大多数流行的云 PaaS(Platform-as-a-Service)提供商做好了准备。这些提供商通常要求你“自带容器”。它们管理的是应用程序进程(而非专门针对 Java 应用程序),因此需要一个中间层,将你的应用程序适配到云平台对运行中进程的概念。
两个流行的云提供商 Heroku 和 Cloud Foundry 采用了“buildpack”方法。buildpack 会将你部署的代码包装在启动应用程序所需的任何内容中。这可能是一个 JDK 和对 java 的调用、一个嵌入式 Web 服务器,或者一个功能完备的应用服务器。buildpack 是可插拔的,但理想情况下,你应该尽可能少地对其进行自定义。这样可以减少不受你控制的功能所占的比重,并最大限度地减小开发环境与生产环境之间的差异。
理想情况下,你的应用程序(例如 Spring Boot 可执行 jar)已将运行所需的所有内容打包在其中。
在本节中,我们将探讨如何将“入门”部分中开发的应用程序部署并运行在云端。
Cloud Foundry
Cloud Foundry 提供了默认的 buildpack,当未指定其他 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 访问该应用程序。在前面的示例中,您可以通过 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 删除一个应用实例时,关闭过程会同时涉及多个子系统:shutdown hooks、服务注销、从负载均衡器中移除实例……由于这种关闭处理是并行进行的(并且由于分布式系统的特性),存在一个时间窗口,在此期间流量仍可能被路由到已经开始关闭处理的 Pod。
你可以在 preStop 处理程序中配置一个 sleep 执行,以避免请求被路由到已经开始关闭的 Pod。该 sleep 时间应足够长,以确保新请求不再被路由到该 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 hook 执行完成,SIGTERM 将被发送到容器,优雅关闭 将开始,允许所有剩余的飞行中请求完成。
当 Kubernetes 向 Pod 发送 SIGTERM 信号时,它会等待一段称为终止宽限期(termination grace period)的指定时间(默认为 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)或自包含的可执行 jar 文件。常见选项包括:
-
Amazon Elastic Container Service (ECS)
-
AWS Elastic Beanstalk
Amazon Elastic Container Service (ECS)
官方的 Amazon ECS 开发者指南 提供了对该平台功能的全面概述,并包含了一份 入门指南,引导您完成让容器启动并运行所需的步骤。
Spring Boot 应用程序可以使用 Container Images 中描述的技术打包到 Docker 容器中。
除了开发者指南外,AWS 还提供了一份主题指南,用于指导如何使用 AWS Fargate 在 Amazon ECS 上部署容器化的 Java 服务。
AWS Elastic Beanstalk
如官方 Elastic Beanstalk Java 指南 中所述,部署 Java 应用程序主要有两种选项:可以使用 “Tomcat Platform” 或 “Java SE platform”。
使用 Tomcat 平台
此选项适用于生成 war 文件的 Spring Boot 项目。请遵循官方指南和 Java on Tomcat 教程。
为 Spring Boot 应用程序创建可部署的 war 文件的方法在 Traditional Deployment 中有描述。
使用 Java SE Platform
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 的虚拟机上运行。
要将您的第一个应用部署到 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 进行部署(你需要先进行身份验证,否则构建会失败)。