どういうこと?
Javalin で構築した WebAPIのサーバーを GraalVM でネイティブの単一のバイナリとしてコンパイルし、さらに Docker イメージ化してコンテナとして動かしてみちゃうけど、ぶっちゃけどんくらい軽量なのよ?というのを確認するために cAdvisor + Prometheus & Grafana でコンテナの使用リソースをチェックしてみましょう、といった相変わらず情報量の多いウザめの記事となっておりスマ。はい、すいません。。。
動作環境
OS: macOS Mojave 10.14.6
Docker: Docker Desktop Mac版 2.1.0.3 (Engine: 19.03.2, Comose: 1.24.1)
Graalvm SDK: 1.0.0-rc-6
Javalin: 2.2.0
Docker、Docker Compose が動く環境であればWindowsでも大丈夫かと思います。
Javalinについては深く触れませんが、Jettyをラッピングした軽量な(?)Webフレームワークです。
それでは早速、参りましょう。
Javalin on Graal on Docker の構築
GraalとJavalinが最新版ではありませんが、今回は本家チュートリアル(?)に従いたいと思います。
こちらの記事ではJavalinの簡易WebアプリをGraalVMでビルドし、そのままDockerコンテナにしてしまう手順が紹介されています。
さらに scratch イメージに構築する(実行形式バイナリそのままにする)ことで 22MBという極小サイズでJavaのWebアプリをコンテナ化できているらしいです。(実測はしてません。)
それでは早速、構築していきましょう。
1. サンプルのソースコードを入手
いきなりチートですが、今回のゴールはcAdvisor(+Prometheus+Grafana)でコンテナのチェックするところまでなので、Javalinのサンプル実装は構築済みの成果物をまるごと利用させていただきたいと思います。
以下のリポジトリをcloneしてしまいましょう。
$ git clone git@github.com:tipsy/graal-javalin.git
2. docker-compose.yaml の記述
cloneはしましたがgraal-javalin
ディレクトリには入らず、引き続き同じディレクトリで以下の docker-compose.yaml
を作成します。
version : "3"
services:
javalin-graalvm:
build:
context: graal-javalin
ports:
- 7000:7000
stdin_open: true
tty: true
clone してきたコードはそのまま動かしてしまいます。
3. Javalin on Graalvm のコンテナを実行
さて以下のコマンドでさっさと起動してしまいましょう。
$ docker-compose up -d
そして curl でポート 7000番を叩いてみると・・・
$ curl http://localhost:7000
{"someValue":"Hello World!"}
というわけでサクッとサンプルで構築した(?)REST APIの結果がjsonで返ってきました。
素晴らしい〜!
3-1. 余談:コンテナイメージ scratch
について
上記のコンテナをビルドする Dockerfile では、GraalVMが含まれるコンテナでビルドしたバイナリを scratch
イメージにコピーしてコンテナ化するという2段階でイメージをビルドしていますが、scratch
というイメージは特殊な空っぽのイメージ
です。
つまり、GraalVMでビルドされたバイナリのほぼそのままが docker のプロセスによってキックされており、docker プロセスが動いている環境(OSやマシンのアーキテクチャ)が同じでないと動作しません。
今回は検証目的ですので気にせずいきますが、実際のプロダクション環境での採用はなかなか難しいと思います。上記の記事はあくまで"scratchで22MBしかないんだぜ!俺スゲー!"という内容です。察してあげてください。
cAdvisor, Prometheus, Grafana を一撃で動かす
cAdvisor, Prometheus, Grafana のサービスを動かしてみましょう。
以下の記事に紹介されているプロジェクトが今回には持ってこいですね!
というわけで "dockprom" のクローンを作成致します。
相変わらずディレクトリはそのままで・・・
$ git clone git@github.com:stefanprodan/dockprom.git
そして、docker-compose up
で立ち上げてしまいます!
$ cd dockprom
$ docker-compose up -d
...
Creating alertmanager ... done
Creating prometheus ... done
Creating caddy ... done
Creating grafana ... done
Creating pushgateway ... done
Creating nodeexporter ... done
Creating cadvisor ... done
イメージのダウンロードにはそこそこ時間がかかりますが気長に待ちます。しばらくして、コンテナが全て作成し終わったところで grafana
コンテナが待ち受けている http://localhost:3000
にアクセスしてみましょう。
Grafana のダッシュボードをのぞいてみると・・・
初回は、パスワードのリセットなどありますが、そのまま問題なく使用できるはずです。いや〜、素晴らしい。。。
非常に簡単に Grafana 立ち上がるのでちょっと感動してしまいましたが気を取り直して、画面左上の"Dashboards"から Docker Containers
を選んでください。先ほど立ち上げた、javalin-graalvmのコンテナが表示されていると思います。
賢いことにPrometheus,Grafanaなど監視用のコンテナは表示されず、監視用コンテナ以外のコンテナだけのstat情報が表示されているはずです。
私の環境では上のような状態です。http://localhost:7000
を叩いてJSON吐き出させてみましたが、メモリ使用量、20MBも行かないです。
Oh.... か、軽い。。。
画面のグラフはかなりリソースの消費量が急上昇しているように見えますが、そもそものメモリが5MiB単位なんで、小さな世界なんです。It's a Small World
!!
そもそもHello World
返すだけのプログラムですけれどもぉ、Jetty+JRE相当のバイナリ同梱してますしぃ・・・JVMは動くだけでも数100MBオーダーのメモリは消費してしまうんではなかったかと!
Spring や Micronaut も・・・
Micornaut や Spring も着々と GraalVM対応が進んでいるようです。
- GraalVM の native image を使って Java で爆速 Lambda の夢を見る
- Spring Boot ApplicationをMicronaut for Springでnative-imageにしてみる
どこまでGraalVMがネイティブ化対応できているか、については引き続き調査が必要ですが、GraalVMでネイティブ化したJavaアプリが超軽量なコンテナとしてバンバンDockerやK8sにデプロイできるようになると・・・ちょっと胸熱ですね!
本日はここまでと致します!