Edited at

Amazon LambdaでScala

More than 3 years have passed since last update.

AWSのLambdaって、元々は、Node.jsベースの環境で、JavaScriptしか実行できませんでした。

つい先日ですが、Java8も実行できるようになりました。

http://aws.typepad.com/aws_japan/2015/06/aws-lambda-update-run-java-code-in-response-to-events.html

つまりそれって、JVMで実行できるってことですよね?じゃあScalaも動きますよね?

ってことで、やってみた。

コードは こちら


AWSでJava8を動かす方法

Scala云々の前に、まずはJavaでどーやるのか知っておかないとねってことで、主に、

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/java-lambda.html

を読みました。

ので、読んでね。


activator new

まずは、プロジェクトを作らないとねってことで、いつものこれを実行しました。

$ activator new                                                                                                                                                                                                       [~/workspace]

Fetching the latest list of templates...

Browse the list of templates: http://typesafe.com/activator/templates
Choose from these featured templates or enter a template name:
1) minimal-akka-java-seed
2) minimal-akka-scala-seed
3) minimal-java
4) minimal-scala
5) play-java
6) play-scala
(hit tab to see a list of all templates)
> 4
Enter a name for your application (just press enter for 'minimal-scala')
> aws-lambda-scala
OK, application "aws-lambda-scala" is being created using the "minimal-scala" template.

To run "aws-lambda-scala" from the command line, "cd aws-lambda-scala" then:
/home/vagrant/workspace/aws-lambda-scala/activator run

To run the test for "aws-lambda-scala" from the command line, "cd aws-lambda-scala" then:
/home/vagrant/workspace/aws-lambda-scala/activator test

To run the Activator UI for "aws-lambda-scala" from the command line, "cd aws-lambda-scala" then:
/home/vagrant/workspace/aws-lambda-scala/activator ui

これで、 aws-lambda-scala ってプロジェクトディレクトリができる。


activator eclipse with-source=true

次に、いま作ったプロジェクトをEclipseで扱えるようにしたい。

ちょっと前のactivatorならnewした時点で、eclipseプラグインが最初っから読み込まれていたんだけど、いまは自分で仕込む必要があるので、仕込む。


project/plugins.sbt

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")


で、

$ ./activator                                                                                                                                                             

...
> eclipse with-source=true

みたいなことをすると必要なパッケージをとってきてくれるので、あとはEclipseで、既存プロジェクトをインポートする操作をしてやったらいい。


AWS SDK

Lambdaで動くよーにするために必要なLibraryを取得してくる。

けど、厳密にいうと、Lambdaで動かすだけなら、POJO でもいい。

が、S3のStreamで動かしたいとか、KinesisのStreamで動かしたいとか、そういったときにそれらからデータを取得してくるのに、SDKがあると便利だよねってことで使うことがあるから、一応、取ってくる。


build.sbt

val Version = "0.1-SNAPSHOT"

lazy val root = (project in file(".")).settings(
name := "aws-lambda-scala",
version := Version,
organization := "com.example",
scalaVersion := "2.11.7",
libraryDependencies ++= Seq(
"com.amazonaws" % "aws-lambda-java-core" % "1.0.0",
"com.amazonaws" % "aws-lambda-java-events" % "1.1.0",
"org.specs2" %% "specs2-core" % "3.6" % "test",
"org.specs2" %% "specs2-mock" % "3.6" % "test",
"org.specs2" %% "specs2-junit" % "3.6" % "test"
),
assemblyJarName in assembly := "aws-lambda-scala-%s.jar" format(Version)
)


なんか色々書いたけど、必要なのは、 aws-lambda-java-coreaws-lambda-java-events のとこっすね。


assembly

LambdaにJavaのコードを実行させるには、オールインのJarを作る必要がある。

sbtのプラグインで、それするのに便利なプラグインがあるから、それも取ってくる。


project/plugins.sbt

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.13.0")


さっきのEclipseのしたに sbt-assembly ってやつを追記した。

ちなみに、 plugins.sbt は改行を1つ多めにはさんであげないとコードの区切りを認識しないから一行あけてね。

0.13.7以降でなくなったとのこと。コメありがとうございます!!


Scala

で、やっとこコード。


src/main/scala/com/example/Hello.scala

package com.example

import scala.collection.JavaConverters._
import com.amazonaws.services.lambda.runtime.Context

trait HelloBase {

def hello(myCount: Int, context: Context): java.util.List[String] = {
println("Hello World!!")
List("%d".format(myCount)).asJava
}
}

class Hello extends HelloBase


こんな感じ。

このコードで大事なのは、


  1. helloメソッドの第一引数に、Lambda実行時のインプットを受け取るパラメータを用意する

  2. helloメソッドの第二引数に、 com.amazonaws.services.lambda.runtime.Context を受け取る

  3. 返り値の型を、 java.util.Listjava.uti.Map を指定する

ってとこで、それ以外は、好きにしていいみたい。

ちなみに、POJOでやるなら、

import scala.collection.JavaConverters._

class Hello {
def hello(input: Any, context: Any): java.util.List[String] = {
List("1","2","3").asJava
}
}

って感じでもいけるはず。


activator assembly

コードも出来たし、Jarファイルを出力します。

$ ./activator assembly

って実行すると、 target/scala-2.11/aws-lambda-scala-0.1-SNAPSHOT.jar ってjarが出来ているはず!


Lambdaにfunctionを作成

コンソールでやってもいいけど、どうせならAWS CLI使いたいよねってことで、以下のコマンドを実行して、Lambda上に新しいFunctionを作る。

$ aws lambda create-function \

--region us-east-1 \
--function-name aws-lambda-scala \
--zip-file fileb://aws-lambda-scala-0.1-SNAPSHOT.jar \
--role arn:aws:iam::(AWSアカウントID):role/lambda_basic_execution \
--handler com.example.Hello::hello \
--runtime java8 \
--timeout 15 \
--memory-size 512

これ実行すると、

{

"FunctionName": "aws-lambda-scala",
"CodeSize": 12342769,
"MemorySize": 512,
"FunctionArn": "arn:aws:lambda:(リージョン):(AWSアカウントID):function:aws-lambda-scala",
"Handler": "com.example.Hello::hello",
"Role": "arn:aws:iam::(AWSアカウントID):role/lambda_basic_execution",
"Timeout": 15,
"LastModified": "2015-09-02T10:12:17.691+0000",
"Runtime": "java8",
"Description": ""
}

って結果が返ってくる。

これで出来た。


Lambda Invoke!!

じゃ実行してみよう!

$ aws lambda invoke \

--region us-east-1 \
--function-name aws-lambda-scala \
--payload 123 \
--invocation-type RequestResponse /tmp/response

で、

{

"StatusCode": 200
}

って返ってきた!

成功したっぽいぞ。

invokeの結果を /tmp/response に吐き出したので、そっちを開いて確認してみる。

["123"]

ってなってる! payload で渡した 123 が List[String] になってJson化されて返ってきた!


まとめ

ScalaのSpecsテストが使えるから、単体テストもやりたい放題だぞ。

以上。