#Quarkus で作成したJavaアプリをAWS Lambda で動かそうとして失敗した

【2019/11/12追記】

1.0.0.CR1で動作確認できました。🎉

kdnakt.hatenablog.com

【追記ここまで】

 

どうやら2019年3月19日現在は、Quarkus(v0.11.0)で生成されたバイナリをAWS Lambda上で動かすことはできないらしい。

 

以下の記事の続きです。

kdnakt.hatenablog.com

 

[やりたいこと=Quarkus+Lambda]

Lambdaのカスタムランタイムの出番だ!と思い、カスタムランタイムの公式チュートリアルを眺めながら、Quarkusで作成したアプリ用にbootstrapを改造した。

docs.aws.amazon.com

 

改造したと言っても以下のように、 初期化処理の部分で直接Quarkusで生成されたアプリを呼び出すようにして、イベント処理部分でイベントを無視して起動したアプリにリクエストを投げるようにしただけ。

#!/bin/sh
set -euo pipefail

# Initialization: run native app made with Quarkus
EXEC="$LAMBDA_TASK_ROOT/$_HANDLER"
$EXEC

# Processing
while true
do
  HEADERS="$(mktemp)"
  # Get an event
  EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
  REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

  # Execute the handler function from the script
  RESPONSE=$(curl -X GET http://localhost:8080/hello)

  # Send the response
  curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response"  -d "$RESPONSE"
done

 

ところが動かない。

以下のようなエラーがでる。

Util_sun_misc_Signal.ensureInitialized: CSunMiscSignal.create() failed. errno: 38 Function not implemented
VMError.shouldNotReachHere: Util_sun_misc_Signal.ensureInitialized: CSunMiscSignal.open() failed.

JavaFrameAnchor dump:

No anchors

TopFrame info:

Lookup TotalFrameSize in CodeInfoTable:
SourceTotalFrameSize 96

VMThreads info:

VMThread 0000000002682010 STATUS_IN_JAVA (safepoints disabled) java.lang.Thread@0x1697838

VM Thread State for current thread 0000000002682010:

0 (32 bytes): com.oracle.svm.core.genscavenge.ThreadLocalAllocation.pinnedTLAB = (bytes) 
0000000002682010: 0000000000000000 0000000000000000
…(以下略)…

 

ググってみると同じように困っている人がいた。どうやら今の所この構成は実現できないらしい。

github.com

 

[GraalVM+Lambda?]

Quarkusプロジェクトのissueでリンクが貼られている通り、ネイティブバイナリの生成を担当しているGraalVM(今回は0.13を利用)にも同様のissueがあった。

github.com

 

こちらのissueの作者はGraalVMで生成したネイティブバイナリをAWS Lambda上で動かすのに成功している。(そういえば以前この記事を読んだことがあるような……)

qiita.com

 

AllowVMInspection オプションは、SEGV などのシグナル起因でヒープダンプを出すようなオプションなのですが、これを無効にしないと Lamdbda では動きませんでした。

EC2 上なら動くのに Lamdba では動かないのは謎ではありますが、 issue を報告しています。 シグナルの扱いとかに制約があるのかな? ネイティブ周りは良くわからないです。。

 

どうやらいくつかのオプションを指定すればいけるらしい。GraalVM本体がLambdaで動くのならば、GraalVMを利用しているQuarkusでもいけるに違いない。

ということで、pom.xmlで指定されているQuarkusのnative imageコマンドにいくつかのオプションを試してみた。が、上記ブログとQuarkusのmavenプラグインのソースを参考に、additionalArgsなどを追加したものの、効果はなし。同じエラーが出つづける。

<plugin>
  <groupid>io.quarkus</groupid>
  <artifactid>quarkus-maven-plugin</artifactid>
  <version>${quarkus.version}</version>
  <executions>
    <execution>
      <goals>
        <goal>native-image</goal>
      </goals>
      <configuration>
        <enablehttpurlhandler>true</enablehttpurlhandler>
        <additionalbuildargs>
          <arg>-H:-AllowVMInspection</arg>
          <arg>-R:-InstallSegfaultHandler</arg>
          <arg>-H:+ReportUnsupportedElementsAtRuntime</arg>
        </additionalbuildargs>
      </configuration>
    </execution> 
  </executions>
</plugin>

 

確かにAWS Lambda実行環境とされるAMI(amzn-ami-hvm-2017.03.1.20170812-x86_64-gp2)をベースに起動したEC2インスタンス上では、問題なくQuarkusで生成したバイナリを起動することができた。

Lambdaの実行環境に特別な制約がかかっているのだろう。

docs.aws.amazon.com

 

しかし、EC2上で起動できたということはAWS環境でQuarkusを動かすことに望みがない訳ではない。アプリの要件によっては、Quarkusで生成したDockerイメージをそのままECRあたりに放り込んで、コンテナ系のサービスやスポットインスタンス上で必要に応じて動かしていくのもありかもしれない。

 

[まとめ]

とまあ色々模索しているのは、既存のJavaソースコードをできるだけ生かしつつ、AWS上にサービスを構築したいと考えているからだ。

Quarkusに拘らず、 本当にやりたいこと=「既存Java資産を生かす」ために、発表当時にあまり注目できなかったGraalVMにも目を向けていきたい。