Jib でコンテナ化された Micronaut アプリケーションを Google Kubernetes Engine にデプロイする

1. 概要

Micronaut について

Micronaut は、モジュール式でテストしやすいマイクロサービスとサーバーレス アプリケーションを構築するための、最新の JVM ベースのフルスタック フレームワークです。Micronaut は、最小限のメモリフットプリントで、優れた起動時間と高速なスループットを実現することを目的としています。デベロッパーは、Java、Groovy、Kotlin で Micronaut を使用して開発できます。

Micronaut は以下を提供します。

  • 起動時間が短く、メモリ消費量が少ない - リフレクション ベースの IoC フレームワークは、コード内のすべてのフィールド、メソッド、コンストラクタのリフレクション データを読み込んでキャッシュに保存しますが、Micronaut では、アプリケーションの起動時間とメモリ消費量はコードベースのサイズに依存しません。
  • 宣言型、リアクティブ、コンパイル時の HTTP クライアント - コンパイル時に実装されるリアクティブ HTTP クライアントを宣言的に構築し、メモリ消費量を削減します。
  • Netty 上に構築されたノンブロッキング HTTP サーバー - Micronaut の HTTP サーバーは、学習曲線が緩やかで、HTTP クライアントで使用できる API を簡単に公開できます。
  • 迅速かつ簡単なテスト - ユニットテストでサーバーとクライアントを簡単にスピンアップし、瞬時に実行できます。
  • 効率的なコンパイル時の依存性注入と AOP - Micronaut は、リフレクションを使用しないシンプルなコンパイル時のアスペクト指向プログラミング API を提供します。
  • 完全にリアクティブでノンブロッキングのアプリを構築する - Micronaut は、RxJava や Reactor など、Reactive Streams を実装するあらゆるフレームワークをサポートしています。

詳細については、Micronaut のウェブサイトをご覧ください。

Kubernetes について

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

このラボでは、シンプルな Groovy ベースの Micronaut マイクロサービスを Kubernetes Engine で動作する Kubernetes にデプロイします。

この Codelab の目標は、Kubernetes で実行される複製サービスとしてマイクロサービスを実行することです。自分のマシンで開発したコードを Docker コンテナ イメージに変換してから、そのイメージを Kubernetes Engine で実行します。

次の図はこの Codelab で利用するさまざまなパーツを示しています。全体がどのように構成されるのか理解するのに役立つでしょう。図を参照しながら進めれば、終了する頃には内容をすべて理解できるようになります(現時点で理解できなくても問題ありません)。

Kubernetes Codelab Diagram 1 (2).png

この Codelab では、Kubernetes Engine(Google がホストし、Compute Engine で実行される Kubernetes の 1 バージョン)などのマネージド環境を使用するため、基盤となるインフラストラクチャの設定を気にせずに Kubernetes の操作に集中できます。

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

Jib について

Jib は、Java アプリケーションの Docker イメージと OCI イメージをビルドできるオープンソース ツールです。Maven と Gradle のプラグイン、および Java ライブラリとして使用できます。

Jib の目標は次のとおりです。

  • 高速 - 変更を迅速にデプロイします。Jib は、アプリケーションを複数のレイヤに分割し、依存関係をクラスから分離します。これで、Docker が Java アプリケーション全体を再ビルドするのを待つ必要がなくなり、変更されたレイヤだけをデプロイできます。
  • 再現可能 - 同じコンテンツを使ってコンテナ イメージを再ビルドし、常に同じイメージを生成します。不要な更新が再びトリガーされることはありません。
  • デーモンレス - CLI への依存を軽減します。Maven 内または Gradle 内から Docker イメージをビルドし、選択したレジストリに push。Dockerfile の書き込みや Docker ビルド / push の呼び出しが不要。

Jib の詳細については、Github のプロジェクト ページをご覧ください。

このチュートリアルについて

このチュートリアルでは、Jib ツールのサンプルコードを使用して、Java アプリケーションのコンテナをビルドします。

このサンプルは、Micronaut フレームワークと Apache Groovy プログラミング言語を使用するシンプルな hello world サービスです。

学習内容

  • Jib を使用してシンプルな Java アプリケーションを Docker コンテナとしてパッケージ化する方法
  • Kubernetes Engine に Kubernetes クラスタを作成する方法。
  • Micronaut サービスを Kubernetes Engine の Kubernetes にデプロイする方法
  • サービスをスケールアップし、アップグレードをロールアウトする
  • Kubernetes グラフィカル ダッシュボードにアクセスする

