Helidonで作ったマイクロサービスをPrometheus Operatorでサクッとモニタリング

Helidonとは

HelidonはマイクロサービスなWeb APIを実装することを想定したJavaフレームワークです。以下のような特徴があります。

  • 割と軽い実装で、パフォーマンスに優れる
  • MicroProfileの実装の一つ
  • PrometheusやZipkinの形式のメトリック/トレーシングの情報を簡単に取得できる

f:id:charlier_shoe:20190207113902j:plain

このエントリーでやること

Helidonで作ったマイクロサービスをPrometheus Operatorでサクッとモニタリングしてみます。だいたい以下のような流れになります。

  1. Kubernetesクラスター上に、PrometheusとPrometheus Operatorをデプロイする
  2. Helidonでアプリを作成し、Kubernetesクラスターにデプロイする
  3. Prometheusでアプリのアクセスカウンターをモニタリングする

では順に行きたいと思います。

1. Kubernetesクラスター上に、PrometheusとPrometheus Operatorをデプロイする

CRDやらなんやらをKubernetesクラスターに作ることになるので、kubectlコマンドを実行するアカウントに必要な権限をつけておきます。 今回はKubernetesの環境として、OKE(Oracle Container Engine for Kubernetes)を利用します。この場合、以下のようなコマンドを実行します(cluster-adminロールをつけて楽しちゃってます)。

kubectl create clusterrolebinding [Cluster Role Bindingオブジェクトの名前] --clusterrole=cluster-admin --user=[kubectlの実行アカウントのOCID]

詳細はOKEのドキュメントを参照ください。

次に、適当なディレクトリにPrometheus Operatorをcloneします。

git clone -b v0.28.0 https://github.com/coreos/prometheus-operator.git
cd prometheus-operator

諸々をクラスターにデプロイします。 (2019/02/06時点でkube-prometheusはExperimentalだそうなので、今後同じ方法が使えるかは未知数です)

kubectl apply -f contrib/kube-prometheus/manifests/

Podが上がりきったのを確認したら、Prometheusのダッシュボードにアクセスします。PrometheusにのダッシュボードにつながるServiceはClusterIPで作られているので、port forwardを使います。

kubectl --namespace monitoring port-forward svc/prometheus-k8s 9090

上のコマンドを実行したら、ブラウザでhttp://localhost:9090/にアクセスします。以下のような感じでダッシュボードが表示されればOKです。

f:id:charlier_shoe:20190207013510p:plain

2. Helidonでアプリを作成し、Kubernetesクラスターにデプロイする

HelidonのアプリはクイックスタートにあるHello World的なアプリを使います。

まず、以下のコマンドを実行します。

mvn archetype:generate -DinteractiveMode=false \
    -DarchetypeGroupId=io.helidon.archetypes \
    -DarchetypeArtifactId=helidon-quickstart-se \
    -DarchetypeVersion=0.11.0 \
    -DgroupId=io.helidon.examples \
    -DartifactId=quickstart-se \
    -Dpackage=io.helidon.examples.quickstart.se
cd quickstart-se

これでHelidonのプロジェクトの雛形が作成されますが、この雛形にはすでにHello World的なAPIが実装されています。

この雛形に、カスタムメトリックとしてアクセスカウンターを追加してみます。

やり方はHelidonのドキュメントのMetrics supportに従えばOKですが、quickstartを使っている場合、必要な手順は一箇所だけ、GreetingServiceにアクセスカウンターを書き足す作業です。

vim src/main/java/io/helidon/examples/quickstart/se/GreetService.java

以下、diff形式で差分を記します。行頭に + が書かれているところを追記すればOKです。

@@ -25,6 +25,10 @@
 import io.helidon.webserver.ServerResponse;
 import io.helidon.webserver.Service;
 
