Slide 1

Slide 1 text

NEALLE Kotlin + Quarkus を Lambda SnapStart で動かす - 仕組みと事前ウォームアップ 2026.10.14 Server-Side Kotlin Meetup vol.20 株式会社ニーリー 宮後 啓介 @miya10kei 1

Slide 2

Slide 2 text

自己紹介 Keisuke Miyaushiro 宮後 啓介 株式会社ニーリー プラットフォーム本部 プロダクトAI開発 リーダー 2023年にニーリーにジョイン SREリードとしてサービスの信頼性やアジリティ向上の施策を実施。 2025年よりプロダクト/事業部門での生成AI活用を推進するチームを立 ち上げ。 現在は新規プロダクト開発とCSS業務の効率化を担当 @miya10kei 2

Slide 3

Slide 3 text

プロダクト紹介 「Park Direct」は駐車場の募集から契約、決済、管理までをオンライン で完結させるモビリティ領域のOperating System(Mobility OS)です。 3

Slide 4

Slide 4 text

目次 1. SnapStart の仕組み 2. 事前ウォームアップ 4

Slide 5

Slide 5 text

SnapStart の仕組み 5

Slide 6

Slide 6 text

SnapStartの仕組み - 背景 外部SaaSからのWebhookを受けるAPIに「Kotlin + Quarkus on Lambda」を利用中! 【Lambdaを採用した理由】 1. 2. 3. リクエスト数が時間帯で大きく変動する 急激なスパイクが想定される Web APIと負荷特性・可用性要件が異なる → 常時稼働によるコストを抑えたい → 高速なスケールアウトが求められる → 分離して低コストで運用したい → LambdaにはCold Startという課題があるのでそこへのアプローチを紹介します! Kotlin + Quarkusを選択した経緯はこちらで紹介しています! 「どうして今サーバーサイドKotlinを選択したのか @ Server-Side Kotlin LT大会 vol.19」 6

Slide 7

Slide 7 text

SnapStartの仕組み - LambdaでKotlinを動かす時の選択肢 - Cold Start 追加料金 アプリ側の対応 On-Demand 長い なし なし Provisioned Concurrency 確保分は発生しない あり なし SnapStart 短い なし 事前ウォームアップ GraalVM Native 最も短い なし Native Build対応 Cold Startによる遅延を抑えつつ、低コストで運用可能なSnapStartを選択! → 将来的にはGraalVM Nativeも視野に入れている 7

Slide 8

Slide 8 text

SnapStartの仕組み - CRaC(Coordinated Restore at Checkpoint)CRaCはJVM の起動とウォームアップを短縮する OpenJDK プロジェクト → 初期化済のイメージを事前作成し、復元して起動を高速化する仕組み 通常起動 起動のたび JVM起動 アプリ初期化 初回リクエスト処理 (クラスロード等で遅い) JVM起動 アプリ初期化 beforeCheckpoint() (保存直前のフック) afterRestore() (復元直後のフック) リクエスト処理 リクエスト処理 CRaC 事前に1回 起動のたび 復元 保存 イメージ 8

Slide 9

Slide 9 text

SnapStartの仕組み - SnapStart SnapStart は Lambda の Cold Start を短縮する AWS の機能 → 初期化済の実行環境のスナップショットを事前作成し、復元して起動を高速化する仕組み Cold Startのたび バージョン発行時 実行環境(microVM) JVM起動 アプリ初期化 beforeCheckpoint() afterRestore() リクエスト処理 afterRestore() リクエスト処理 afterRestore() リクエスト処理 復元 保存 スナップショット 9

Slide 10

Slide 10 text

SnapStartの仕組み - CRaCとSnapStartの関係 SnapStart は CRaC のフックの API だけを採用している → 保存と復元の仕組みは Lambda 独自で、アプリは CRaC と同じ API でフックを書く アプリ フックの実装を登録 Resource / 共通API(org.crac) beforeCheckpoint() / afterRestore() フックを呼び出す CRaC対応JDK フック呼び元:JDK 保存対象 :JVMプロセス 保存手段 :CRIU ● ● Lambda SnapStart フック呼び元:Lambda Runtime 保存対象 :microVM 全体 保存手段 :Firecracker CRIU(Checkpoint/Restore In Userspace): 動作中の Linux プロセスの状態を保存し、そこから再開できるツール Firecracker: AWS が開発した軽量の仮想マシンモニター。Lambda の実行環境(microVM)を動かしている 10

Slide 11

Slide 11 text

事前ウォームアップ 11