必要なもの

  • Google Cloud Platform プロジェクト
  • ChromeFirefox などのブラウザ
  • Linux の標準的なテキスト エディタ(vim、emacs、nano など)を使い慣れていること

このチュートリアルの利用方法をお選びください。

通読するのみ 通読し、演習を行う

HTML/CSS ウェブアプリの作成経験についてお答えください。

初心者 中級者 上級者

Google Cloud Platform サービスのご利用経験についてどのように評価されますか?

初心者 中級者 上級者

2. 設定と要件

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

  1. Cloud Console にログインし、新しいプロジェクトを作成するか、既存のプロジェクトを再利用します(Gmail アカウントまたは G Suite アカウントをお持ちでない場合は、アカウントを作成する必要があります)。

dMbN6g9RawQj_VXCSYpdYncY-DbaRzr2GbnwoV7jFf1u3avxJtmGPmKpMYgiaMH-qu80a_NJ9p2IIXFppYk8x3wyymZXavjglNLJJhuXieCem56H30hwXtd8PvXGpXJO9gEUDu3cZw

ci9Oe6PgnbNuSYlMyvbXF1JdQyiHoEgnhl4PlV_MFagm2ppzhueRkqX4eLjJllZco_2zCp0V0bpTupUSKji9KkQyWqj11pqit1K1faS1V6aFxLGQdkuzGp4rsQTan7F01iePL5DtqQ

8-tA_Lheyo8SscAVKrGii2coplQp2_D1Iosb2ViABY0UUO1A8cimXUu6Wf1R9zJIRExL5OB2j946aIiFtyKTzxDcNnuznmR45vZ2HMoK3o67jxuoUJCAnqvEX6NgPGFjCVNgASc-lg

プロジェクト ID を忘れないようにしてください。プロジェクト ID はすべての Google Cloud プロジェクトを通じて一意の名前にする必要があります(上記の名前はすでに使用されているので使用できません)。以降、このコードラボでは PROJECT_ID と呼びます。

  1. 次に、Google Cloud リソースを使用するために、Cloud Console で課金を有効にする必要があります。

このコードラボを実行しても、費用はほとんどかからないはずです。このチュートリアル以外で請求が発生しないように、リソースのシャットダウン方法を説明する「クリーンアップ」セクションの手順に従うようにしてください。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。

3. Micronaut サンプルのソースコードを取得する

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

$ git clone https://github.com/GoogleContainerTools/jib.git
$ cd jib/examples/micronaut/

4. コードの概要

Micronaut のシンプルなサービスは、有名な Hello World メッセージを出力するコントローラで構成されています。

@Controller("/hello")
class HelloController {
    @Get("/")
    String index() {
        "Hello World"
    }
}

HelloController コントローラは /hello パスのリクエストに応答し、index() メソッドは HTTP GET リクエストを受け入れます。

Spock テストクラスを使用して、出力で正しいメッセージが返されることを確認することもできます。

class HelloControllerSpec extends Specification {
    @Shared
    @AutoCleanup
    EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer)

    @Shared
    @AutoCleanup
    RxHttpClient client = embeddedServer.applicationContext.createBean(RxHttpClient, embeddedServer.getURL()) 

    void "test hello world response"() {
        when:
        HttpRequest request = HttpRequest.GET('/hello')
        String rsp  = client.toBlocking().retrieve(request)

        then:
        rsp == "Hello World"
    }
}

これは単なる単体テストではなく、本番環境で実行されるのと同じ Micronaut サーバー スタック(Netty フレームワークに基づく)を実際に実行するテストです。そのため、コードの動作はテスト時と本番環境で同じになります。

テストを実行するには、次のコマンドを実行して、すべてが正常であることを確認します。

./gradlew test

5. アプリケーションをローカルで実行する

次の Gradle コマンドを使用して、Micronaut サービスを通常どおりに起動できます。

$ ./gradlew run

アプリケーションが起動したら、小さな + アイコンを使用して追加の Cloud Shell インスタンスを開き、curl で期待どおりの出力が得られることを確認します。

$ curl localhost:8080/hello

「Hello World」というシンプルなメッセージが表示されます。

6. Jib を使用してアプリケーションを Docker コンテナとしてパッケージ化する

次に、アプリを Kubernetes で実行するための準備を行います。この目的のために、Jib を利用して難しい作業を代行します。これにより、Dockerfile を自分で操作する必要がなくなります。

コンテナをビルドするコマンドを実行しましょう。