+import io.helidon.metrics.RegistryFactory;
+import org.eclipse.microprofile.metrics.Counter;
+import org.eclipse.microprofile.metrics.MetricRegistry;
+
 /**
  * A simple service to greet you. Examples:
  *
@@ -42,6 +46,10 @@
 
 public class GreetService implements Service {
 
+    private final MetricRegistry registry = RegistryFactory.getRegistryFactory().get()
+        .getRegistry(MetricRegistry.Type.APPLICATION); 
+    private final Counter accessCtr = registry.counter("accessctr"); 
+
     /**
      * The config value for the key {@code greeting}.
      */
@@ -58,6 +66,7 @@
     @Override
     public void update(Routing.Rules rules) {
         rules
+            .any(this::countAccess)
             .get("/", this::getDefaultMessageHandler)
             .get("/{name}", this::getMessageHandler)
             .put("/greeting/{greeting}", this::updateGreetingHandler);
@@ -108,4 +117,9 @@
         response.send(returnObject);
     }
 
+    private void countAccess(ServerRequest request, ServerResponse response) {
+            accessCtr.inc(); 
+            request.next();
+    }
+
 }

Kubernetesにデプロイするためのmanifestファイルの雛形もあります。 後の手順でアプリをコンテナ化しますが、このときのイメージ名に合うように、manifestで指定するイメージ名を修正しておきます。

vim src/main/k8s/app.yaml

イメージ名をしている箇所で、元のイメージ名の頭に "[Docker Hubのアカウント名]/" を追記します。以下はhhiroshellというアカウント名の場合です。

@@ -43,7 +43,7 @@
     spec:
       containers:
       - name: ${project.artifactId}
-        image: ${project.artifactId}
+        image: hhiroshell/${project.artifactId}
         imagePullPolicy: IfNotPresent
         ports:
         - containerPort: 8080

コードの編集が終わったら、アプリケーションをビルドしてローカルで実行してみます。まずはビルドから。

