Lambda のカスタムランタイムでWhitespaceを使ってみた

2019.06.07

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

突然ですが、Hello Lambda を出力するWhitespaceのコードを書いてみました。
LambdaでWhitespaceを実装したいけど、使用できる言語一覧にないですよね。
なのでLambdaのカスタムランタイムを使用して実装をしたいと思います。

言語処理系の準備

Whitespaceのコードを実行するための言語処理系が必要になります。
ホームページが既にアクティブでなかったのでgo言語で書かれた言語処理系を使用することにしました。
今回はこちらのリポジトリを使用します。

Lambdaで動くバイナリが必要なので下記コマンドでビルドしていきます。

$ git clone https://github.com/mattn/ws 
$ cd ws
$ env GOOS=linux go build

ここまで準備できたら一旦カスタムランタイムとは何かをおさらいしましょう。

Custom Runtime とは

平たくいうとLambdaでサポートされていない言語であってもLambdaで実行可能にする機能です。 bootstrapという実行可能なファイルをデプロイパッケージに配置すると、Lambdaがそれを実行してくれます。 なのでbootstrapがエントリポイントとなるのでここでイベントを処理させます。 では実際にbootstrapを作成して、Lambdaにデプロイしていきましょう。

実装

カスタムランタイムが何かなんとなくわかり、言語処理系を入手したところでLambda関数を実装しましょう。 コードは全てこちらのリポジトリにあります。

まずは、bootstrapを作成します。 公式のチュートリアルを元に作成しました。 bootstrapで使用される環境変数については下記の通りです。

名前 説明
HANDLER 指定したハンドラ名
LAMBDA_TASK_ROOT コードが置かれる場所 この直下にbootstrapがある必要がある
AWS_LAMBDA_RUNTIME_API ここを通じてbootstrapがリクエストを受け取ったりレスポンスを返す

bootstrapのコードはこのようになっています。

bootstrap

#!/bin/bash
set -euo pipefail

while true
do
  HEADERS="$(mktemp)"
  EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
  REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
  export PATH=$PATH:$LAMBDA_TASK_ROOT/bin
  RESPONSE=$(ws handler.ws)

  # Send the response
  curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"
done

ハンドラとして使用するWhitespaceのコードは冒頭に掲載したものをそのまま使用します。 bootstrap、実行するコードが準備できたところでLambdaにデプロイしましょう。

デプロイ

ソースコードをzipファイルに固めて、Lambdaにデプロイしていきます。 また、Lambda実行時にCloud Watch Logsに書き込む権限がためのIAM Roleが必要になるので同時に準備します。 ソースコードに関して、bootstrapなど実行権限が必要なものに関しては確実にパーミッションを変更しましょう。

# ソースコードをzipで固める
$ chmod -R 755 src/
$ cd src && zip -r src.zip ./ && mv src.zip ../ && cd ..

# IAM Roleの作成
$ aws iam create-role --role-name whitespace-exec-role \
    --assume-role-policy-document file://whitespace-exec-rolepolicy.json

# ポリシーのアタッチ
$ aws iam attach-role-policy --role-name whitespace-exec-role \
    --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"

# Lambda関数の作成(roleのARNは置き換えてください)
$ aws lambda create-function \
    --function-name WhitespaceFunction \
    --runtime provided \
    --role arn:aws:iam::xxxxxxxxxxx:role/whitespace-exec-role \
    --handler handler.ws \
    --zip-file fileb://src.zip \
    --region ap-northeast-1

IAM Role作成時に使用しているポリシーファイルの中身はこんな感じです。

whitespace-exec-rolepolicy.json

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

ここまででLambda関数ができたので次は実際に実行して出力を確認してみましょう。

$ aws lambda invoke \
    --function-name WhitespaceFunction \
    --payload  '{}' output.txt
$ cat output.txt
> Hello Lambda

無事、関数が実行され予測通りの出力が返ってきました。

さいごに

bootstrap自体に実行権限を付与することを忘れてしまいつまづきましたが無事実行できてよかったです。 カスタムランタイム自体は今後もどんどん使われていく機能なのでWhitespaceに限らずにどう使うかがわかっていただけれれば幸いです。