部署到雲端

Spring Boot 的可執行 jar 適用於大多數熱門的雲端 PaaS(平台即服務)供應商。這些供應商通常要求您「自備容器」。他們管理應用程式流程(並非專指 Java 應用程式),因此需要一個中間層來調整*您的*應用程式以適應*雲端*對於執行中流程的概念。

兩個熱門的雲端供應商 Heroku 和 Cloud Foundry 採用「建置包」方法。建置包會將您部署的程式碼包裝在*啟動*應用程式所需的任何內容中。它可能是一個 JDK 和一個對 `java` 的呼叫,一個嵌入式網頁伺服器,或是一個完整的應用程式伺服器。建置包是可插拔的,但理想情況下,您應該盡可能減少對它的客製化。這減少了不在您控制範圍內的功能的影響,並將開發環境和生產環境之間的差異降至最低。

理想情況下,您的應用程式,就像 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` Javadoc。

對於配置 DataSource 等任務,Java CFEnv 專案更為合適。

Kubernetes

Spring Boot 會透過檢查環境中是否存在 `"*_SERVICE_HOST"` 和 `"*_SERVICE_PORT"` 變數來自動偵測 Kubernetes 部署環境。您可以使用 `spring.main.cloud-platform` 設定屬性覆寫此偵測。

Spring Boot 協助您管理應用程式的狀態,並使用Actuator 的 HTTP Kubernetes 探針將其匯出。

Kubernetes 容器生命週期

當 Kubernetes 刪除應用程式執行個體時,關閉程序會同時涉及多個子系統:關閉鉤子、取消註冊服務、從負載平衡器中移除執行個體⋯⋯。由於此關閉處理是並行進行的(並且由於分散式系統的特性),因此存在一個時間區段,在此期間,流量可能會被路由到也已開始關閉處理的 Pod。

您可以在 preStop 處理程式中設定睡眠執行,以避免將請求路由到已開始關閉的 Pod。此睡眠時間應足以讓新的請求停止路由到 Pod,其持續時間會因部署而異。可以使用 Pod 設定檔中的 PodSpec 來設定 preStop 處理程式,如下所示:

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

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 會指派一個 port 供 Java 應用程式使用,然後確保路由到外部 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 [email protected]:agile-sierra-1405.git
 * [new branch]      main -> main

您的應用程式現在應該已在 Heroku 上啟動並運行。如需更多詳細資訊,請參閱將 Spring Boot 應用程式部署到 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 指南 中所述,部署 Java 應用程式主要有兩種選項。您可以使用「Tomcat 平台」或「Java SE 平台」。

使用 Tomcat 平台

此選項適用於產生 war 檔案的 Spring Boot 專案。無需特殊設定。您只需按照官方指南操作即可。

使用 Java SE 平台

此選項適用於產生 jar 檔案並執行嵌入式 Web 容器的 Spring Boot 專案。Elastic Beanstalk 環境會在連接埠 80 上執行 nginx 實例,以代理實際應用程式(在連接埠 5000 上執行)。要設定它,請將以下行添加到您的 application.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 環境預設會啟用負載平衡。但負載平衡器會產生顯著的成本。為了避免此成本,請將環境類型設定為「單一執行個體」,如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 用戶端,並確保應用程式已由 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 會為您的應用程式建立映像檔、上傳映像檔,並在 AWS 上設定和啟動必要的資源, resulting in output similar to the following example 產生類似以下範例的輸出:

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 進行部署(您需要先進行驗證,否則建置會失敗)。