プログラマよ、Webアプリケーションにヘルスチェック機能を入れてくれ!
ここではサーバーの構築というよりシステム運用/サーバー運用の立場から開発の人達にお願いしたいこと、アプリケーションの設計段階で運用サイドから依頼したいことについて触れています。
端的に言うと、サーバーで動かすプログラムに正常動作を監視/確認できる仕組みを入れて欲しい、ということです。
それを具現化する例として、Javaのフレームワーク Spring Framework より、Spring Boot Actuator というものを例としてご紹介します。
Spring Bootのリンクはこちら。
Spring Boot
Actuatorについては独立したドキュメントを見つけられませんでしたので、あえて載せるならここかなと。
本番対応機能
Spring Frameworkは凄いもので、これのおかげで Javaでの Webアプリケーション作成が非常にシンプルになって、コード量が激減したと思います。
Spring Bootは Spring Frameworkの数多の機能を取り纏めるテンプレートを提供してくれるツールで、これのおかげで更に楽になりました。
これまで Javaサーブレットといえば、Tomcatサーバーを構築し、そこに warファイルをデプロイするという運用をしたものですが、Spring Bootでは組み込み Tomcatといって、Tomcatを内包している全部入り jarファイル 1つ(fat jar)が生成されるようになりました。
これはアプリケーションのポータビリティが高まる事を意味していて、Tomcatのようなサーブレットエンジンのミドルウェアがなくても Javaさえ動く環境ならどこでもサーバーとして動きます。
ですのでパブリッククラウドの PaaSや Kubernetesの podなどのアプリケーションコンテナと非常に親和性が高いです。
でもシステム運用管理者の立場から言わせてもらうと
アプリケーション実行場所の自由度高まる ≒ 監視しづらくなる
なのです。
これまでのアプリケーション生死確認といえば、サーバー上で対象のプロセスが必要数動いているかを監視するとか、動作ログを監視するとか、アプリケーションプログラムの外郭部分を監視するやり方が多いと思います。
しかし PaaSや アプリケーションコンテナサービスといったインフラたるサーバーに手を入れられない環境では従来の方法が使えません。
そこで何をするかというと、アプリケーションそれ自体に監視のための機能(ヘルスチェック)を提供させる設計が登場してきます。
Spring Frameworkを使った Javaアプリ運用において DB管理者の私がどうしても欲しかったのは、DB接続を統括するコネクションプール(Spring Boot では HikariCPが使われています)の状態確認方法でした。
「DBに繋がらないんだけど!(ドウニカシテ)」
と本当によく言われてチビりそうになりますよね。
すぐに DBサーバーに問題がないか調査しますけど、同時に DBクライアントであるアプリケーション側の状態も知りたいのです。
しかしコネクションプールを使われて、プログラマがあまり意識しないクラスでコネクションが作られているとデータフローの障害調査に空白地帯ができてしまいます。
なんでサバ管がそれを拾わにゃならんのか…
なのでコネクションプールがどんな状態になっているかは定期的に確認したい事項なのです。
この運用要件はオンプレだろうがパブリッククラウドだろうが変わりませんが、確認の手法において、サーバーローカルでやれるかネットワーク上の別ノードからしかできないかで話が大分変わってきます。
多分世界中に同じ想いに至ったエンジニアが一杯いて、Spring Boot Actuator というものができたんでしょうし、HikariCPの状態を Actuatorで取れるようにしたのでしょう。
Spring Boot Actuatorでどんなことができるかを確認するため、適当な Webアプリを作りました。
プログラマではない私が DBアクセスまでする Webアプリを作れてしまう程に Spring Bootは凄いのであります。
環境は以下です。
- Java18 (OSは Ubuntu 22.04でやってます)
- MySQL8
- Spring Boot 2.7.1
Spring Initializrでプロジェクトを作った時に組み込んだ Dependencyは以下です。
- Lombok
- Spring Web
- Thymeleaf
- MySQL Driver
- MyBatis Framework
- Spring Boot Actuator
まぁ良くある構成だと思います。
最後の「Spring Boot Actuator」を選択するだけでアプリケーションの様々なメトリクスを取得できるエンドポイントを提供してくれるようになります。
できあがった Webアプリは MySQLにあるテーブルのデータを表示するだけのものです。
以下が作成した Webアプリの実行開始画面で、見ていただきたいのはピンク色の箇所です。
この Webアプリの本体機能が [/] で提供されている傍らで [/actuator]エンドポイントが提供されています。
subro@Ubuntu2204:~/work/java/actuatordemo/target$ java -jar ./actuatordemo-0.0.1-SNAPSHOT.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.1)
2022-07-04 10:57:15.525 INFO 7970 --- [ main] org.subro.actuatordemo.SubroApplication : Starting SubroApplication v0.0.1-SNAPSHOT using Java 18-ea on Ubuntu2204 with PID 7970 (/home/subro/work/java/actuatordemo/target/actuatordemo-0.0.1-SNAPSHOT.jar started by subro in /home/subro/work/java/actuatordemo/target)
2022-07-04 10:57:15.529 INFO 7970 --- [ main] org.subro.actuatordemo.SubroApplication : No active profile set, falling back to 1 default profile: "default"
2022-07-04 10:57:18.649 INFO 7970 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2022-07-04 10:57:18.682 INFO 7970 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-07-04 10:57:18.683 INFO 7970 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.64]
2022-07-04 10:57:18.908 INFO 7970 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-07-04 10:57:18.909 INFO 7970 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3215 ms
2022-07-04 10:57:21.005 INFO 7970 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator'
2022-07-04 10:57:21.106 INFO 7970 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2022-07-04 10:57:21.142 INFO 7970 --- [ main] org.subro.actuatordemo.SubroApplication : Started SubroApplication in 6.76 seconds (JVM running for 7.848)
MySQLのテーブルデータの一覧を出すだけの、こんな画面が出るだけのアプリです。
なお [application.properties]ファイルで、Actuatorが提供する情報として以下のように [health] と [metrics] を選択しています。
spring.datasource.url=jdbc:mysql://localhost:3306/subrotest
spring.datasource.username=root
spring.datasource.password=********
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
management.endpoints.web.exposure.include=health,metrics
では早速 Actuatorのエンドポイントにアクセスしてみましょう。
subro@Ubuntu2204:~$ curl -sXGET http://localhost:8080/actuator | jq
{
"_links": {
"self": {
"href": "http://localhost:8080/actuator",
"templated": false
},
"health": {
"href": "http://localhost:8080/actuator/health",
"templated": false
},
"health-path": {
"href": "http://localhost:8080/actuator/health/{*path}",
"templated": true
},
"metrics": {
"href": "http://localhost:8080/actuator/metrics",
"templated": false
},
"metrics-requiredMetricName": {
"href": "http://localhost:8080/actuator/metrics/{requiredMetricName}",
"templated": true
}
}
}
[/acruator/health] でこのアプリが正常に稼働しているか確認できます。
この確認には HikariCPの状態確認も含まれているそうなので、ここが [UP] になっていれば、アプリが問題なく稼働している事の確認としては充足していそうです。
subro@Ubuntu2204:~$ curl -sXGET http://localhost:8080/actuator/health | jq
{
"status": "UP"
}
続いて [/actuator/metrics] です。
hikaricpで始まっている値が HikariCP関連メトリクスの名前です。
subro@Ubuntu2204:~$ curl -sXGET http://localhost:8080/actuator/metrics | jq
{
"names": [
"application.ready.time",
"application.started.time",
"disk.free",
"disk.total",
"executor.active",
"executor.completed",
"executor.pool.core",
"executor.pool.max",
"executor.pool.size",
"executor.queue.remaining",
"executor.queued",
"hikaricp.connections",
"hikaricp.connections.acquire",
"hikaricp.connections.active",
"hikaricp.connections.creation",
"hikaricp.connections.idle",
"hikaricp.connections.max",
"hikaricp.connections.min",
"hikaricp.connections.pending",
"hikaricp.connections.timeout",
"hikaricp.connections.usage",
"http.server.requests",
"jdbc.connections.max",
"jdbc.connections.min",
"jvm.buffer.count",
"jvm.buffer.memory.used",
"jvm.buffer.total.capacity",
"jvm.classes.loaded",
"jvm.classes.unloaded",
"jvm.gc.live.data.size",
"jvm.gc.max.data.size",
"jvm.gc.memory.allocated",
"jvm.gc.memory.promoted",
"jvm.gc.overhead",
"jvm.gc.pause",
"jvm.memory.committed",
"jvm.memory.max",
"jvm.memory.usage.after.gc",
"jvm.memory.used",
"jvm.threads.daemon",
"jvm.threads.live",
"jvm.threads.peak",
"jvm.threads.states",
"logback.events",
"process.cpu.usage",
"process.files.max",
"process.files.open",
"process.start.time",
"process.uptime",
"system.cpu.count",
"system.cpu.usage",
"system.load.average.1m",
"tomcat.sessions.active.current",
"tomcat.sessions.active.max",
"tomcat.sessions.alive.max",
"tomcat.sessions.created",
"tomcat.sessions.expired",
"tomcat.sessions.rejected"
]
}
[/actuator/metrics/hikaricp.connections.idle] が使われていないコネクションの数だと思いますので取ってみました。
MAXで 10個のコネクションを作れるようにしていまして、現時点では 10個空いていますね。
subro@Ubuntu2204:~$ curl -sXGET http://localhost:8080/actuator/metrics/hikaricp.connections.idle | jq
{
"name": "hikaricp.connections.idle",
"description": "Idle connections",
"baseUnit": null,
"measurements": [
{
"statistic": "VALUE",
"value": 10
}
],
"availableTags": [
{
"tag": "pool",
"values": [
"HikariPool-1"
]
}
]
}
アプリケーションがどのようなインフラの上で動いていようが、このように アプリそのものに内部の状態を出してくれる機能があれば、あとはお好きな所から定期的にアクセスして記録をグラフにでもすれば、運用状態の把握に貢献できると思います。
以上、Spring Boot Actuatorを例にしましたが、最近のアプリケーションではこういうのが流行っていると思います。
フルスクラッチのシステムでもこのような機能を実装してもらえると運用側としては非常に助かります。
なお、Node.jsで動かすサーバーサイド JavaScriptの世界でも、Expressフレームワークで使える Spring Boot Actuatorを真似た express-actuator なんてのがあったりします。