Categories
程式開發

Spring Boot教程:構建微服務並部署至Google Cloud


本文要點

  • 組合使用Google Kubernetes Engine(GKE)和Spring Boot,能夠讓我們更加輕鬆快速地搭建微服務。
  • Jib是容器化Java應用的好辦法。借助Maven或Gradle,它能夠讓我們在不使用Docker的情況下,就能創建優化的鏡像。
  • Google的Spring Cloud GCP實現允許開發人員通過很少的配置和某些Spring模式使用Google Cloud Platform(GCP)提供的服務。
  • 使用Cloud Code搭建Skaffold能夠讓開發人員有一個很棒的開發週期。對於初始化一個新的服務原型時,這特別有用。

引言

隨著微服務在我們行業中的日益普及,在如何構建應用方面,相關的技術和平台得到了繁榮發展。有時候,開始該選用哪些技術是很難抉擇的。在本文中,我將會向你展示如何創建一個基於Spring Boot的應用,該應用會用到Google Cloud所提供的一些服務。這些方法我們在Google已經用了很長時間了。我希望它對你有用。

基礎

我們首先來定義一下要做些什麼。我們會從一個非常基礎的基於Spring Boot的Java應用開始。 Spring是一個成熟的框架,借助它,我們能夠快速創建強大且特性豐富的應用。

隨後,我們會對其進行一些修改,使用Jib(能夠在不使用Docker的情況下,為Java應用構建優化的Docker和OCI鏡像)和distroless版本的Java 11容器化該應用。 Jib能夠與Maven和Gradle協作,在樣例中我們會使用Maven。

接下來,我們會創建一個Google Cloud Platform(GCP)項目,並藉助Spring Cloud GCP來使用Cloud Firestore的功能。 Spring Cloud GCP允許基於Spring的應用便利地消費Google服務,如數據庫(Cloud Firestore、Cloud Spanner甚至Cloud SQL)、Google Cloud Pub/Sub以及用於日誌和跟踪的Stackdriver等。

在此之後,我們將會對應用做一些修改,將其部署到Google Kubernetes Engine(GKE)。 GKE是一個託管的、生產就緒的環境,用於部署容器化的、基於Kubernetes的應用。

最後,我們將會使用SkaffoldCloud Code讓部署過程更簡便。 Skaffold會處理構建、推送和部署應用的工作流。 Cloud Code是一個適用於VS Code和IntelliJ的插件,它可以與Skaffold和IDE協作,這樣的話,你只需要點擊一個按鈕就可以部署至GKE。在本文中,我們將會組合使用IntelliJ和Cloud Code。

搭建工具

在編寫代碼之前,我們首先確保有一個Google Cloud項目並且所有的工具都已經安裝就緒。

創建Google Cloud項目

搭建GCP實例非常容易。你可以按照這些指南完成該過程。這個新的項目允許我們將應用部署到GKE和訪問數據庫(Cloud Firestore),隨後當我們容器化應用的時候,它還提供了一個讓我們推送鏡像的地方。

安裝Cloud Code

接下來,我們要安裝Cloud Code。你可以按照這些指南學習如何將Cloud Code安裝到IntelliJ中。 Cloud Code會管理Skaffold的Google SDK安裝,在本文後續的內容中,我們將會用到它們。 Cloud Code還允許我們探查GKE deployment和 service。最重要的是,它還有一個很聰明的GKE開發模式,它會持續監聽代碼的變化,當它探測到變化時,就會構建應用、構建鏡像、推送鏡像到倉庫、部署應用到GKE集群、啟動流式日誌並打開localhost通道,這樣的話就可以在本地測試應用了。這個過程就像魔法一樣!

為了使用Cloud Code繼續處理我們的應用,首先確保你已經使用Cloud Code插件進行了登錄,這可以通過點擊IntelliJ窗口右上角的圖標來實現:

Spring Boot教程:構建微服務並部署至Google Cloud 1

除此之外,我們還會運行一些命令確保應用可以在你的機器上運行並且能夠與Google Cloud上項目所需的服務進行通信。我們要確保指向了正確的項目並進行認證:

