takeda_san’s blog

KotlinとVRを頑張っていく方向。

Serverless Java Containerをやっていく

きっかけ

AWS Lambdaちからが欲しい…!というわけでAlexaスキルをちょいちょいやっていたけど、これではギョーミングLambdaちからが育たない…というわけで
Serverless Java ContainerでギョーミングLambdaをやっていこう。

Serverless Java Containerって?

レダッ!
https://github.com/awslabs/aws-serverless-java-container

といってもわけわかんないです。
ReadMe見ただけでどんなものなのかわかる人いますけど、どうなってんですかね。

https://github.com/awslabs/aws-serverless-java-container/wiki

AWS LambdaとAmazon API Gatewayを使用して、Spring、Jersey、またはSparkアプリケーションを簡単に実行できます。

解説動画もあるよ!
わかりやすい…ありがてぇ…
サーバレスで王道 Web フレームワークを使う方法|AWS Summit Tokyo 2017 - YouTube

サーバレスでAPI作り出すとLambdaがとんでもない量になるので、Spirngとかで作りたくなる気持ちよくわかる。

Spring Bootのクイックスタートを試す

ご丁寧にSpring Bootでのチュートリアルがあるのでやっていきましょう。 Quick start Spring Boot · awslabs/aws-serverless-java-container Wiki · GitHub

クイックスタートを試す(その通りにやるとは言ってない)

そのままサンプルプロジェクトをcloneしてきて手順通りやるのもよいのですが、せっかくなので自分で作りましょう。
そっちのほうがお勉強になりますからね。

github.com

というわけで、いつも通り、Spring Initializrでプロジェクトを作成。
https://start.spring.io/
依存はWebDevToolsを入れました。

build.gradleに例のライブラリを追加。

compile('com.amazonaws.serverless:aws-serverless-java-container-spring:1.1.4')

Lambdaのハンドラを作る

Spring部分にリクエストを送るハンドラを作る。

ハンドラ

class StreamLambdaHandler : RequestStreamHandler {
    private var handler: SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse>? = null

    init {
        try {
            handler = SpringLambdaContainerHandler.getAwsProxyHandler(LambdaconteinerApplication::class.java)

        } catch (e: ContainerInitializationException) {
            // if we fail here. We re-throw the exception to force another cold start
            e.printStackTrace()
            throw RuntimeException("Could not initialize Spring framework", e)
        }
    }

    @Throws(IOException::class)
    override fun handleRequest(inputStream: InputStream, outputStream: OutputStream, context: Context) {
        handler!!.proxyStream(inputStream, outputStream, context)

        // just in case it wasn't closed by the mapper
        outputStream.close()
    }
}

あとは、動作確認用のコントローラをつくる。
この辺はもうSpringの世界の話なのでなれたものですね。

@RestController
class SpringController {
    @GetMapping("")
    fun printMessage(): String {
        return "こんにちは!"
    }
}

デプロイしよう!

Lambda関数の作成と、モジュールをzipで固めてアップロード。
この辺Serverless Frameworkみたいにひゅーんとコマンド一発でできると良いんだけれども、方法がわからんのでAWSのコンソール画面から心を込めてアップロードしました。

20180912追記:  
コメントいただいたおかげで、Serverless Frameworkでもできました!  
記事の最後に追記してます。  

zipの固め方は過去にこのへんで書きました。
takeda-san.hatenablog.com

f:id:takeda_san:20180909004614p:plain 今回呼び出すハンドラは、com.example.lambdaconteiner.StreamLambdaHandler::handleRequest

動作確認のためのテストを作成します。
今回だとAPI Gateway AWS Proxyを選んで、Controllerに合わせてリクエスト内容を編集します。

ルートにてGETでリクエストをうけとるので、こんな感じ。
"httpMethod": "GET", "path": "/"

いざ、テスト。

{
  "statusCode": 200,
  "headers": {
    "Content-Encoding": "UTF-8",
    "Content-Length": "18",
    "Content-Type": "text/html;charset=UTF-8"
  },
  "body": "こんにちは!",
  "base64Encoded": false
}

おー、やった。
ちゃんとログにもSpringのログが出てます。
がっつりバッチ処理とかはメモリとか処理時間の関係で無理かもですが、軽めのやつなら良いかも。

20180912追記:Serverless Frameworkでやっていく

コメントにてServerless Frameworkでやっていく方法を教えてもらったので、いざ実践。
(id:nkgrさんありがとうございます!)

sls create --template aws-java-gradle --path [プロジェクト名]
cd [プロジェクトのフォルダ]
gradle wrapper --gradle-version 4.9

gradleのwrapper作り直してるのはこの辺の話を参照。

takeda-san.hatenablog.com

んで、ソースコードをエイヤッとコピー。
あと、Spring Boot関連のgradle設定を移植、serverless.ymlのhandlerを自分で作ったクラスのFQCNで編集。
ビルドを通して、zipを生成したらレッツデプロイ。

sls deploy -v

無事Lambda関数ができたら、AWSのコンソールから動作確認。

{
  "statusCode": 200,
  "headers": {
    "Content-Encoding": "UTF-8",
    "Content-Length": "18",
    "Content-Type": "text/html;charset=UTF-8"
  },
  "body": "こんにちは!",
  "base64Encoded": false
}

やった、次からデプロイが高速でできるぞ。
未来を感じる。