Spring Native の sampleアプリを Serverlessでデプロイして lambdaで動かす


モチベーション

大量リクエストを低コストでさばく AWS Lambda 関数を JVM で実現 と言う記事 が 2021-10-05 に builders.flash で書かれていました。私は現場でECS上で動くSpringアプリケーションの運用保守をしていますが、その経験から見るとこの記事に書かれている事はとても魅力的に見えます。この記事に端を発し類似する記事をいくつか調べると以下の事が実現できると言う事が書かれています。

  • GraalVM の native-image と呼ばれる技術 を使いJava アプリのネイティブ化する事で省メモリ、高速を両立する
  • lambdaで 動かす事でスパイクアクセスにも対応できる、またコストも抑えられる
  • 月間数十億のリクエストのサービスを支えている事例がある

そこで実際にSpringアプリケーションをlambdaで動かしてみたいと思いましたが、そのやり方が簡単に分かる記事がなかなか見つかりません。(筆者はインフラエンジニアであり、Springアプリケーションの知識に乏しい事にも原因があるかもしれません)

色々と調査・検証した結果、Springアプリケーションをネイティブ化、Serverlessで API Gateway + lambdaをデプロイし、Hello world的な事が実行できたのでその内容を記事に致します。

何をするか

  • Spring Native の sample アプリ cloud-function-aws を Serverless でデプロイし動作確認をします
  • 同アプリにhandlerを追加しそれを同様に Serverless でデプロイし動作確認をします

なお作業環境はMacです。

前提条件

  • 作業環境に以下のソフトウェアがインストールされている
    • Docker for Desktop
    • Serverless
  • AWSアカウントがあり作業環境からアクセスできる

検証内容

ソースを取得

git clone https://github.com/spring-projects-experimental/spring-native.git
cd spring-native/samples/cloud-function-aws

ビルド

README -> Build project (MAC users) の通りに実行しNativeイメージをビルドするとzipファイルが作成されます

cd ../../
./run-dev-container.sh

cat /etc/os-release
# Ubuntu OSになっています

cd samples/cloud-function-aws
./build.sh
# 3:35

exit
# コンテナから抜けます

cd samples/cloud-function-aws
ls -ltr target/cloud-function-aws-0.0.1-SNAPSHOT-native-zip.zip
# zipファイルができています

前述の記事にも書かれていますが、ネイティブ化は起動が早くなりますがビルドには時間がかかります

Serverless設定、デプロイ

serverless.yml を作成します。(Githubで見つけたこちらの内容を参考にしました)

frameworkVersion: '3'

service: cloud-function-demo

provider:
  lambdaHashingVersion: 20201221
  name: aws
  stackName: cloud-function-aws
  apiName: cloud-function-aws
  region: ap-northeast-1
  runtime: provided
  memorySize: 128
  timeout: 3
  tracing:
    lambda: true
    apiGateway: true

package:
  individually: true

functions:
  NameUppercase:
    name: name-uppercase
    handler: nameUppercaser
    package:
      artifact: ./target/cloud-function-aws-0.0.1-SNAPSHOT-native-zip.zip
    events:
      - http:
          integration: lambda
          path: /name-uppercase
          method: post
          request:
            template:
              text/plain: '{ "name": "$input.body" }'

デプロイします。

sls deploy

動作確認

curl -X POST -H 'Content-Type: text/plain' https://rjd3qmhu4k.execute-api.ap-northeast-1.amazonaws.com/dev/name-uppercase -d 'hello world!'
# "hi HELLO WORLD!!"

handlerの追加

1つのアプリケーションに複数のhandlerがあっても問題なく動作するのかが気になりましたが、
spring cloud function の sample に function-sample-aws-routing があったのでできそうだと思い確認しました

cd src/main/java/com/example/demo/
cp NameUppercaser.java NameLowercaser.java
sed -i -e "s/Upper/Lower/g" NameLowercaser.java
cd ../../../../../..

ビルド

cd ../../
./run-dev-container.sh
cd samples/cloud-function-aws
./build.sh
exit
cd samples/cloud-function-aws

serverless.ymlに追記

  NameLowercase:
    name: name-lowercase
    handler: nameLowercaser
    package:
      artifact: ./target/cloud-function-aws-0.0.1-SNAPSHOT-native-zip.zip
    events:
      - http:
          integration: lambda
          path: /name-lowercase
          method: post
          request:
            template:
              text/plain: '{ "name": "$input.body" }'

デプロイ

sls deploy

動作確認

curl -X POST -H 'Content-Type: text/plain' https://rjd3qmhu4k.execute-api.ap-northeast-1.amazonaws.com/dev/name-uppercase -d 'hello world!'
# "hi HELLO WORLD!!"
curl -X POST -H 'Content-Type: text/plain' https://rjd3qmhu4k.execute-api.ap-northeast-1.amazonaws.com/dev/name-lowercase -d 'HELLO WORLD!'
# "hi hello world!!"

無事動作確認する事ができました!

後片付け

Serverlessを使ったので後片付けも簡単です

sls remove

今回の記事は以上となります。参考になれば嬉しいです。