Introducing Micronaut® AOT: build-time optimizations for your Micronaut applications

原文はこちら。
The original article was written by Cédric Champeau (Consulting member of technical staff, Oracle Labs).
https://medium.com/graalvm/introducing-micronaut-aot-build-time-optimizations-for-your-micronaut-applications-68b8f1302c5

Micronaut® Frameworkは、Micronaut Foundation[1]が支援するモダンなフルスタック・ツールキットです。本日、Micronaut Foundationは、Micronautファミリーの新しいモジュールであるMicronaut AOT(ahead-of-time、この文脈ではビルド時を意味する)を発表しました。Oracle Labsが開発したMicronaut AOTは、オープンソースの拡張フレームワークで、ビルド時に動的解析を適用し、Micronautアプリケーションを特定の展開環境に最適化するものです。

Micronaut AOTは、ターゲットとなるデプロイメント環境やテクノロジースタックに応じて、アプリケーションをさまざまに最適化できます。例えば、アプリケーションをJVMアプリケーションとして実行するか、GraalVM Native Imageでビルドされたネイティブ実行ファイルとして実行するかによって、特定の最適化を生成することが可能です。

例として、GraalVM Native ImageでコンパイルされたシンプルなMicronautアプリケーションの起動時間を見てみましょう。

10:56:08.699 [main] INFO io.micronaut.runtime.Micronaut — Startup completed in 133ms. Server Running: http://localhost:8181

アプリケーションは133msで起動したことがわかります。すでに高速な立ち上がりではありますが、びっくりするほどではありません。ネイティブアプリケーションでは、これより速い起動を見たことがあります。連続して複数回試すと、122ms、145msなど、かなりのばらつきがあることがわかります。私のマシンでは、800msを超える起動時間の場合もありました。この挙動を理解するためには、デフォルトで有効になっている特定の機能について説明する必要があります。フレームワークは、実行時にデプロイメント環境を推測し、デプロイ時にアプリケーションを、例えばOracle CloudやAWS向けに特別に設定します。このために、フレームワークがネットワークリクエストを含むいくつかのチェックを行う必要がありますが、これらはすべてアプリケーションのスタートアップで行われるのです。同じアプリケーションの起動時間をMicronaut AOTで最適化した後、どのようになるかを見てみましょう。

11:04:05.373 [main] INFO io.micronaut.runtime.Micronaut — Startup completed in 7ms. Server Running: http://localhost:8181

同じアプリケーションが7msで起動しました。再試行しても、この値はあまり変動せず、6msから8msでした。すごいですね。これはMicronaut AOTが異なる最適化を実行しているためです。その中には環境の検知を実行時からビルド時に移動させることも含まれます。もちろん、これはアプリケーションがデプロイ対象の環境と同じ環境でビルドされていなければなりませんが、これはDevOpsチームが起動時間の改善とコスト削減のために実施するトレードオフのようなものなのです。

Optimizations for the JVM too

Micronaut AOTは、ネイティブアプリケーションの最適化だけでなく、「従来の」JVM上で動作するアプリケーションに対しても同じことができます。ネイティブイメージとJVMバイトコードに対して異なる最適化を生成できる最適化フレームワークを持つことが設計目標でした。これを説明するために、JVM(またはJIT、just-in-timeコンパイラ)モードでの同じアプリケーションの起動時間を見てみましょう。まず、Micronaut AOT最適化なしの場合です。

11:22:36.852 [main] INFO io.micronaut.runtime.Micronaut — Startup completed in 723ms. Server Running: http://localhost:8181

当然ながら、起動時間はネイティブモードに比べて長くなっています。では同じアプリケーションを最適化した場合の起動時間をみてみましょう。

11:23:30.696 [main] INFO io.micronaut.runtime.Micronaut — Startup completed in 419ms. Server Running: http://localhost:8181

基準のJVMモードでの起動時間の58%にまで短縮できました。ほぼ2倍の高速化です。

Micronaut AOTによる起動時間の改善

How does it work?

Micronaut AOTでは、ビルド中に様々な処理を実行してアプリケーションの起動時間やデプロイメントサイズを削減します。bean要件を事前に計算し、ビルド時に置換を行うことで、実運用で使用する予定のクラスのみを含めることができます。上記の例では、ネイティブの実行ファイルサイズが55MBから53MBに削減されました。今後、新たなMicronaut AOTのリリースにより、メモリ消費量、起動性能、画像サイズのさらなる改善が期待されます。Micronaut AOTは、拡張性を考慮して設計されていますので、他のMicronautライブラリは、独自の最適化を提供することができます。