gcloud config set project 
gcloud auth login

接下來,需要確保你的機器具有應用憑證(application credential),以便於在本地運行應用:

gcloud auth application-default login

啟用API

現在,所有的準備工作都已經搭建完成,我們接下來需要啟用應用中所使用的API:

  • Google Container Registry API:該API允許我們有一個自己的registry,可以將私有的鏡像推送到這裡。
  • Datastore模式中的Cloud Firestore:它會允許我們將實體存儲到NoSQL數據庫中。請務必選擇Datastore模式,這樣我們才能使用Spring Cloud GCP為其提供的支持功能。

通過訪問項目的API Dashboard,你可以管理項目中已經啟用的API。

創建我們的Dog服務

要事優先!我們需要從一個可以本地運行的簡單應用開始。我們將會創建一個Dog微服務。因為我使用的是IntelliJ Ultimate,所以可以依次訪問“File -> New -> Project…”並選擇“Spring Initializr”。在這裡,我會選擇Maven、Jar和Java 11,並且會將其命名為dog,如下所示:

Spring Boot教程:構建微服務並部署至Google Cloud 2點擊next並添加:Lombok、Spring Web和GCP Support,如下所示:

Spring Boot教程:構建微服務並部署至Google Cloud 3

如果一切正常的話,我們應該會得到一個可以運行的應用。如果你不想使用IntelliJ來完成該步驟的話,那麼可以使用你的IDE的對等功能或者直接使用Spring的Initilizr

接下來,我們要為Dog服務添加一個POJO以及幾個REST端點,以便於測試我們的應用。我們的Dog對象會有name和age屬性,在這裡,我們會使用Lombok的@Data註解來標註它們,這樣就不用為它們編寫setter和getter方法了。另外,我們還會使用@AllArgsConstructor註解,這樣Lombok會為我們創建一個構造器。在稍後創建Dog的時候,我們將會使用該構造器。

@Data
@AllArgsConstructor
public class Dog {
 private String name;
 private int age;
}

接下來,我們要為Dog創建控制器類和REST端點:

@RestController
@Slf4j
public class DogController {

  @GetMapping("/api/v1/dogs")
  public List getAllDogs() {
    log.debug("->getAllDogs");
    return ImmutableList.of(new Dog("Fluffy", 5),
        new Dog("Bob", 6),
        new Dog("Cupcake", 11));
  }

  @PostMapping("/api/v1/dogs")
  public Dog saveDog(@RequestBody Dog dog) {
    log.debug("->saveDog {}", dog);
    return dog;
  }
}

端點會返回預先定義好的dog列表,saveDog其實沒有實現什麼功能,但是作為起步來講,這已經足夠了。

使用Cloud Firestore

現在,我們已經有了一個應用的骨架,接下來,我們會嘗試使用GCP的一些服務。 Spring Cloud GCP為Datastore模式的Google Cloud Firestore添加了Spring Data的支持。我們會使用該特性來存儲Dog,而不是使用簡單的列表。這樣,用戶也可以真正將Dog保存到數據庫中。

首先,我們需要將Spring Cloud GCP Data Datastore依賴添加到POM文件中:


    org.springframework.cloud
    spring-cloud-gcp-data-datastore

現在,我們需要修改Dog,使其能夠進行存儲。我們將會添加一個@Entity註解,並為Long值類型添加@Id註解,使其作為實體的標識符:

@Entity
@Data
@AllArgsConstructor
public class Dog {
  @Id private Long id;
  private String name;
  private int age;
}

隨後,我們可以創建一個常規的Spring Repository類,如下所示:

@Repository
public interface DogRepository extends DatastoreRepository {}

和一般的Spring Repository類似,這裡沒有必要編寫接口的實現,因為我們只會使用非常基礎的方法。

現在,可以修改控制器類了,我們會將DogRepository注入到 DogController中,然後修改這個類並按照如下的方式使用repository:

@RestController
@Slf4j
@RequiredArgsConstructor
public class DogController {

  private final DogRepository dogRepository;

  @GetMapping("/api/v1/dogs")
  public Iterable getAllDogs() {
    log.debug("->getAllDogs");
    return dogRepository.findAll();
  }

  @PostMapping("/api/v1/dogs")
  public Dog saveDog(@RequestBody Dog dog) {
    log.debug("->saveDog {}", dog);
    return dogRepository.save(dog);
  }
}

注意,我們使用了Lombok的@RequiredArgsConstructor註解來創建構造器,以便於注入DogRepository。在運行應用的時候,端點會調用Dog服務,進而會嘗試使用Cloud Firestore來檢索和存儲Dog。

小提示:要快速對其進行測試的話,我們可以在IntelliJ中使用如下的內容創建HTTP請求:

POST http://localhost:8080/api/v1/dogs
Content-Type: application/json

{
  "name": "bob",
  "age": 5
}

只需簡單的幾步,我們就將應用運行了起來,並且能夠消費來自GCP的服務。太棒了!現在,我們將其變成一個容器並部署它。

容器化Dog服務

現在,我們可以編寫Dockerfile文件來容器化上述的應用。但是,我們採用一個不同的方案,也就是Jib。 Jib有一點我非常喜歡,那就是它會把應用程序分割為多個層,將依賴從類中分離出來。這樣做的好處在於,我們會有更快的構建速度,這樣不必等待Docker重新構建整個Java應用,而是只部署那些發生了變化的層。除此之外,Jib還有一個Maven插件,我們只需修改應用的POM文件就可以輕鬆將其搭建起來。

要開始使用該插件,我們需要修改POM文件並添加如下的內容:


        com.google.cloud.tools
        jib-maven-plugin
        1.8.0
        
          
            gcr.io/distroless/java:11
          
          
            gcr.io//${project.artifactId}
          
        

注意,我們使用了Google的Java 11 distroless鏡像。 “Distroless”鏡像只包含了你的應用及其運行時依賴。它們不包含包管理器、shell或其他在標準Linux發行版中會存在的其他程序。

我們限制運行時容器中所包含的內容恰好是應用所需,這是Google和其他在生產環境使用容器多年的其他技術巨頭的最佳實踐。它會改善掃描器(如CVE)的信噪比並降低確定問題出處的負擔。

記住,需要將上述代碼替換為你的GCP registry並匹配項目的名稱。

完成之後,就可以嘗試通過如下的命令構建並推送應用的鏡像了:

$ ./mvnw install jib:build

運行該命令會構建並測試應用,創建鏡像並最終將新創建的鏡像推送至你的registry。

注意:通常來講,使用distro和一個特定的摘要,而不是使用“latest”是一個好的實踐。我將決定權留給讀者,讓你們根據所使用的Java版本自行確定該使用哪個基礎鏡像和摘要。

部署Dog服務

到這裡,我們的應用幾乎就可以部署了。為了實現這一點,我們先創建一個GKE集群,應用將會部署到這裡面。

創建GKE集群

關於如何創建GKE集群,可以參考這些指南。基本上來講,你需要訪問GKE頁面,等待API啟用,然後點擊按鈕來創建集群。你可以使用默認的配置,但是要確保點擊“More options”按鈕,並啟用對Cloud API的完全訪問:

Spring Boot教程:構建微服務並部署至Google Cloud 4

這將允許你的GKE節點具有訪問所有其他Google Cloud服務的權限。稍等片刻之後,集群就能創建出來了:

Spring Boot教程:構建微服務並部署至Google Cloud 5

在Kubernetes中,探測應用的存活狀態

Kubernetes會希望監控你的應用,從而確保應用處於正常運行的狀態。在出現故障的時候,Kubernetes能夠知道應用停機了,因此它會啟動一個新的實例。為了實現這一點,我們需要確保當Kubernetes問詢應用程序的時候,應用能夠給出響應。我們需要添加一個actuator和Spring Cloud Kubernetes。

