Yoshi.dev

技術系の趣味、またはやった仕事やそこから学んだことを忘れないために

Kotlin×springbootアプリをAWS Lambdaで動かしてみた

Kotlinとspringbootで作ったアプリをAWS Lambdaにデプロイした時のメモです

バージョン

kotlinVersion = '1.2.21'  
springBootVersion = '2.0.3.RELEASE'  
gradleVersion = '4.2.1'

Kotlinサンプルコード

package com.test.batch.job

// import省略

@SpringBootApplication(exclude = [
AopAutoConfiguration::class, 
DataSourceAutoConfiguration::class,
EmbeddedServletContainerAutoConfiguration::class, 
WebMvcAutoConfiguration::class, 
JmxAutoConfiguration::class, 
PersistenceExceptionTranslationAutoConfiguration::class, 
RedisAutoConfiguration::class, 
WebClientAutoConfiguration::class])
@ComponentScan(basePackages = ["com.test.batch", "com.test.core"])  // 適宜変更
class SampeLambdaHandler: RequestHandler<Request, Response> {

    @Autowired
    lateinit var sampleService: SampleService  // Autowiredもできる

    override fun handleRequest(input: Request?, context: Context?): Response {
        val ctx = SpringApplication.run(HolidayCalendarUpdateHandler::class.java)
        ctx.autowireCapableBeanFactory.autowireBean(this)
        val app = ctx.getBean(HolidayCalendarUpdateHandler::class.java)
        app.run {
            val lambdaLogger = context?.logger

            lambdaLogger?.log("hello lambda world")

            sampleService.doSomething()
        }

        return Response()
    }

}

class Request

class Response

@SpringBootApplicaiton でexcludeにあるAutoConfiguration系のクラスを除外しないと、Lambdaの処理が終了してから一定時間開かないうちに、再度Lambdaの処理を走らせたときに javax.management.InstanceAlreadyExistsException というエラーが発生するかもしれない
これはLambdaのタイムアウト時間を5分に設定している場合、処理が終わってもLambdaのコンテナは5分経つまで生き残るっぽくて
そしてその状態で再度Lambdaを走らせたとき、同じ実行コンテナが再利用され、そこに残ってるspringbootのプロセスとぶつかり上記のExceptionが投げられるみたい
excludeで指定されているものは不要と思われるなものは適宜取り除けばいいと思う

RequestHandler interfaceを使用したため、gradleには

compile('com.amazonaws:aws-lambda-java-core:1.2.0')

を追加した

また、AWS Lambdaにアップロードするためのzipファイルを作成するgradle taskを登録する
build.gradleに下記を追加

task buildZip(type: Zip) {
        from compileKotlin
        from processResources
        into('lib') {
            from configurations.runtime
        }
    }

buildZipを実行すると/build/distributionsというディレクトリ内にzipファイルができた
あとはLambdaにzipファイルをアップロードし、上記の例であればハンドラに com.test.batch.job.SampeLambdaHandler::handleRequest を指定
ランタイムは Java8 とする
springのprofileで環境を切り替えている場合は、Lambdaの設定で環境変数SPRING_PROFILES_ACTIVE を設定すると良さそう

参考
Java で Lambda 関数を作成するためのプログラミングモデル - AWS Lambda
.zip デプロイパッケージの作成 (Java) - AWS Lambda
Running Spring Integration as AWS Lambda Function — Serverless

関係ないけど、最近家でリモートするときスタンディングで作業するようにしてるのだが、足元にひくこのマットが足が痛くならなくて良い感じ