端的に言うと、Micronaut AOTはMicronautアプリケーションを分析し、ソース、リソース、メタデータを生成するツールキットで、ビルドツールがこれらを利用して最適化されたアプリケーションを生成します。

AOT最適化されたネイティブアプリケーションのビルドフロー (異なる生成ソースを持つJVMでも同じワークフローが適用される)

現在提供している最適化は以下の通りです。

サービス・ロードの最適化利用可能なサービスのリストを事前にスキャンし、JVMでは完全に並列、GraalVMネイティブ実行ファイルでは直列のロード戦略を実装することで、サービス・ロードを最適化する(ネイティブ実行ファイルではクラスロードが事実上コストゼロになるため)。
YAMLの設定をJavaの設定に変換アプリの起動を高速化するとともに、YAMLのパースが不要になるため最終的なバイナリサイズを小さくできる。
環境のキャッシュ一度アプリケーションが起動すれば、フレームワークはシステムプロパティと環境変数が変更されないと仮定し、検索を実行する時間を短縮する。
bean要件の事前計算Bean の要件を事前に計算し、実行時に要件を満たさない Bean を排除する。これは、使用しない Bean をもたらす推移的依存関係がある場合などに起こり得る。
ビルド時の環境推論上記の例で使用した主要な最適化
高コストな処理の事前計算例えば環境変数名をMicronaut構成プロパティに変換するような処理は事前に計算する
クラスロードの最適化クラスパス上にないことがわかっているクラスの検索を避けることで、クラスロードを最適化する

これらの最適化はすべてオプトインであり、その意味を理解した上で使用する必要があります。デプロイと同じ環境でビルドする必要があるものもあれば、まったく関係ないものもあります。アプリケーションやユースケースによって、使用可能な範囲は異なる場合があります。

Trying it out

Micronaut AOT は現在、Micronaut フレームワークのバージョン 3.2 以上と Micronaut Gradle プラグインのバージョン 3.1.0 以降を使用している場合のみ利用可能です(Maven への対応は将来的に追加予定です)。

Micronaut gradle plugin
https://micronaut-projects.github.io/micronaut-gradle-plugin/latest/

Micronaut AOTはまだ実験的な段階です。バグ報告や改善要望は、遠慮なくMicronaut AOTのGitHubリポジトリでIssueを立ててください。現時点では、AOTはアプリケーションの最適化のみ可能です(将来的には、ライブラリやクラウドのFunctionsの最適化も可能になる予定です)。

Micronaut AOT
https://github.com/micronaut-projects/micronaut-aot/

利用にあたっては、ビルドスクリプトにaot設定ブロックを追加するio.micronaut.aotプラグインを利用します(以下のコードはGroovyとKotlinの両方のGradle DSLで動作します)。

Gradle plugin: io.micronaut.aot
https://plugins.gradle.org/plugin/io.micronaut.aot

plugins {
id("io.micronaut.application") version "3.1.1"
id("io.micronaut.aot") version "3.1.1"
}
...
micronaut {
...
aot {
version.set("1.0.0-M5")
cacheEnvironment.set(true)
optimizeServiceLoading.set(true)
optimizeClassLoading.set(true)
convertYamlToJava.set(true)
precomputeOperations.set(true)
}
}

これだけでいいんです。これらのGradleタスクのいずれかを実行して、AOT最適化されたMicronautアプリケーションを試すことができます。

./gradlew nativeOptimizedCompileAOT最適化されたネイティブイメージをビルドする
./gradlew nativeOptimizedRunAOT最適化されたネイティブイメージをビルドした上で、ビルド後すぐにアプリケーションを開始する
./gradlew optimizedRunJVMモードでビルドした上で、ビルド後すぐにアプリケーションを開始する(AOTネイティブアプリケーションではなく、jarファイルの実行)

Micronaut AOTはAOTに最適化されたDockerイメージのビルドをサポートします。これは、Micronaut Gradleプラグインがデフォルトで行うのと同様です。

利用可能なタスクや設定の詳細については、GradleプラグインのドキュメントのAOTのセクションを参照してください。

Micronaut AOT plugin
https://micronaut-projects.github.io/micronaut-gradle-plugin/latest/#_micronaut_aot_plugin

Summary

Micronaut AOTはMicronaut Frameworkの拡張機能で、アノテーション処理だけでは実現できなかったビルド時の最適化を数多く実現する基盤です。デプロイ環境を効果的に分析することで、Micronaut AOTはネイティブモードとJVMモード両方の成果物で、起動時間や配布サイズを削減できます。


[1] Micronaut® はObject Computing, Inc.の登録商標です。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中