部署到雲

Spring Boot 的可執行 jar 包已為大多數流行的雲 PaaS(平臺即服務)提供商做好準備。這些提供商通常要求你“自帶容器”。它們管理應用程序(而非特指 Java 應用),因此需要一箇中間層,將你的應用適配到雲對執行程序的概念。

兩個流行的雲提供商,Heroku 和 Cloud Foundry,採用“構建包”方法。構建包將你部署的程式碼包裹在啟動應用所需的一切之中。這可能是一個 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 配置屬性覆蓋此檢測。

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 的路由工作正常。

你必須配置你的應用監聽正確的埠。以下示例展示了我們的 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 [email protected]: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 平臺”。

使用 Tomcat 平臺

此選項適用於生成 war 檔案的 Spring Boot 專案。無需特殊配置。你只需要遵循官方指南即可。

使用 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 環境是負載均衡的。負載均衡器會產生顯著的成本。為了避免該成本,請將環境型別設定為“Single instance”(單例項),如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 CloudAzure App Service

Google Cloud

Google Cloud 提供多種選項來啟動 Spring Boot 應用。最容易上手可能是 App Engine,但你也可以找到方法在 Container Engine 中透過容器或在 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 進行部署(你需要先進行認證,否則構建會失敗)。