AWS LambdaをHaskellで実装!aws-lambda-haskell-runtimeを試してみた。

2018.12.22

こちらはAWS Lambda Custom Runtimes芸人 Advent Calendar 2018の22日目の記事です。

こんにちは、かたいなかです。

AWS LambdaのCustom RuntimeでHaskellを動かすためのCustom RuntimeのLayerの実装、およびそのRuntime上でアプリケーションコードを実装するためのTemplate等がtheam/aws-lambda-haskell-runtimeというGitHub上のリポジトリにあり、ビルドされたものがRuntime Layerとして利用できるようになっているのを発見したため、試してみた内容を記事にまとめました。

ローカルでの準備

まず、以下のようなコードを実行し、ローカルに今回使用するコードの展開を行います。

$ stack new <repository名> https://github.com/theam/aws-lambda-haskell-runtime/raw/master/stack-template.hsfiles \
    --resolver=lts-12.13 --omit-packages #テンプレートを展開
$ cd <repository名> #作成されたディレクトリに入る

次に、ビルド環境として使用するstack-buildのDockerイメージをPullしておきます。

$ stack docker pull #ビルド環境として使用するDockerイメージをPull

最後に、stack.ymlに上記のような内容を追加します。

packages:
- .

extra-deps:
- aws-lambda-haskell-runtime-1.0.4

ここまでの準備で、Lambdaにアップロードできるbuild/function.zipmakeコマンドにより作成できるようになっています。

テンプレートから作成されたリポジトリの内容

ここまでの処理で以下のようなファイルが作成されます

- .stack-work
  - (省略)
- app
  - Main.hs
- src
  - Lib.hs
- .gitignore
- LISCENCE
- Makefile
- <repository名>.cabal
- package.yaml
- README.md
- Setup.hs
- stack.yaml

src/Lib.hsではハンドラを定義しています。

module Lib where

import GHC.Generics
import Aws.Lambda.Runtime
import Data.Aeson

data Person = Person
  { personName :: String
  , personAge :: Int
  } deriving (Generic)
instance FromJSON Person
instance ToJSON Person

handler :: Person -> Context -> IO (Either String Person)
handler person context =
  if personAge person > 0 then
    return (Right person)
  else
    return (Left "A person's age must be positive")

他の言語のHandler同様にEventとContextを受け取る形で実装されています。

処理の内容としては以下のようなJsonを受け取り、personAgeが正であることを確認したうえで受け取ったJsonと同じものを返すというものです。

{
  "personName": "katainaka"
  "personAge": 3
}

app/Main.hsは以下のようなコードで、Template Haskellを使用して動的にhandlerにディスパッチする処理を生成しているようです。

module Main where

import Aws.Lambda.Configuration
import Aws.Lambda.Runtime

import qualified Lib

configureLambda

動作確認

実際にデプロイしていきます。

リポジトリのルートディレクトリでmakeコマンドを実行します。

$ make

すると、buildディレクトリの下にfunction.zipというファイルが作成されます。

以下のようなコマンドですでに公開されているRuntimeのLayerと合わせてFunctionを作成します。

$ aws lambda create-function --function-name "haskell-lambda" \
    --zip-file "fileb://build/function.zip" \
    --handler "src/Lib.handler" \
    --runtime provided \
    --layers arn:aws:lambda:<YOUR REGION>:785355572843:layer:haskell-runtime:<VERSION> ##公開されているRuntimeのLayerのARNです
    --role arn:aws:iam::XXXXXXXXXXXX:role/your-lambda-role

Functionを作成したら、コンソール上から試していきます。

以下のようなテストイベントを作成して実行すると

以下のように実行に成功し、正しく結果が返されました!

感想

re:Inventでの発表からそれほど日がたっていないにもかかわらず、すでにHaskellのRuntimeの実装例がRuntime LayerのARNを指定すれば誰でも利用可能な状態になっており、関連したパッケージがCabalに登録されているのには驚きました。

今回はテンプレートから生成されたコードを実行してみただけでしたが、Haskellがお好きな方は是非本格的に試してみてはいかがでしょうか。

参考リンク