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

1. 始める前に

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

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

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

開発用ノートパソコンなどのローカルマシンで Kubernetes を実行することに関心がある場合は、Minikube を検討してください。Minikube では、開発とテスト用に単一ノードの Kubernetes クラスタを簡単にセットアップできます。必要に応じて、Minikube を使用して Codelab を進めることができます。

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

前提条件

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

演習内容

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

必要なもの

  • 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

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

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 リポジトリが存在しない場合は、新しい Docker リポジトリを作成します。イメージを push する前に、リポジトリを作成する必要があります。
$ gcloud artifacts repositories create codelabrepo     --repository-format=docker --location=us-central1 
  1. イメージの形式は次のとおりです。

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

たとえば、ロケーション us-central1codelabrepo という名前のリポジトリを作成し、イメージに 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 コンソールで Artifacts Registry の [イメージ] ページに移動すると、コンソールに一覧表示されるはずです。これで、プロジェクト全体の Docker イメージを使用できるようになりました。Kubernetes はこのイメージにアクセスし、数分後にオーケストレートできます。
  2. (省略可)完了後(すべてをダウンロードして抽出するには時間がかかります)、次のコマンドを使用してイメージをテストします。このコマンドは、新しく作成されたコンテナ イメージから、ポート 8080 で Docker コンテナをデーモンとして実行します。権限の問題が発生した場合は、まず 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. 作成した Deployment を表示するには、次のコマンドを実行します。
$ 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 でのみアクセスできます。Kubernetes 仮想ネットワークの外部から hello-java コンテナにアクセスできるようにするには、Pod を Kubernetes Service として公開する必要があります。

  1. Cloud Shell で Kubernetes LoadBalancer Service を作成すると、Pod を公共のインターネットに公開できます。
$ kubectl create service loadbalancer hello-java --tcp=8080:8080

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

Kubernetes マスターは、ロードバランサと関連する Compute Engine 転送ルール、ターゲット プール、ファイアウォール ルールを作成して、Google Cloud の外部からサービスを完全に利用できるようにします。

  1. Service の一般公開されている 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 Deployment を編集して、イメージを 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

詳細