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

1. 概要

Micronaut について

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

Micronaut が提供するもの:

  • 高速起動と低いメモリ消費量 - リフレクション ベースの IoC フレームワークは、コード内のすべてのフィールド、メソッド、コンストラクタについてリフレクション データを読み込んでキャッシュしますが、Micronaut ではアプリケーションの起動時間とメモリ消費量がコードベースのサイズの制約を受けることはありません。
  • 宣言型、リアクティブ、コンパイル時 HTTP クライアント - リアクティブの HTTP クライアントを宣言的に構築します。このクライアントはコンパイル時に実装され、メモリ使用量を削減します。
  • Netty 上に構築されたノンブロッキング HTTP サーバー - Micronaut の HTTP サーバーは、習得が容易で、HTTP クライアントで使用できる API を可能な限り簡単に公開できます。
  • 迅速で簡単なテスト - 単体テストでサーバーやクライアントを簡単にスピンアップし、即座に実行できます。
  • 効率的なコンパイル時の依存関係インジェクションと AOP - Micronaut は、リフレクションを使用しないシンプルなコンパイル時アスペクト指向プログラミング API を提供しています。
  • 完全にリアクティブでブロックしないアプリの構築 - Micronaut は、RxJava や Reactor など、リアクティブ ストリームを実装するあらゆるフレームワークをサポートしています。

詳しくは、Micronaut のウェブサイトをご覧ください。

Kubernetes について

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

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

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

次の図に、この Codelab で使用されるさまざまな部分を示します。これは、要素がどのように組み合わされているのかを理解するのに役立ちます。Codelab を進める際の参考としてご利用ください。終わりに近づく頃には、すべて理解できるはずです(ただし、今は無視してかまいません)。

Kubernetes Codelab の図 1(2).png

この Codelab では、Kubernetes Engine(Google がホストする Compute Engine で実行される Kubernetes のバージョン)などのマネージド環境を使用することで、基盤となるインフラストラクチャの設定ではなく、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 クラスタを作成する方法
  • Kubernetes Engine で Kubernetes に Micronaut サービスをデプロイする方法
  • サービスをスケールアップし、アップグレードをロールアウトする
  • Kubernetes のグラフィカル ダッシュボードにアクセスする方法。

必要なもの

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

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

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

HTML/CSS ウェブアプリの作成経験をどのように評価されますか。

初心者 中級者 上級者

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

<ph type="x-smartling-placeholder"></ph> 初心者 中級 上達 をご覧ください。

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. アプリケーションをローカルで実行する

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

$ ./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 イメージを実行して、わかりやすい hello メッセージが表示されるかどうかを確認しましょう。

$ 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 する

イメージが意図したとおりに機能するようになったので、このイメージを Google Container Registry に push します。Google Container Registry は、すべての Google Cloud プロジェクトから(ただし Google Cloud Platform の外部からも)アクセス可能な Docker イメージの非公開リポジトリです。

レジストリに push できるようにする前に、[Tools] > [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 で Docker を構成して承認し、Container Registry のインスタンスにイメージを push し、レジストリ内の場所を指すようにイメージにタグを付けてレジストリに push します。

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

12224c4e42183b4e.png

8. クラスタを作成する

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

20c0587c0108b8ba.png

クラスタは、Google が管理する Kubernetes マスター API サーバーと、一連のワーカーノードから構成されます。ワーカーノードは Compute Engine 仮想マシンです。CloudShell セッションの 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 へのアプリケーションの Deployment を作成しましょう。

$ 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. 外部トラフィックを許可する

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

Cloud Shell から kubectl expose コマンドと --type=LoadBalancer フラグを組み合わせて使用すると、Pod を公共のインターネットに公開できます。外部からアクセスできる IP を作成するには、このフラグが必要です。

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

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

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

Service の一般公開されている 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

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

ブラウザでアドレス http://<EXTERNAL_IP>:8080/hello を指定して、サービスにアクセスできるようになりました。

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

Service の出力を確認すると、最初の「Hello World」に戻ります。表示されます。

14.まとめ

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

15. 完了

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

詳細

ライセンス

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