Google Kubernetes Engine 上の Kubernetes に Spring Boot Java アプリをデプロイする

1. 始める前に

Kubernetes はオープンソースのプロジェクトで、ノートパソコンから可用性の高いマルチノード クラスタ、パブリック クラウドからオンプレミスのデプロイ、仮想マシン(VM)インスタンスからベアメタルまで、さまざまな環境で実行できます。

この Codelab では、シンプルな Spring Boot Java ウェブアプリを GKE の Kubernetes にデプロイします。この Codelab の目的は、ウェブアプリを Kubernetes で複製されたアプリとして実行することです。自分のマシンで開発したコードを Docker コンテナ イメージに変換してから、そのイメージを GKE で実行します。

このラボでは、基盤となるインフラストラクチャの設定ではなく、Kubernetes の操作に集中できるように、Google Cloud のフルマネージド Kubernetes サービスである GKE を使用します。

Kubernetes を開発用のノートパソコンなどのローカルのマシンで実行したい場合は、Minikube について調べることをおすすめします。Minikube を使用すると、開発とテスト用に単一ノードの Kubernetes クラスタを簡単に設定できます。Minikube をこの Codelab の学習に使用することも可能です。

この Codelab では、Spring Boot を使用したアプリの構築に関するガイドのサンプルコードを使用します。

前提条件

  • Java プログラミング言語とツールに関する知識
  • Linux の標準的なテキスト エディタ(Vim、Emacs、nano など)に関する知識

演習内容

  • シンプルな Java アプリを Docker コンテナとしてパッケージ化する。
  • GKE で Kubernetes クラスタを作成します。
  • Java アプリを GKE の Kubernetes にデプロイします。
  • サービスをスケールアップし、アップグレードをロールアウトする。
  • ウェブベースの Kubernetes ユーザー インターフェースである Access Dashboard。

必要なもの

  • Google Cloud プロジェクト
  • ブラウザ(Google Chrome など)

2. 設定と要件

セルフペース型の環境設定

  1. Google Cloud Console にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Gmail アカウントも Google Workspace アカウントもまだお持ちでない場合は、アカウントを作成してください。

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列です。いつでも更新できます。
  • プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud コンソールでは一意の文字列が自動生成されます。通常は、この内容を意識する必要はありません。ほとんどの Codelab では、プロジェクト ID(通常は PROJECT_ID と識別されます)を参照する必要があります。生成された ID が好みではない場合は、ランダムに別の ID を生成できます。または、ご自身で試して、利用可能かどうかを確認することもできます。このステップ以降は変更できず、プロジェクトを通して同じ ID になります。
  • なお、3 つ目の値として、一部の API が使用するプロジェクト番号があります。これら 3 つの値について詳しくは、こちらのドキュメントをご覧ください。
  1. 次に、Cloud のリソースや API を使用するために、Cloud コンソールで課金を有効にする必要があります。この Codelab の操作をすべて行って、費用が生じたとしても、少額です。このチュートリアルの終了後に請求が発生しないようにリソースをシャットダウンするには、作成したリソースを削除するか、プロジェクトを削除します。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。

Cloud Shell をアクティブにする

  1. Cloud Console で、[Cloud Shell をアクティブにする] 853e55310c205094.png をクリックします。

55efc1aaa7a4d3ad.png

Cloud Shell を初めて起動する場合は、その内容を説明する中間画面が表示されます。中間画面が表示された場合は、[続行] をクリックします。

9c92662c6a846a5c.png

すぐにプロビジョニングが実行され、Cloud Shell に接続されます。

9f0e51b578fecce5.png

この仮想マシンには、必要な開発ツールがすべて用意されています。仮想マシンは Google Cloud で稼働し、永続的なホーム ディレクトリが 5 GB 用意されているため、ネットワークのパフォーマンスと認証が大幅に向上しています。このコードラボで行う作業のほとんどはブラウザから実行できます。

Cloud Shell に接続すると、認証が完了しており、プロジェクトに各自のプロジェクト ID が設定されていることがわかります。

  1. Cloud Shell で次のコマンドを実行して、認証されたことを確認します。
gcloud auth list

コマンド出力

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Cloud Shell で次のコマンドを実行して、gcloud コマンドがプロジェクトを認識していることを確認します。
gcloud config list project

コマンド出力

[core]
project = <PROJECT_ID>

上記のようになっていない場合は、次のコマンドで設定できます。

gcloud config set project <PROJECT_ID>

コマンド出力

Updated property [core/project].

3. ソースコードを取得する

Cloud Shell の起動後に以下のコマンドラインを使用すると、ホーム ディレクトリ内にサンプル ソースコードのクローンを作成できます。

$ git clone https://github.com/spring-guides/gs-spring-boot.git
$ cd gs-spring-boot/complete

4. アプリをローカルで実行する

  1. JAVA_HOME が正しいバージョンに設定されていることを確認します。
$ export JAVA_HOME=/usr/lib/jvm/java-1.17.0-openjdk-amd64
  1. Spring Boot プラグインを使用して、Spring Boot アプリを通常どおり起動できます。