$ ./gradlew jibDockerBuild

次のような出力が表示されます。

Tagging image with generated image reference micronaut-jib:0.1. If you'd like to specify a different tag, you can set the jib.to.image parameter in your build.gradle, or use the --im
age=<MY IMAGE> commandline flag.

Containerizing application to Docker daemon as micronaut-jib:0.1...
warning: Base image 'gcr.io/distroless/java' does not use a specific image digest - build may not be reproducible
Getting base image gcr.io/distroless/java...
Building dependencies layer...
Building resources layer...
Building classes layer...
Finalizing...

Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, example.micronaut.Application]
Loading to Docker daemon...

Built image to Docker daemon as micronaut-jib:0.1

イメージがビルドされたので、Cloud Shell の最初のタブで Docker イメージを実行して、フレンドリーな挨拶メッセージが表示されるかどうかを確認しましょう。

$ docker run -it -p 8080:8080 micronaut-jib:0.1
16:57:20.255 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [cloud, gcp]
16:57:23.203 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 2926ms. Server Running: http://97b7d76ccf3f:8080

サービスが実行されているので、2 番目の Cloud Shell タブで curl コマンドを起動して、想定どおりに動作しているかどうかを確認します。

$ curl localhost:8080/hello
Hello World

Cloud Shell で Ctrl+C を押すと、コンテナを停止できます。

7. コンテナ化されたサービスをレジストリに push する

イメージが意図どおりに機能することを確認したので、これを Docker イメージ用の非公開リポジトリである Google Container Registry に push します。このリポジトリにはすべての Google Cloud プロジェクトからアクセスできるだけでなく、Google Cloud Platform の外部からもアクセスできます。

レジストリに push する前に、[ツール > Container Registry] に移動して、Container Registry がプロジェクトで有効になっていることを確認します。有効になっていない場合は、次のダイアログが表示されます。[Container Registry API を有効にする] をクリックして有効にします。

ac812e6260ac7dfb.png

レジストリの準備ができたら、次のコマンドを実行してイメージをレジストリに push します。

$ gcloud auth configure-docker
$ docker tag micronaut-jib:0.1 \
         gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1
$ docker push gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1

上記のコマンドを使用すると、gcloud SDK は、Container Registry のインスタンスにイメージを push するように Docker を構成して承認し、レジストリ内の場所を指すようにイメージにタグを付け、そのイメージをレジストリに push します。

すべてがうまくいけば、しばらくするとコンソール([ツール] > [Container Registry])にコンテナ イメージが表示されます。この時点で Docker イメージはプロジェクト全体で使用できる状態になっており、数分後に実演するとおり、Kubernetes からアクセスしてオーケストレートできます。

12224c4e42183b4e.png

8. クラスタを作成する

これで、Kubernetes Engine クラスタを作成する準備が整いました。ただし、その前に、ウェブ コンソールの Google Kubernetes Engine セクションに移動し、システムが初期化されるまで待ちます(数秒で完了します)。

20c0587c0108b8ba.png

クラスタは、Google が管理する Kubernetes マスター API サーバーと、一連のワーカーノードから構成されます。ワーカーノードは Compute Engine 仮想マシンです。Cloud Shell セッションの gcloud CLI を使用して、2 つの n1-standard-1 ノードを含むクラスタを作成します(完了するまでに数分かかります)。

$ gcloud container clusters create hello-cluster \
  --num-nodes 2 \
  --machine-type n1-standard-1 \
  --zone us-central1-c

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