Slide 12

Slide 12 text

事前ウォームアップ - Quarkusに任せられるところと任せられないところ QuarkusのAWS Lambda拡張(quarkus-amazon-lambda)を入れるだけで、SnapStart最適化を 利用できる! 有効化することでQuarkusが次のクラスプリロードを実行する 【自作クラス】 【ライブラリ】 起動処理で利用するクラス Quarkusが自動ロード 【ライブラリ】 リクエスト処理で 利用するクラス 自前でのプリロードが必要 12

Slide 13

Slide 13 text

事前ウォームアップ - 自前でのプリロードの手段 - Class Priming Invoke Priming 「META-INF/quarkus-preload-classes.txt」 に対象クラスを記述することでプリロード対 象にする手法 beforeCheckpoint()で実際の処理を呼び出す ことでプリロードをする手法 実行経路全体がプリロードの対象になる 【例】 com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal com.fasterxml.jackson.annotation.JsonAlias com.fasterxml.jackson.annotation.JsonFormat$Feature com.fasterxml.jackson.core.exc.InputCoercionException ・・・ 実行 クラスA クラスB クラスC クラスD ※ ライブラリ更新への追従を考慮し採用! 13

Slide 14

Slide 14 text

事前ウォームアップ - Invoke Primingの実装方法 @Startup // 起動時にインスタンスを作らないと、フックが登録されない @ApplicationScoped class HelloPriming : Resource { @PostConstruct fun init() { Core.getGlobalContext().register(this) // フックを登録する } // スナップショットを取る直前に呼ばれる override fun beforeCheckpoint(context: Context) { // ここでリクエスト処理を 1 回実行する = Invoke Priming } // 復元した直後に呼ばれる override fun afterRestore(context: Context) { // 復元のたびに必要な処理を書く(接続の張り直しなど) } } 14

Slide 15

Slide 15 text

事前ウォームアップ - Invoke Primingの実行対象 リクエストが通る以下の処理を、それぞれの方法でInvoke Primingの実行対象とした。 ● ● ● ● ● ● 署名検証 ○ ダミー署名の検証 設定データの読み込み ○ 固定のJSONデータをパース レスポンスの生成 ○ 固定値での全要素の組み立て DBの読み取り ○ 未登録キーでのデータ読み取り DBの書き込み ○ 書き込みSQLの組み立て(DBへの書き込みまではしない) 外部HTTP送信 ○ ダミー宛先へのHTTP呼び出し 15

Slide 16

Slide 16 text

事前ウォームアップ - 副作用 Invoke Primingで開いた接続(DB/外部サービス)が、スナップショットに含まれる副作用がある → 復元後の接続は使えないため、スナップショットに含めない工夫が必要 ① Invoke Priming実行 ② 保存 ③ 復元 スナップショット アプリ アプリ DB アプリ DB アプリ 接続を確立 接続ごと保存される 復元された接続は使用不可 16

Slide 17

Slide 17 text

事前ウォームアップ - 副作用への対処(DB/HTTP) 1.DB接続のライフサイクルをSnapStartのフックで制御することで対処 quarkus.datasource.jdbc.initial-size=0 quarkus.datasource.jdbc.min-size=0 # 起動時の接続作成をOFF # 接続の作り直しをOFF // スナップショットを取る直前に呼ばれる override fun beforeCheckpoint(context: Context) { dataSource.flush(AgroalDataSource.FlushMode.ALL) // 接続をフラッシュ } // 復元した直後に呼ばれる override fun afterRestore(context: Context) { dataSource.connection.use { it.isValid(1) } // 接続を張り直す } 2.HTTP接続はKeep AliveをOFFにすることで対処 quarkus.rest-client.webpush-priming.keep-alive-enabled=false 17

Slide 18

Slide 18 text

まとめ ● SnapStart は「起動済みの状態を保存して復元する」ことで Cold Start を短縮する ○ ● Quarkus が自動でプリロードするのは自作クラスまで ○ ● リクエスト処理で使うライブラリのクラスは、自前でのプリロードが必要 Invoke Priming なら、実行経路ごとプリロードできる ○ ● アプリからは CRaC の API(フック)で、保存の直前と復元の直後に処理を挟める クラス一覧の管理が不要で、ライブラリ更新にも追従できる Invoke Priming の副作用(接続がスナップショットに入る)への対処が必要 ○ DB 接続は SnapStart のフックで制御、HTTP 接続は Keep Alive を OFF 18

Slide 19

Slide 19 text

Thank you 19