$ ./mvnw -DskipTests spring-boot:run
  1. アプリが起動したら、Cloud Shell ツールバーの [ウェブでプレビュー] 1a94d5bd10bfc072.png アイコンをクリックし、[ポート 8080 でプレビュー] を選択します。

6252b94905f3f7bd.png

ブラウザのタブが開き、起動したサーバーに接続されます。

9b6c29059957bd0.jpeg

5. Java アプリを Docker コンテナとしてパッケージ化する

次に、アプリを Kubernetes で実行するための準備を行います。最初のステップとして、コンテナとその内容を定義します。

  1. アプリのデプロイ可能な JAR を作成します。
$ ./mvnw -DskipTests package
  1. 作成するコンテナ イメージを保存するために、Artifact Registry API を有効にします。
$ gcloud services enable artifactregistry.googleapis.com
  1. Docker リポジトリが存在しない場合は、新しいリポジトリを作成します。イメージを push する前に、リポジトリを作成する必要があります。
$ gcloud artifacts repositories create codelabrepo     --repository-format=docker --location=us-central1 
  1. 画像は次の形式になります。

{LOCATION}-docker.pkg.dev/{PROJECT-ID}/{REPOSITORY}/{IMAGE-NAME}

たとえば、us-central1 というロケーションに codelabrepo という名前のリポジトリを作成し、イメージに hello-java:v1 という名前を付ける場合は、イメージは次のようになります。

us-central1-docker.pkg.dev/{PROJECT-ID}/codelabrepo/hello-java:v1

  1. Jib を使用してコンテナ イメージを作成し、Artifact Registry に push します。
$ export GOOGLE_CLOUD_PROJECT=`gcloud config list --format="value(core.project)"`

$ ./mvnw -DskipTests com.google.cloud.tools:jib-maven-plugin:build -Dimage=us-central1-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/codelabrepo/hello-java:v1
  1. Cloud Console の Artifacts Registry の [イメージ] ページに移動すると、コンテナ イメージがコンソールに表示されます。これで、Docker イメージをプロジェクト全体で利用できるようになりました。数分後に実演するとおり、Kubernetes でこのイメージにアクセスし、オーケストレートできます。
  2. (省略可)完了したら(すべてをダウンロードして抽出するのに時間がかかります)、次のコマンドを使用してイメージをテストします。このコマンドは、新たに作成したコンテナ イメージから Docker コンテナをポート 8080 上のデーモンとして実行します。権限に関する問題が発生した場合は、まず gcloud auth configure-docker us-central1-docker.pkg.dev を実行します。
$ docker run -ti --rm -p 8080:8080 \
  us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v1
  1. ここでも Cloud Shell のウェブ プレビュー機能を利用します。

6252b94905f3f7bd.png

  1. 新しいタブにデフォルトのページが表示されます。アプリが Docker コンテナでローカルに実行されていることを確認したら、Control+C を押して実行中のコンテナを停止できます。

6. クラスタを作成する

これで、GKE クラスタを作成する準備が整いました。クラスタは、Google が管理する Kubernetes API サーバーと一連のワーカーノードで構成されます。ワーカーノードは Compute Engine VM です。

  1. まず、関連する API 機能が有効になっていることを確認します。
$ gcloud services enable compute.googleapis.com container.googleapis.com
  1. 2 つの n1-standard-1 ノードを含むクラスタを作成します(完了するまでに数分かかります)。
$ gcloud container clusters create hello-java-cluster \
  --num-nodes 2 \
  --machine-type n1-standard-1 \
  --zone us-central1-c

最終的に、作成されたクラスタが表示されます。

