軽量かつネイティブに動くJavaを求めてQuarkus 2.0をやってみてLambdaにデプロイした

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Javaを尋ねて25年...

こんにちは!みなさんJava使っていますか?私は学生のときにJavaを使い始めて、コミュニティと出会い、仕事にもなって、勢いでサンタクララの本社に突撃したことがあるぐらいJavaが好きでした。そして、時が経ち、軽量で軽快に動く他の言語やフレームワークが出てきたことで、若い方がJavaを使う機会が減ってきたなぁと感じています。多くのエンプラ現場で使われてきて実績十分、起動するまでが遅いけど動き出したらパフォーマンス良好、エコシステムが出来上がっているJavaを、もっと楽しく簡単に、できれば軽快に動く形で使えないものかと、モヤモヤしている中、昨年Quarkusという面白いものを見つけてしまいました。そして、昨日バージョン2.0がリリースされましたので、試してみたいと思います。

Quarkusとは

Quarkusは、Kubernetesなどのコンテナ上での動作に最適化されたフレームワークです。Red Hat社がサポートしていて、Apacheライセンスの下で公開されています。Quarkusを使うことで、メモリ使用量を小さくすることができ、ブート時間も短くなります。更にネイティブアプリとしてコンパイルできるため、メモリ使用量1/10、起動時間1/100を実現するようなとんでもない代物です。サーバーレスやコンテナ環境で使用するメモリ量や起動時間が減れば、そのままコストに大きく影響しますので、試してみる価値は十分にあるフレームワークです。

マシン環境

  • macOS Big Sur version 11.4
  • MacBook Pro (13インチ, 2020, Thunderbolt 3ポート x 4)
  • プロセッサ 2.3 GHz クアッドコアIntel Core i7
  • メモリ 32 GB 3733 MHz LPDDR4X
  • グラフィックス Intel Iris Plus Graphics 1536 MB

環境構築

まずは必要となる各種ミドルウェアのセットアップを行います。まずは以下のソフトウェアをダウンロードしておいてください。

Dockerを起動

今回、最終目標としてLambda上で動くカスタムランタイムとして、ネイティブコンパイルされたコードを実行しようと思っています。コンパイルする環境としてMacOSでは、実行ファイルはそのままLambda上で動きません。そこで、Amazon Linux 2上でコンパイルします。

Dockerをダウンロードしてインストールしたあと、Amazon Linux 2のImageをローカルに持ってきます。

$ docker pull amazonlinux

以後、このImageから起動したコンテナ内に入っての処理となります。

基本的なライブラリ等のセットアップ

まずは基本から

$ bash
$ yum update -y
$ yum install wget tar git java maven awscli unzip gcc zlib-devel -y
$ yum groupinstall "Development Tools" -y
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
$ brew --version
Homebrew 3.2.1

GraalVMのセットアップ

$ cd /opt/
$ wget https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-amd64-21.1.0.tar.gz
$ tar -xzf graalvm-ce-java11-darwin-amd64-21.1.0.tar.gz

$ vi /root/.bash_profile

export PATH=/opt/graalvm-ce-java11-21.1.0/:$PATH
export PATH=/home/linuxbrew/.linuxbrew/bin:$PATH
export JAVA_HOME=/usr/lib/jvm/java-11-amazon-corretto.x86_64/

$ source ~/.bash_profile

$ gu install native-image

JavaのSDKを切り替え

$ alternatives --config java
java -version
openjdk version "11.0.11" 2021-04-20 LTS
OpenJDK Runtime Environment Corretto-11.0.11.9.1 (build 11.0.11+9-LTS)
OpenJDK 64-Bit Server VM Corretto-11.0.11.9.1 (build 11.0.11+9-LTS, mixed mode)

SAMのセットアップ

$ brew tap aws/tap
$ brew install aws-sam-cli

$ sam --version
SAM CLI, version 1.26.0

サンプルプロジェクトのセットアップ

公開されているプロジェクトからLambdaにネイティブコードを載せる例があましたので紹介します。

$ cd /usr/local/src/
$ git clone https://github.com/quarkusio/quarkus-quickstarts.git
$ cd /usr/local/src/quarkus-quickstarts/funqy-quickstarts/funqy-amazon-lambda-http-quickstart/
$ ./mvnw clean install -Dnative

次にSAMを使ってLambdaにデプロイします。先にアクセスキーの設定を忘れずに。うまくいくと、AWS側でCloudFormationが走りまして、API Gateway + Lambdaの環境が自動生成されます。

$ aws configure

$ sam deploy -t target/sam.native.yaml -g

動作確認

AWSのマネジメントコンソールからLambdaにデプロイされているか確認します。

CloudFormationからLambdaとAPI Gatewayが作成されていることが分かります。

プロパティを見ると、カスタムランタイムとしてデプロイされていることが分かります。

ブラウザなどから動作確認してみましょう。実際に動作確認できました。

$ curl https://nxct3826wb.execute-api.ap-northeast-1.amazonaws.com/hello
"Hello World"

ソースコードはこちら

package org.acme.funqy;
import io.quarkus.funqy.Funq;

public class PrimitiveFunctions {
    @Funq
    public String hello() {
        return "Hello World";
    }

    @Funq
    public String toLowerCase(String val) {
        return val.toLowerCase();
    }

    @Funq("double")
    public int doubleIt(int val) {
        return val + val;
    }


}

こちらはパラメータ付きの例

$ curl https://nxct3826wb.execute-api.ap-northeast-1.amazonaws.com/greet?name=akari
{"message":"Hello akari"}

ソースコードはこちら

package org.acme.funqy;
import io.quarkus.funqy.Funq;
import javax.inject.Inject;

public class GreetingFunction {
    @Inject
    GreetingService service;

    @Funq
    public Greeting greet(Friend friend) {
        Greeting greeting = new Greeting();
        greeting.setMessage(service.greet(friend.getName()));
        return greeting;
    }
}

まとめ

Quarkusを用いたことで、Javaのコードをネイティブにコンパイルして、SAMでローカルで動作確認をし、最後にLambdaにデプロイして動作するところまで確認できました。デプロイされたコードは、14MBでカスタムランタイム上で実行されるため、Lambdaの動作に必要なメモリは最小限で済み、起動時の遅延もありません。理想的な形となりました。今回は、Lambdaにデプロイしましたが、コンテナイメージとして作ることもできますので、実際の応用範囲は広いと思います。

参考資料

QUARKUS - AMAZON LAMBDA WITH RESTEASY, UNDERTOW, OR VERT.X WEB