雑多に技術メモと他色々

主に自分用な技術メモが多くなる気がする。他色々が書かれるかどうかは不明。

Lambdaのjava環境をOpenJDKからCorrettoに移行すると利用料金が上がるお話

タイトルは多少誇張したもの。

移行の経緯

AWS Lambdaで利用可能なJavaランタイムをOpenJDKからCorrettoに移行するとの発表があった。
AWS LambdaのJava 8ランタイムがAmazon Correttoへ。OpenJDKから移行するとAWSが発表 - Publickey

これにより、java+Lambdaの利用者は順次ランタイムの移行を行う必要がある。
「CorrettoはAWSが開発してるし、そんな変なことは起きないだろ」と油断してたら刺されるかもしれないという内容。

Correttoにするとメモリが足りない!?

移行検証のために、ランタイムだけ変更して検証を行ったところ、OutOfMemoryErrorが発生してエラーになるという話が出てきた。
確かに利用メモリが最小構成の128MBだったとはいえ、そんなことあるか?ということで、一応検証もしてみることに。

検証の実施

メモリ使用量を出力する処理の実装~jarファイル作成

こちらのページを参考にEclipseでせっせこプロジェクトを作成。
いまからはじめるJavaでAWS Lambda(ラムダ) 前編 - Qiita

InputやOutputは一切不要で、その替わりににメモリの状態を出力するようにハンドラーのコードは下記の通りとした。

package com.sample.lambdatest;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.sample.lambdatest.LambdaTest.Input;
import com.sample.lambdatest.LambdaTest.Output;

public class LambdaTest implements RequestHandler<Input, Output> {

	@Override
	public Output handleRequest(Input in, Context context) {
		System.out.println("free  : " + Runtime.getRuntime().freeMemory());
		System.out.println("max   : " + Runtime.getRuntime().maxMemory());
		System.out.println("total : " + Runtime.getRuntime().totalMemory());
		final Output out = new Output();
		return out;
	}

	public static class Input {
	}

	public static class Output {

	}

}

mavenで引き込むaws-lambda-java-coreは1.2.1にバージョンアップした上で、maven packageでFatJarを固める。

AWS上で検証用Lambda関数を作成

マネジメントコンソールからGUIポチポチやって、テスト用の関数を作成。

設定箇所 設定値(設定方法)
コード > コードソース 検証用なので、前手順で作成したjarファイルをローカルからそのままアップロード
コード > ランタイム設定 > ハンドラ 実装ソースに合わせてcom.sample.lambdatest.LambdaTest::handleRequestに変更
設定 > メモリ (MB) 128に変更

実行してメモリ使用量を検証

ランタイムを「Java 8」と「Java 8 (Corretto)」で切り替えて、テストを実行。
利用メモリに差分が出るか確認する。

検証してみた結果

CloudWatchLogsの出力は下記の通り。

f:id:yamakisso:20210630221038p:plain
OpenJDKの場合
f:id:yamakisso:20210630221058p:plain
Correttoの場合

totalやfreeの値は置いといて、Correttoに移行するとなぜかJava仮想マシンが利用可能と認識するメモリサイズが「約110MB」から「約90MB」程度に低下してしまっている。
検証コードは実処理を行っていないので余裕があるが、FarJarが重量になっているケースや、実業務向けのコードでそれなりにメモリを消費してギリギリを攻めている場合だと、この20MB程度の差で最大ヒープサイズをオーバーする/しないが変化してしまいそうである。

動かなくなった場合に考えられる対応策

  • メモリを拡張する(1MB単位で刻めるため、少々気持ち悪いが148などでも設定はできる)
  • メモリ使用量を抑えるように処理をチューニングする
  • メモリ使用量を抑えるようにFatJarサイズが小さくなるように最適化する

とりあえずメモリ拡張が単純ではあるが、Lambda料金はCPU使用時間 * メモリでかかっていくため、無料枠に収まらないレベルでガンガン実行している関数の場合、AWS利用料金に跳ねてしまう。最小構成128MBで実行できていたものだと、20MB程度の差分を埋めるために148MBにするだけで料金15%アップなので、なかなか憚られる。

対して、元々が無料枠に収まる程度であれば、それほど考えずにメモリ拡張で良いのではないだろうか。

移行すると料金が上がる件まとめ

  • OpenJDKからCorrettoに移行すると微妙に利用可能なヒープサイズが減るっぽい。
  • メモリ使用量ギリギリのところを攻めていたアプリケーションだと、メモリ拡張やチューニングが必要になりそう。
  • 128MBのようなメモリ最小構成から拡張をする場合、10%~20%程度料金アップに跳ねるかも。(キリ良く256MBにすると料金倍…)
  • FaaSなのでメモリ最小限で関数を大量に並べるユースケースも結構ありそう。できればAWS側でも改善して欲しい。
    • ただ、このケースでのLambda利用時にJavaで実装するか?という疑問は多少ある。
  • なぜ利用可能なヒープサイズが減少しているのか見えてない。デフォルトのヒープ割当設定が違うなど?今後余裕があれば調査。