超爆速 LambdaでDenoを動かしてみる


私はLambdaが大好きだ。
もう全てLambdaで動かせばいいんじゃないかな?(少なくともAPIはAPIGateway + Lambdaで十分だと思ってる。)

それはさておき

そんなLambdaでDenoを爆速で動かす方法をまとめてみた。

前提条件

  • aws cliが使用できる
  • 実行環境はmac

1.Layerの登録

deno-lambda-layer.zipをダウンロード

$ curl -OL https://github.com/hayd/deno-lambda/releases/download/1.11.0/deno-lambda-layer.zip

公式サイトより最新のアーカイブファイルをダウンロードすることを推奨

deno-lambda-layerを登録

$ aws lambda publish-layer-version \
  --layer-name deno-lambda-layer \
  --zip-file fileb://deno-lambda-layer.zip
実行結果.json
{
    "Content": {
        "Location": "xxx",
        "CodeSha256": "xxx",
        "CodeSize": 31586895
    },
    "LayerArn": "arn:aws:lambda:ap-northeast-1:xxx:layer:deno-lambda-layer",
    "LayerVersionArn": "arn:aws:lambda:ap-northeast-1:xxx:layer:deno-lambda-layer:1",
    "Description": "",
    "CreatedDate": "9999-99-99T99:99:99.999+0000",
    "Version": 1
}

LayerVersionArnを使用するので控えること。

2.実行Roleの作成

role作成済みの場合はSKIP

定義ファイル作成

$ vim role.json
role.json
{
    "Version": "2012-10-17",
    "Statement":[
        {
            "Effect": "Allow",
            "Principal":{
                "Service":"lambda.amazonaws.com"
            },
            "Action":"sts:AssumeRole"
        }
    ]
}

role作成

$ aws iam create-role \
  --role-name deno-function-exec-role \
  --assume-role-policy-document file://role.json
実行結果.json
{
    "Role": {
        "Path": "/",
        "RoleName": "deno-function-exec-role",
        "RoleId": "xxxx",
        "Arn": "arn:aws:iam::xxxx:role/deno-function-exec-role",
        "CreateDate": "9999-99-99T99:99:99.999+0000",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        }
    }
}

Arnを使用するので控えること。

3.LambdaDeploy

Create index.ts

$ vim index.ts
index.ts
import { Context, Event } from "https://deno.land/x/lambda/mod.ts";

export function handler(event: Event, context: Context) {
  const message:String = `Hey deno ${Deno.version.deno}.`
  console.log(message)
  return {
    statusCode: 200,
    body: message
  }
}

Deploy

$ zip index.zip index.ts
$ aws lambda create-function \
  --function-name DenoFunction \
  --runtime provided.al2 \
  --handler index.handler \
  --zip-file fileb://index.zip \
  --role arn:aws:iam::xxxx:role/deno-function-exec-role \
  --region ap-northeast-1

--roleに2.実行ロールで作成で控えたArnを定義して実行

実行結果.
割愛。
エラーが発生していなければOK。

Layerの登録

$ aws lambda update-function-configuration \
  --function-name DenoFunction \
  --layers \
    "arn:aws:lambda:ap-northeast-1:xxx:layer:deno-lambda-layer:1"

--layersに1.Layerの登録で控えたLayerVersionArn定義して実行

実行結果.
割愛。
エラーが発生していなければOK。

動作確認

$ aws lambda invoke  \
  --function-name DenoFunction \
  --log-type Tail \
  outputfile.txt \
  --query 'LogResult' | tr -d '"' | base64 -D
実行結果.
START RequestId: xxxx Version: $LATEST
Hey deno 1.11.0.d: xxxx
END RequestId: xxxx
REPORT RequestId: xxxx  Duration: 76.08 ms  Billed Duration: 1804 ms    Memory Size: 128 MB Max Memory Used: 76 MB  Init Duration: 1727.22 ms

ちゃんと動いた、
これで完了。

番外編

やっぱりDenoらしいコードでも試してみたいよね。

メインロジックを分離する想定で、

index.ts
import { Context, Event } from "https://deno.land/x/lambda/mod.ts";
import { execute } from "./usecase.ts";

export function handler(event: Event, context: Context) {
  const message:String = execute()
  console.log(message)
  return {
    statusCode: 200,
    body: message
  }
}
usecase.ts
export function execute(): String {
  return 'My son is an angel'
}

ほいで更新

$ zip index.zip *.ts
$ aws lambda update-function-code \
  --function-name DenoFunction \
  --zip-file fileb://index.zip

最後に実行

aws lambda invoke  \
  --function-name DenoFunction \
  --log-type Tail \
  outputfile.txt \
  --query 'LogResult' | tr -d '"' | base64 -D
実行結果.
START RequestId: xxxx Version: $LATEST
My son is an angel xxxx
END RequestId: xxxx
REPORT RequestId: xxxx  Duration: 74.14 ms  Billed Duration: 1360 ms    Memory Size: 128 MB Max Memory Used: 76 MB  Init Duration: 1285.37 ms

できた。

まとめ

・TypeScriptの記法で実装できたり、node_modules管理が無くなったり、awaitの使い勝手が良くなったりと利便性が高いので真面目に導入するのもアリかと。
次はもうちょっと実務に耐えれる設計や、CI/CDの導入など考慮してみたい。

・Denoのアイコンってチンアナゴ?