Creating cluster hello-java-cluster...done.
Created [https://container.googleapis.com/v1/projects/...].
kubeconfig entry generated for hello-dotnet-cluster.
NAME                  ZONE            MASTER_VERSION  
hello-java-cluster  us-central1-c  ...

これで、GKE を搭載した Kubernetes クラスタが完全に機能するようになりました。

758c7fca14f70623.png

次に、コンテナ化されたアプリを Kubernetes クラスタにデプロイします。ここからは kubectl コマンドライン(Cloud Shell 環境で設定済み)を使用します。この Codelab の残りの部分では、Kubernetes のクライアントとサーバーがどちらもバージョン 1.2 以降である必要があります。kubectl version を実行すると、コマンドの現在のバージョンが表示されます。

7. アプリを Kubernetes にデプロイする

  1. Kubernetes Deployment は、作成したコンテナ イメージを使用して、アプリの複数のインスタンスを作成、管理、スケーリングできます。kubectl run コマンドを使用して、アプリの 1 つのインスタンスを Kubernetes にデプロイします。
$ kubectl create deployment hello-java --image=us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v1
  1. 作成したデプロイを表示するには、次のコマンドを実行します。
$ kubectl get deployments

NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-java   1         1         1            1           37s
  1. デプロイによって作成されたアプリ インスタンスを表示するには、次のコマンドを実行します。
$ kubectl get pods

NAME                         READY     STATUS    RESTARTS   AGE
hello-java-714049816-ztzrb   1/1       Running   0          57s

この時点で、コンテナは Kubernetes の制御の下に動作するようになりますが、外部からはまだアクセスできないので、アクセスを許可する必要があります。

8. 外部トラフィックを許可する

デフォルトでは、Pod にはクラスタ内の内部 IP からしかアクセスできません。hello-java コンテナに Kubernetes 仮想ネットワークの外部からアクセスできるようにするには、Pod を Kubernetes Service として公開する必要があります。

  1. Cloud Shell で、Kubernetes LoadBalancer サービスを作成して、Pod をインターネットに公開できます。
$ kubectl create service loadbalancer hello-java --tcp=8080:8080

Pod ではなく、Deployment を直接公開していることに注意してください。これにより、Deployment で管理されるすべての Pod でトラフィックをロードバランスする Service が作成されます(ここでは 1 つの Pod だけですが、後でレプリカを追加します)。

Kubernetes マスターによってロードバランサ、それに関連する Compute Engine 転送ルール、ターゲット プール、ファイアウォール ルールが作成され、Google Cloud の外部からこの Service に完全にアクセスできるようになります。

  1. サービスのパブリック IP アドレスを確認するには、kubectl をリクエストしてすべてのクラスタ サービスを一覧表示します。
$ kubectl get services

NAME         CLUSTER-IP     EXTERNAL-IP      PORT(S)    AGE
hello-java   10.3.253.62    aaa.bbb.ccc.ddd  8080/TCP    1m
kubernetes   10.3.240.1     <none>           443/TCP    5m
  1. ブラウザで http://<EXTERNAL_IP>:8080 を指定すると、サービスにアクセスできるようになります。

9. サービスをスケーリングする

Kubernetes の優れた特長の一つは、アプリのスケールが容易なことです。アプリの容量が突然必要になったとします。レプリケーション コントローラに、アプリ インスタンスの新しいレプリカ数を管理するよう指示するだけで簡単に対応できます。

$ kubectl scale deployment hello-java --replicas=3

deployment "hello-java" scaled

$ kubectl get deployment
NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-java   3         3         3            3           22m

宣言型アプローチに注目してください。新しいインスタンスの起動や停止を行うのではなく、実行するインスタンスの数を宣言します。Kubernetes の調整ループは、リクエストした内容と実際の状態が一致しているかどうかが照合され、必要に応じて調整が行われます。

10. サービスへのアップグレードをロールアウトする

本番環境にデプロイしたアプリは、いずれかの時点でバグ修正や追加機能の実装が必要になります。Kubernetes を使用すると、ユーザーに影響を及ぼすことなく、新しいバージョンを本番環境にデプロイできます。

  1. Cloud Shell メニューの [エディタを開く ] 2109d75686c889a.png をクリックして、コードエディタを開きます。
  2. src/main/java/com/example/springboot/HelloController.java に移動して、レスポンスの値を更新します。
package com.example.springboot;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
public class HelloController {

    @RequestMapping("/")
    public String index() {
        return "Greetings from Google Kubernetes Engine!";
    }
}
  1. Jib を使用して、コンテナ イメージの新しいバージョンをビルドして push します。更新されたイメージの構築と push は、キャッシュを最大限に活用して迅速に行います。
$ ./mvnw -DskipTests package com.google.cloud.tools:jib-maven-plugin:build -Dimage=us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v2

これで、Kubernetes がレプリケーション コントローラを新しいバージョンのアプリにスムーズに更新する準備が整いました。

  1. コンテナの実行中にイメージラベルを変更するには、既存の hello-java デプロイを編集し、イメージを us-central1-docker.pkg.dev/PROJECT_ID/codelabrepo/hello-java:v1

目的地: us-central1-docker.pkg.dev/PROJECT_ID/codelabrepo/hello-java:v2

  1. kubectl set image コマンドを使用すると、ローリング アップデートで新しいバージョンのアプリをクラスタ全体に一度に 1 つのインスタンスでデプロイするように Kubernetes に要求できます。
$ kubectl set image deployment/hello-java hello-java=us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v2

deployment "hello-java" image updated
  1. http://EXTERNAL_IP:8080 を再度確認し、新しいレスポンスが返されていることを確認します。

11. ロールバック

エラー: アプリの新しいバージョンで間違いがありましたか?新しいバージョンにエラーが含まれており、迅速にロールバックする必要がある場合などです。Kubernetes を使用すると、以前の状態に簡単にロールバックできます。次のコマンドを実行して、アプリをロールバックします。

$ kubectl rollout undo deployment/hello-java

http://EXTERNAL_IP:8080 を再度確認すると、古いレスポンスが表示されます。

12. 完了

新しい Java ベースのウェブアプリをビルドして GKE の Kubernetes にデプロイする方法を学習しました。

クリーンアップ

$ gcloud container clusters delete hello-java-cluster --zone us-central1-c

$ gcloud container images delete us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v1 us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v2

詳細