谷本 心 in せろ部屋

はてなダイアリーから引っ越してきました

Spring Boot 3.0アプリケーションをGrafanaスタックで可視化してみた。

Spring Boot 3.0でMicrometer対応が強化されたとか、トレース情報を収集するSpring Cloud SleuthがMicrometerに入ったと聞き、この辺りはしばらく追いかけてなかったので、この機会にまとめて学び直すことにしました。

今回作ったものはGitHubに置いてあります。 github.com もう2023年になってしまいましたが、作り始めたのが2022年なので spring-store-2022 となっています。

イキってREADMEを英語で書く習慣がついてしまったので、代わりにこのブログで日本語の説明を書きたいと思います。工夫したところやハマったポイントなどの話はまた別途ブログを書くとして、今回はこのアプリケーションの概要と動かすところまで説明します。

1. ゴール

マイクロサービスのアプリケーションをこんな風に可視化するところがゴールです。

Grafanaでマイクロサービスのトレースを表示

2. サンプルアプリケーションの概要

サンプルアプリケーションは、いわゆるECサイト的なマイクロサービスを想定したものです。商品一覧を見る、カートに入れる、購入するなどができ、購入すると非同期で手配やクレジットカードの決済が行われるという動きを模倣しています。

サービスの構成は次の図の通りです。

マイクロサービスの構成
見やすさのため一部の処理を割愛していますが、いずれにせよBFF(Backend for Frontend)を介して各サービスを利用する形です。

なお今回作るのはサーバサイドのみです。フロントエンドは2019年にvueで作ったものを再利用したかったのですが、残念ながらすでにコンパイルが通らない状況になってしまっていたので諦めてしまいました。誰か助けて。

利用する技術スタック

  • Java 17
  • Spring Boot 3.0 (without Spring Cloud)
  • Grafana、Loki、Tempo、Prometheus、Promtail
  • Kubernetesk8sがなくても動く)

可視化はGrafanaスタックのみでメトリクス、ログ、トレースを見られるようになったので、これに一本化することにしました。

Grafanaスタックを使ったモニタリングのインフラ

また令和のマイクロサービスアプリケーションは一般的にコンテナとしてデプロイされるものであり *1 もはやデファクトスタンダードな基礎技術となっているk8s *2 にもデプロイできるようにしておきました。

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

ローカル環境ではDockerでミドルウェアを立ち上げ、各マイクロサービスアプリケーションをIDEから起動する形で実行します。

(0) 必要なもの

ソースコードhttps://github.com/cero-t/spring-store-2022 からダウンロードするなりcloneするなりしてください。

(1) ミドルウェア(RabbitMQとGrafanaスタック)の起動

spring-store-2022/docker ディレクトリに移動します

cd docker

docker-composeでミドルウェアを起動します

docker-compose up -d

(2) アプリケーションの起動

IDEを使ってアプリケーションを起動してください。

Mavenspring-boot:run コマンドはプロジェクトの構成上、使えません。僕これSpring Maven Pluginのバグだと思ってるんですけどね。

(参考) cero-t.hatenadiary.jp

(3) アプリケーションへのアクセス

BFFでSwagger UIを使えるようにしているため、これを利用します。

http://localhost:9000/swagger-ui.html

  1. catalog-controller: GET /catalog
    • 商品一覧の名前や値段、画像などを取得できます
  2. cart-controller: POST /cart
    • カートを作成して cartId を取得できます
  3. cart-controller: POST /cart/{cartId}
    • カートに商品を追加します
    • 追加する itemId/catalog で取得できる商品のIDを指定してください
  4. cart-controller: GET /cart/{cartId}
    • カートに入った商品の詳細と合計金額を確認できます
  5. order-controller: POST /order
    • 商品を購入できます。と言ってもメールが送られたりクレジットカードの決済が発生することはありません
    • cardExpireMM/yy 形式で年月を入れてください
    • cartId は POST /cart で取得したIDを取得してください

/order にPOSTするボディの例

{
  "name": "Shin Tanimoto",
  "address": "Tokyo",
  "telephone": "0123456789",
  "mailAddress": "hello@example.com",
  "cardNumber": "0000111122223333",
  "cardExpire": "12/24",
  "cardName": "Shin Tanimoto",
  "cartId": "1"
}

これでエラーが出なければ、アプリケーションの操作は完了です。

(4) Grafanaのダッシュボードを確認

アプリケーションがどう動いたのかをGrafanaで確認します。

http://localhost:3000/

左メニューのコンパスのアイコン(Explore)をクリックします

GrafanaのExplore

上のドロップダウンリストから Loki を選択します

Lokiを選択

Label filters に app = bff を指定して右上の Run query ボタンを押します。アプリからLokiにログが送られるまで1分ほど掛かるので、選択できない場合やログが出ない場合は少し待ってから実行してください。

Lokiのクエリを実行

おそらく一番上にある CHECKOUT が含まれるログをクリックします

CHECKOUTのログ

TraceID のところに Tempo のボタンがあるのでこれをクリックします

TraceIdの隣にあるTempoボタン

トレースが表示されましたね!

トレースが右側に出た!

これで分散トレーシングの情報を可視化することができました。他にもPrometheusを使ったメトリクスの可視化などもできますが、説明はいったん割愛します。

(5) アプリケーションを終了させる

それぞれのアプリケーションをIDEで停止させてください

続いて、ミドルウェアを停止させます

docker-compose down

これで環境が綺麗になりました。

