この記事は公開されてから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