【2019年10月版】GraalVMでNative化した Javalin のコンテナをcAdvisorでチェックしてみる


どういうこと?

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 [email protected]:tipsy/graal-javalin.git

2. docker-compose.yaml の記述

cloneはしましたがgraal-javalinディレクトリには入らず、引き続き同じディレクトリで以下の docker-compose.yaml を作成します。

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 [email protected]: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がネイティブ化対応できているか、については引き続き調査が必要ですが、GraalVMでネイティブ化したJavaアプリが超軽量なコンテナとしてバンバンDockerやK8sにデプロイできるようになると・・・ちょっと胸熱ですね!

本日はここまでと致します!