添加如下的依賴到POM文件中:


    org.springframework.boot
        spring-boot-starter-actuator

如果你的應用在src/main/resources目錄下存在application.properties文件的話,請刪除掉它,並創建一個包含如下內容的application.yaml文件:

spring:
  application:
    name: dog-service

management:
  endpoint:
    health:
      enabled: true

這樣會給我們的應用一個名稱,並且會暴露前文所述的健康端點。為了校驗它是否正常運行,你可以訪問應用的localhost:8080/actuator/health地址。你將會看到如下的響應:

{
    "status": "UP"
}

配置應用在Kubernetes中運行

為了將應用部署到新GKE集群中,我們編寫一些額外的YAML,以便於創建deployment和service。我們可以使用如下的deployment,但是需要記住要將GCR改成你自己項目的名稱:

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dog-service
spec:
  selector:
    matchLabels:
      app: dog-service
  replicas: 1
  template:
    metadata:
      labels:
        app: dog-service
    spec:
      containers:
        - name: dog-service
          image: gcr.io//dog
          ports:
            - containerPort: 8080
          livenessProbe:
            initialDelaySeconds: 20
            httpGet:
              port: 8080
              path: /actuator/health
          readinessProbe:
            initialDelaySeconds: 30
            httpGet:
              port: 8080
              path: /actuator/health

添加一個具有如下內容的service.yaml文件:

apiVersion: v1
kind: Service
metadata:
  name: dog-service
spec:
  type: NodePort
  selector:
    app: dog-service
  ports:
    - port: 8080
      targetPort: 8080

deployment包含了幾個針對就緒狀態和存活狀態探針的修改。 Kubernetes會使用這些端點來詢問應用是否處於活躍狀態。 service暴露了deployment,這樣其他的服務就可以消費它了。

完成之後,我們現在就可以使用在本文開始時安裝的Cloud Code插件進行啟動了。通過Tools菜單,選擇Cloud Code -> Kubernetes -> Add Kubernetes Support。這將會自動向你的應用中添加Skaffold YAML並且會搭建一些內容,這樣的話,我們只需點擊一個按鈕就可以將其部署到集群中了。要確認所有的這些都能正常運行,我們可以通過IntelliJ的Run/Debug Configurations探查配置。如果點擊Develop on Kubernetes話,它會自動獲取你的GKE和Kubernetes配置文件,如下所示:

Spring Boot教程:構建微服務並部署至Google Cloud 6

點擊Ok按鈕,然後點擊右上角的綠色”Play”按鈕:

Spring Boot教程:構建微服務並部署至Google Cloud 7

隨後,Cloud Code會自動構建應用、創建鏡像、部署應用到GKE集群並將Stackdriver的日誌轉發到本地機器上。它還會打開一個通道,這樣的話,我們就可以通過 localhost:8080來消費該服務。你還可以在Google Cloud Console的工作負載頁面查看:

Spring Boot教程:構建微服務並部署至Google Cloud 8

結論

祝賀你完成到了這一步!我們在本文中構建的應用展現了大多數基於微服務的應用都會用到的一些核心技術:快速的、完全託管的、serverless的、雲原生NoSQL文檔數據庫(Cloud Firestore),以及GKE,這是一個用於部署基於Kubernetes的容器化應用的託管的、生產就緒的環境,最後我們使用Spring Boot構建了一個雲原生微服務。在這個過程中,我們還學習瞭如何通過常見的Java模式,使用Cloud Code等工具來簡化開發工作流,並使用Jib來構建容器化應用。

希望這篇文章對你有用,並且能嘗試一下這些技術。如果你覺得這很有意思的話,可以看一下Google提供的codelabs,通過它們你可以學習Spring和Google Cloud產品。

作者簡介:

Sergio Feilx 是Google Cloud的一名軟件工程師,他就職於Cloud Engineering Productivity團隊,這是Google Cloud中的一個組織,致力於讓開發更加流暢並提升產品和工程質量。

原文鏈接:

Spring Boot Tutorial: Building Microservices Deployed to Google Cloud