Creating cluster hello-cluster in us-central1-c...done.
Created [https://container.googleapis.com/v1/projects/mn-gke-test/zones/us-central1-c/clusters/hello-cluster].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-c/hello-cluster?project=mn-gke-test
kubeconfig entry generated for hello-cluster.
NAME           LOCATION       MASTER_VERSION  MASTER_IP       MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
hello-cluster  us-central1-c  1.9.7-gke.7     35.239.224.115  n1-standard-1  1.9.7-gke.7   2          RUNNING

これで、Google Kubernetes Engine を搭載した、完全版の Kubernetes クラスタが作成されました。

d9e1e314769753e7.png

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

9. アプリケーションを Kubernetes にデプロイする

Kubernetes Deployment は、作成したコンテナ イメージを使用して、アプリケーションの複数のインスタンスを作成、管理、スケーリングできます。kubectl create deployment コマンドを使用して、アプリケーションのデプロイを Kubernetes に作成しましょう。

$ kubectl create deployment hello-micronaut \
  --image=gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1

作成したデプロイを表示するには、次のコマンドを実行します。

$ kubectl get deployments
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-micronaut   1         1         1            1           5m

デプロイによって作成されたアプリケーション インスタンスを表示するには、次のコマンドを実行します。

$ kubectl get pods
NAME                               READY     STATUS    RESTARTS   AGE
hello-micronaut-5647fb98c5-lh5h7   1/1       Running   0          5m

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

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

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

Cloud Shell から、--type=LoadBalancer フラグ付きの kubectl expose コマンドを実行すると、ポッドをインターネットで公開できます。外部からアクセスできる IP を作成するには、このフラグが必要です。

$ kubectl expose deployment hello-micronaut --type=LoadBalancer --port=8080

このコマンドで使用しているフラグは、基盤となるインフラストラクチャで提供されるロードバランサ(この場合は Compute Engine ロードバランサ)を使うよう指定しています。ポッドを直接公開するのではなく、デプロイを公開していることに注意してください。これによって、Deployment で管理されるすべての Pod でトラフィックをロードバランスする Service が作成されます(ここでは 1 つの Pod だけですが、後でレプリカを追加します)。

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

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

$ kubectl get services
NAME              TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
hello-micronaut   LoadBalancer   10.39.243.251   aaa.bbb.ccc.ddd 8080:30354/TCP   1m
kubernetes        ClusterIP      10.39.240.1     <none>          443/TCP          31m

サービスに対して 2 つの IP アドレスが表示されます。いずれもポート 8080 を使用しています。1 つは内部 IP で、Cloud Virtual Network 内でのみ表示されます。もう 1 つは外部負荷分散 IP です。この例では、外部 IP アドレスは aaa.bbb.ccc.ddd です。

ブラウザでアドレス http://<EXTERNAL_IP>:8080/hello を入力すれば、対象の Service にアクセスできるようになります。

11. サービスをスケールアップする

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

$ kubectl scale deployment hello-micronaut --replicas=3
deployment.extensions "hello-micronaut" scaled

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

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

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

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

まず、アプリケーションを修正しましょう。Cloud Shell からコードエディタを開きます。

5aee8f3d1e003571.png

/jib/examples/micronaut/src/main/groovy/example/micronaut/HelloController.groovy に移動し、レスポンスの値を更新します。

@Controller("/hello")
class HelloController {
    @Get("/")
    String index() {
        "Hello Kubernetes World"
    }
}

/jib/examples/micronaut/build.gradle で、次の行を更新して、イメージのバージョンを 0.1 から 0.2 にアップグレードします。

version '0.2'

次に、最新の変更でアプリケーションを再ビルドしてパッケージ化します。

$ ./gradlew jibDockerBuild

イメージにタグを付けて、コンテナ イメージ レジストリに push します。

$ docker tag micronaut-jib:0.2 \
         gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2
$ docker push gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2

これで、Kubernetes がレプリケーション コントローラを新しいバージョンのアプリケーションにスムーズに更新する準備が整いました。コンテナの実行中にイメージラベルを変更するには、既存の hello-micronaut deployment を編集し、イメージを gcr.io/PROJECT_ID/micronaut-jib:0.1 から gcr.io/PROJECT_ID/micronaut-jib:0.2 に変更する必要があります。

kubectl set image コマンドを使用すると、ローリング アップデートで新しいバージョンのアプリケーションをクラスタ全体に一度に 1 つのインスタンスでデプロイするように Kubernetes にリクエストできます。

$ kubectl set image deployment/hello-micronaut \
          micronaut-jib=gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2

deployment.apps "hello-micronaut" image updated

http://EXTERNAL_IP:8080 を再度確認して、新しいレスポンスが返されていることを確認します。

13. ロールバック

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

$ kubectl rollout undo deployment/hello-micronaut

サービスの出力を確認すると、最初の「Hello World」というメッセージに戻っています。

14.まとめ

このステップでは、シンプルな Apache Groovy ベースの Micronaut Hello World サービスを設定し、Cloud Shell 内から直接実行し、Jib でコンテナとしてパッケージ化して、Google Kubernetes Engine にデプロイしました。

15. 完了

Google Kubernetes Engine の Kubernetes に新しい Apache Groovy / Micronaut ウェブベースのマイクロサービスをビルドしてデプロイする方法を学習しました。

詳細

ライセンス

この作業はクリエイティブ・コモンズの表示 2.0 汎用ライセンスにより使用許諾されています。