mvn package
[INFO] Scanning for projects...
...(中略)...
[INFO]
[INFO] --- maven-jar-plugin:2.5:jar (default-jar) @ quickstart-se ---
[INFO] Building jar: /mnt/c/Users/hhiroshell/Development/devsumi/quickstart-se/target/quickstart-se.jar [INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  34.004 s
[INFO] Finished at: 2019-02-06T14:14:55Z
[INFO] ------------------------------------------------------------------------

上記の様に BUILD SUCCESS で終わっていればOKです。次に、アプリを起動します。

java -jar target/quickstart-se.jar

何回かgreetingのAPIにアクセスして、アクセスカウンターをカウントアップさせておきます。

curl http://localhost:8080/greet

Helidonは、Prometheusからメトリックを取得するためのEndpointをデフォルトで持っています。ですので、直接 /metrics/ というパスにアクセスするとメトリックを取得できます。新たにExporterを用意する必要はありません。

先程追加したアクセスカウンターは、application:accessctr という名前でメトリックを提供しています。エンドポイントにアクセスして、このアクセスカウンターの値を見てみます。

curl -s -H 'Accept: text/plain' -X GET http://localhost:8080/metrics/ | grep accessctr
# TYPE application:accessctr counter
# HELP application:accessctr null
application:accessctr 5

この様になれば、仕込んでおいたアクセスカウンターのメトリックが取得できていることになります。Prometheusの形式で値が返ってきていることが分かります。

それでは、このアプリをKubernetesにデプロイします。雛形からDockerfileやKubernetesのmanifestが生成済みなので、それを利用していきます。

まずはコンテナのビルド。

docker build -t [Docker Hubのアカウント名]/quickstart-se target

具体的には、例えばこのようなコマンドになります。

docker build -t hhiroshell/quickstart-se target

次にコンテナレジストリにプッシュ。Docker Hubを使っているので、ログインが求められたらDocker Hubのユーザー名とパスワードを入力してください。

docker push [Docker Hubのアカウント名]/quickstart-se

具体的には、例えばこのようなコマンドになります。

docker push hhiroshell/quickstart-se

続いて、kubernetesにデプロイします。

kubectl apply -f target/app.yaml

雛形から生成したmanifestだと、NodePortタイプのServiceが作られている状態です。クラスター上のアプリにアクセスするため、まずはNodeのIPアドレスと、Serviceが公開されているPort番号を確認します。

kubectl get nodes -o wide
NAME            STATUS   ROLES   AGE   VERSION   INTERNAL-IP   EXTERNAL-IP     OS-IMAGE                  KERNEL-VERSION                   CONTAINER-RUNTIME
132.145.27.98   Ready    node    2h    v1.11.5   10.0.10.2     132.145.27.98   Oracle Linux Server 7.5   4.14.35-1818.2.1.el7uek.x86_64   docker://18.3.1

この結果から、EXTERNAL-IP の値を使います(Nodeが複数表示される場合どのNodeでもOK)。

次にPort番号です。

kubectl get service quickstart-se
NAME            TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
quickstart-se   NodePort   10.96.255.64   <none>        8080:31621/TCP   7m

PORT(S)の : の右側にある値を使います。上の例では31621です。

それでは、Kubernetesクラスター上のアプリケーションにアクセスしてみます。

curl "http://[上で確認したIP]:[上で確認したPort番号]/greet"

何度かアクセスを繰り返して、アクセスカウンターの値を増やしておいてください。

これでHelidonのアプリのビルドとデプロイが完了しました。

3. Prometheusでアプリのアクセスカウンターをモニタリングする

Prometheus Operatorを使ってPrometheusを利用する場合、監視対象を追加するにはServiceMonitorというカスタムリソースを使います。これは、KubernetesのServiceがルーティング対象とするPodを、自動的にメトリック収集対象とするものです。

今回は、以下のようなカスタムリソースを、quickstart-service-monitor.yamlというファイル名で作成します。

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: quickstart-service-monitor
  labels:
    app: quickstart-se
spec:
  selector:
    matchLabels:
      app: quickstart-se
  endpoints:
  - port: http

このServiceMonitorは、Label Selectorとして app: quickstart-se と指定しています。こうすると、同じlabelを持ったServiceがルーティング対象とするPodに対して、メトリックを取得しに行くするように設定されます。

多くの場合、Prometheusがメトリックを取得しに行くEndpointにはExporterを用意しますが、HelidonはこのEndpointを予め持っており、そこから直接メトリックを得ることができます。

ではこのSerivceMonitorをクラスターに適用します。

kubectl apply -f quickstart-service-monitor.yaml

Prometeusの設定はこれだけでOKです。Operator便利ですね。

では、ブラウザでhttp://localhost:9090/にアクセスして、Prometheusのダッシュボードを表示します。

port-forwardが切れていたら、以下を実行してから上記のURLにアクセスします。

kubectl --namespace monitoring port-forward svc/prometheus-k8s 9090

Expressionに"application:accessctr"と入力してExecuteをクリックすると、アクセスカウンターの値が取得できていることがわかります(メトリックが収集させるまでタイムラグがあるので、何も表示されなかった場合は少し時間を置いてください)。

f:id:charlier_shoe:20190207013607p:plain

次に、アプリをスケールアウトした場合も自動的に監視対象に追加されることを確認してみます。

まず、以下のコマンドでアプリのPod数を3に増やします。

kubectl scale deployment quickstart-se --replicas=3

アクセス数を追加でカウントアップさせるため、アプリへのアクセスを何度か行っておきます。
(↓を何度か実行)

curl "http://[上で確認したIP]:[上で確認したPort番号]/greet"

Prometheusのダッシュボードでアクセスカウンターのメトリックを観察すると、Pod毎に値が取得できていることがわかります。

f:id:charlier_shoe:20190207013632p:plain

まとめ

Helidonで作成したアプリのカスタムメトリックを、Prometheus Operatorを使ってモニタリングすることができました。

HelidonはExporterいらずでモニタリングできて便利です。また、Prometheus Operatorを使うとカスタムリソースを使うことで簡単にメトリックの収集対象を設定できます。

本エントリーは以上です!