話題の記事

AWS Lambda の新機能でサーバーレス・シェルスクリプト! カスタムランタイムのチュートリアルを動かしてみた #reinvent

re:Invent 2018 のキーノートで発表になったカスタムランタイム、早速シェルスクリプトを Lambda 上で実行してみました。こいつはいろいろと夢が膨らむ機能です!
2018.11.30

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

待望のサーバーレスシェルスクリプト環境!?

既報ですが、AWS re:Invent 2018 のキーノート Day 2 にて AWS Lambda に新機能、Custom Runtime が発表になりました!

【アップデート】 もう言語で悩まない!AWS LambdaでCustom Runtimeが利用できるようになりました! #reinvent

思えば以前から、仕様の隙を突いてごにょごにょすることで Lambda 上で好きなバイナリを動かすことは出来たわけですが、この機能が発表されたことで、もうそんなことする必要も、それをプロダクションレベルで使って良いかどうか考える必要もないわけですね。公式サポート素晴らしいです。

技術情報としては下記のドキュメントがそろっています(まだ英語ですが)。また勿論、既存のドキュメントから得られる環境変数の値などの情報もそろっているわけで、すぐにでも使い始められますね。

ひとまずチュートリアルを動かしてみることにします!

チュートリアルドキュメントを眺めてみる

公式ドキュメントに、カスタムランタイムを動かすためのチュートリアルが用意されています。

こちらとLambda の実行環境のドキュメント、ならびに先にあげた Custom AWS Lambda Runtimes のドキュメント等を合わせ読むと、こんな感じのことが読み取れました。

  • ランタイム bootstrap と function handler function.sh 、ふたつのファイルを用意する
  • bootstrap というがまず実行される
  • ぱっとみ function.sh は別に分ける必要ないのでは??
  • OS は Amazon Linux (AL2ではない)
  • /usr/bin がある
    • というか PATHからすると/usr/bin/usr/local/bin/opt/binもある
    • sh が Amazon Linux のものと同じなら、少なくとも Bash のビルトインコマンド は使えそう?
  • grepとかcurlとかある。基本的なものは一通りそろってそう
  • 環境変数でもろもろ取得できそう
  • $LAMBDA_TASK_ROOT の下に行けば、パッケージされたファイルが呼び出せそう
  • Lambda Runtime API を叩いて何かを送受信している

とまあ、もっと深読みできるとは思うのですが、眺めるのはその辺にして、ひとまずチュートリアルを実行してみます。

やってみる

チュートリアルは AWS CLI で行うかたちになってましたが、とりあえずマネジメントコンソールで始めてみましょう。

今回は macOS Mojave で作業しています。Windows 環境の場合は WSL などの Linux 互換環境を用意するようにとチュートリアルにありましたので、そのようにしましょう(恐らく chmod する関係かと想像しています)。

関数の作成

まずは関数の作成を行います。マネジメントコンソールにログイン、 AWS Lambda のコンソールから「関数の作成」をクリックします。

さすがにシェルスクリプトの設計図はなかったので、素直に「一から作成」をクリックしました。

必要な項目を埋めます。

項目名
名前 lambda-custom-runtime-tutorial
ランタイム 独自のランタイムを使用する
ロール 1つ以上のテンプレートから新しいロールを作成します。
ロール名 lambda-custom-runtime-tutorial-role

名前(関数名)とロール名は任意です。今回は分かるようにこんな名前にしました。

ロール作成時にテンプレートから選ぶようにしたので、適当に「Amazon S3 オブジェクトの読み取り専用アクセス権限」を選択しました。もちろん チュートリアル に忠実に、AWSLambdaBasicExecutionRole を含む IAM ロールをカスタムで作成しても良いと思います。というかそっちが本当ですねw

問題なければ「関数の作成」をクリックします。

しばらく待った後、見慣れた初期画面に遷移しました。ここからチュートリアルの内容にいろいろと置き換えていきます。

ここで画面を良く見ると、カスタムランタイムを選ぶとインラインエディタ(Cloud9)が使えないみたいです。というわけで、コードが含まれた Zip ファイル(パッケージ)を用意します。

パッケージの用意とアップロード

上に書いたとおり、bootstrapfunction.sh の二つのファイルを用意します。適当なテキストエディタで、ドキュメントのとおり作成しました(若干書き方が違っているのは shfmt を通したからです、ご了承下さい)。

bootstrap

#!/bin/sh

set -euo pipefail

# Initialization - load function handler
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"

# Processing
while true; do
  HEADERS="$(mktemp)"
  # Get an event
  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)

  # Execute the handler function from the script
  RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")

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

function.sh

function handler() {
  EVENT_DATA=$1
  echo "$EVENT_DATA" 1>&2
  RESPONSE="Echoing request: '$EVENT_DATA'"

  echo $RESPONSE
}
$ ls -lA
total 16
-rw-r--r--  1 watanabe.seigo  staff  674 11 29 12:49 bootstrap
-rw-r--r--  1 watanabe.seigo  staff  127 11 29 12:49 function.sh

そしたらこれをパッケージにします。

$ chmod 755 function.sh bootstrap

$ zip lambda-custom-runtime-tutorial.zip function.sh bootstrap
  adding: function.sh (deflated 24%)
  adding: bootstrap (deflated 38%)

$ ls -lAF
total 24
-rwxr-xr-x  1 watanabe.seigo  staff  674 11 29 12:49 bootstrap*
-rwxr-xr-x  1 watanabe.seigo  staff  127 11 29 12:49 function.sh*
-rw-r--r--  1 watanabe.seigo  staff  829 11 29 12:53 lambda-custom-runtime-tutorial.zip

できあがったこの Zip ファイルを、Lambda コンソールからアップロード・保存します。コードエントリタイプを「.zip ファイルをアップロード」、関数パッケージの「アップロード」をクリックし、先ほど作成した Zip ファイルを指定します。

そして同時に、ハンドラーの指定を変更します。今は Hello World 仕様になっているので、これをfunction.handlerに変更します。

準備が出来たら「保存」をクリックして下さい。

以上で準備完了です!

実行(テスト)

そしたらさっそく実行・・・の前にテスト設定を行います。右上の「テスト」をクリックしましょう。

イベントテンプレート「Hello World」、イベント名に適当な名前を付けて実行します。

設定できたら、再度「テスト」をクリックします!

見事に実行されました!

CloudWatch Logs にもちゃんと出力されていますね。

結果を眺めると、下記のことが分かります。

  • Lambda から出力(実行によって返された結果)は、スクリプト bootstrap 内で環境変数 $RESPONSE の内容
    • 19行目の curl から API へ POST されたもの
  • 逆算すると、テストから入力された内容(JSON)は 12行目の curl が API から GET したものらしい
  • 標準出力も普通にログに出ている(curl の実行ログも!)

こいつはいろいろと夢が膨らみます!

まとめ

今回はチュートリアルスクリプトを実行しただけですが、得られた結果からいろいろそぎ落としたり正規化したりしていけば、いろいろと手軽に処理が実行できるようになりそうです。もうすこし触っていて、後日あらためて深掘りした結果をアウトプットしたいと思います!