4. アプリケーションをKubernetes上で実行する

続いて、k8s上でも実行してみましょう。アプリケーションや設定ファイルは何も変更することなく、k8sマニフェスト環境変数を使って設定を上書きする形で起動します。

k8sにデプロイする際に application-k8s.properties などを別に作ってプロファイルを切り替える方法のもよく使われていと思いますが、僕としては「アプリケーションが特定のインフラを意識しない」というか、「k8s → アプリケーションの参照は良いけど、アプリケーション → k8sに相互参照させない」ということを意識しているので、k8sマニフェストで上書きすることにしています。

(0) 必要なもの

(1) k8sを起動する(minikubeの場合のみ。Docker Desktopの場合はk8sを有効にするだけでOK)

minikubeを十分なリソースで起動します

minikube start --cpus=4 --memory=4096

環境変数を設定して、minikubeのイメージリポジトリが利用されるようにします

eval $(minikube docker-env)

(2) アプリケーションのイメージをビルドする

spring-store-2022/services ディレクトリに移動します

cd services

アプリケーションイメージを作成します

sh build-image.sh

(3) k8sミドルウェアをインストールする

spring-store-2022/k8s ディレクトリに移動します

cd k8s

ネームスペースを作成します(必須ではありません。他のコンテナなどもk8s上に置いていて環境を分けたい場合のみ作成してください)

kubectl create ns spring-store-2022

Helmのリポジトリを追加します

helm repo add grafana https://grafana.github.io/helm-charts

Tempoをインストールします(ネームスペースを使う場合は -n spring-store-2022 を引数に追加してください)

helm install tempo grafana/tempo -f helm/tempo-config.yml

loki-stackをインストールします。これはGrafana、Prometheus、Loki、Promtailをまとめてインストールできるものです(ネームスペースを使う場合は -n spring-store-2022 を引数に追加してください)

helm install loki-stack grafana/loki-stack -f helm/loki-stack-config.yml

Lokiは起動に1〜2分くらい掛かるようで、それまではコネクションエラーなどが起きることがありますが、しばらく待てば正常に動き始めます。

(4) アプリケーションをk8sにデプロイする

アプリケーションをデプロイします(何度も言いますが、ネームスペースを使う場合は -n spring-store-2022 を引数に追加してください)

kubectl apply -f ./deploy

これでまとめてアプリケーションとRabbitMQがk8s上にデプロイされます。

(5) ポートフォワードを設定する

ローカル環境からアクセスできるよう、ポートフォワードを設定します。ネームスペースを使っている場合は -n spring-store-2022 を引数に追加してください。

Grafanaへのポートフォワードを設定します

kubectl port-forward svc/loki-stack-grafana 3000:80

minikubeを使っている場合は、BFFアプリケーションにポートフォワードを設定します。Docker Desktopの場合はこの手順をスキップしてもアクセスできるようになっています。

kubectl port-forward svc/bff-svc 9000:9000

これでk8s上でもローカル環境と変わらずアプリケーションを利用でき、モニタリングされていることを確認できるはずです。

アプリケーションの使い方やGrafanaの使い方は上で説明した通りですので割愛します。

(6) アプリケーションを終了させる

アプリケーションをk8sから削除します

kubectl delete -f ./deploy

ミドルウェアをアンインストールします

helm uninstall loki-stack
helm uninstall tempo

ネームスペースを作成していた場合はネームスペースを削除します

kubectl delete ns spring-store-2022

これで環境が綺麗になりました。

5. 所感

Grafanaでメトリクス、ログ、トレースのすべての可視化ができるようになっており、またSpring Bootからも簡単に情報が送れるようになっていることはなかなか体験が良く、今後はこれを自分の中での標準技術にしていきたいという気持ちです。

ただ今回、Spring BootのMicrometer対応や、Grafanaスタック、あとHelmも初めて使ってみたわけですが、思った以上に細かいところでハマってしまい、トータル2週間くらい格闘し続けた形になりました。

特にGrafanaスタックはアップデートが早くてHelm Chartがそれに追従していないとか、Helm Chartの設定に問題があって回避しなければいけないなどがあり、「コマンド一発で動くようになる」という状況ではありませんでした。デバッグに近いことをやり続けたおかげで、ずいぶんと知識はついたのですが、良くも悪くもbreaking changeが発生するくらい活発に開発中ということなんですね。

冒頭にも書きましたが、その辺りの細かい話や工夫したところは、また改めてブログを書きたいと思います。

(余談)過去に作っていたアプリケーション

ちなみにこれまで同じアプリケーションをJava 8や11でも作っていました。

2016年バージョン(Java 8 + Spring Boot 1.x + Spring Cloud Stream、Eureka、Hystrix、Sleuth + Zipkin) github.com

2019年バージョン(Vue.js + Java 11 + Spring Boot 2.x + Spring Cloud Stream、Eureka、Sleuth + Elasticsearch + Kibana + Elastic APM + Zipkin) github.com

その当時に僕が興味を持っていた技術がそのまんまスタックに現れてくるのがちょっと面白いですね。昨年、Dapr版を作ればよかったですね。

参考サイト

今回のアプリケーションを作成するに辺り、Grzejszczakさんのブログエントリーを参考にさせていただきました。 spring.io

GrzejszczakさんはSpringのObservability関連のプロダクトを開発されている方です。今後も活動を応援したいと思います!

*1:個人の感想です

*2:あくまでも個人の感想です