みんな大好きLambda
今年の re:Invent では RDS Proxy という個人的神アップデートも発表されて、
ますますサーバーレスしやすくなってきましたね!
(Provisioned Concurrency もな!)
そんな Lambda ですが、現在サポートされているランタイムは以下です。
- Node.js
- Python
- Ruby
- Java
- Go
- .NET
もちろんこれ以外の言語でも使いたい人はたくさんいますよね。
そんなときはカスタムランタイムです。
カスタム AWS Lambda ランタイム
去年(2018)の re:Invent で発表された機能ですね。
New for AWS Lambda – Use Any Programming Language and Share Common Components
bootstrap
という実行可能ファイルにパッケージできれば、
どんな言語でも Lambda 上で動かすことができます。
見た感じそんなに難しくなさそうだったので自分でも何か作ってみることにしました。
カスタムランタイムの例
- カスタムランタイムを使ってLambdaでAWSCLIを動かす
- AWS Lambda 新機能 Custom Runtime を作ってみた
- AWS LambdaのCustom RuntimeでサーバーレスDartしてみた
- AWS LambdaのCustom RuntimeでRustを実行してみた #reinvent
有名どころの言語はあらかた先人たちが試していて、
車輪の再発明するのもあれだな...と思っていたところ、
ふと、
「あれ、アセンブリない......」
ということに気づきました。
かわいそう。
作ってあげないと。
アセンブる
まずソースコードから書きます。
シンプルに Hello world を。
section .data
message: db "早めのパブロン", 10
length: equ $ - message
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, length
syscall
mov rax, 60
xor rdi, rdi
syscall
同じものを Ruby で書くとこうです。
puts "早めのパブロン"
🤷♂️
ざっくり解説すると、
まず1バイトの容量を確保して変数を初期化。
10
は ASCII コードの改行を表す。
message: db "早めのパブロン", 10
出力するサイズも明示する必要がある。
文字列長を計算しているのがここ。
$がその行の先頭アドレスを表しているので、
そこから文字列の先頭アドレスを引けば自動的に文字列長が求められるのだとか。
length: equ $ - message
rax
レジスタに格納している 1
は write
システムコールの番号を表している。
システムコールの一覧は ausyscall --dump
で見れる。
コマンドが入ってなかったら audit
とか auditd
とかインストールする。
mov rax, 1
rdi
レジスタの 1
は出力先で stdout
を表す。
mov rdi, 1
文字列と文字列長を指定して syscall
で呼び出す。
mov rsi, message
mov rdx, length
これは exit 0
と等価。
mov rax, 60
xor rdi, rdi
syscall
一つだけ気になったこととして、 32ビットのレジスタである eax
にすると動きませんでした。
正確にいうと、 amazonlinux:2018.03 ベースの Docker コンテナ上では動くのに、
Lambda に載せるとなぜか SIGSEGV を投げてくる。
これは...
ソースコードができたのでこれをコンパイルして実行可能ファイルにします。
最終 Lambda に載せるので Amazon Linux ベースの Dockerを実行環境として使います。
FROM amazonlinux:2018.03
RUN yum update -y && yum install -y wget tar gzip gcc make perl
RUN cd /tmp \
&& wget https://www.nasm.us/pub/nasm/releasebuilds/2.14.02/nasm-2.14.02.tar.gz \
&& tar xvfz nasm-2.14.02.tar.gz \
&& cd nasm-2.14.02 \
&& ./configure --prefix=/usr/local/nasm/2_14_02 \
&& make \
&& make install \
&& ln -s /usr/local/nasm/2_14_02/bin/nasm /usr/local/bin/
ENV APP_HOME /usr/src/assemmbly_on_aws_lambda
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
ADD . $APP_HOME
RUN nasm -f elf64 $APP_HOME/src/app.asm -o $APP_HOME/src/app.o
RUN ld $APP_HOME/src/app.o -o $APP_HOME/bin/app
アセンブラには NASM を使います。
yum でインストールできる。
nasm
でオブジェクトファイルを作り、 ld
でリンクして実行可能形式に変換します。
これで実行。
./bin/app
早めのパブロン
簡単ですね!!
Bootstrap
Lambdaでカスタムランタイムを動かすには、 bootstrap
というファイルを作成する必要があります。
難しいことは考えずにチュートリアルのをコピペしてきてちょろっと修正します。
#!/bin/sh
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)
EXEC="$LAMBDA_TASK_ROOT/$(echo "$_HANDLER" | cut -d . -f 1)"
RESPONSE=$(echo "$EVENT_DATA" | $EXEC)
curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"
done
EVENT_DATA
が 引数に渡される event
を取得してきている部分です。
カスタムランタイム用に、APIのエンドポイントが用意されています。
取得したイベントデータは、実行するプログラムに渡すのと、
最後に結果を返すエンドポイントのパスにリクエストIDを含める必要があるのでそれに使います。
$LAMBDA_TASK_ROOT
は関数にアップロードしたファイルが置かれる場所です。
見たら /var/task
でした。
$_HANDLER
はハンドラのファイル名+関数名です。
デフォルトで lambda_function.lambda_handler になってるやつですね。
今回は実行可能ファイルを直接呼び出すため、 bin/app
と設定しています。
他にもエラーハンドリングを実装しないといけないんですが、最低限これで動くので省略します。
最後に、ファイルに実行権限をつけます。
Lambda がこいつを呼び出せるように chmod 755 bootstrap
します。
関数の作成
普段は CloudFormation 原理主義者ですが、
zipアップロードが面倒なのでマネジメントコンソールから作ります。
ランタイムには、「ユーザー独自のブートストラップを提供する」を選択します。
英語だと provided
って言うらしいですね。

先ほどの bootstrap
と実行可能ファイルを zip でポンして、
ハンドラに bin/app
を指定します。

テストして動作確認。

終わり
ということで、 Lambda でアセンブリ動くよ。
サーバーレスアセンブリ
Blue-Pix/nasm_on_aws_lambda