LoginSignup
3
3

More than 3 years have passed since last update.

サーバーレスアセンブリ🌴

Last updated at Posted at 2019-12-22

みんな大好きLambda

AWS-Lambda_light-bg@4x.png

今年の 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 上で動かすことができます。

見た感じそんなに難しくなさそうだったので自分でも何か作ってみることにしました。

カスタムランタイムの例

有名どころの言語はあらかた先人たちが試していて、
車輪の再発明するのもあれだな...と思っていたところ、

ふと、

「あれ、アセンブリない......」

ということに気づきました。

かわいそう。
作ってあげないと。

アセンブる

まずソースコードから書きます。
シンプルに Hello world を。

app.asm
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 で書くとこうです。

app.rb
puts "早めのパブロン"

🤷‍♂️

ざっくり解説すると、

まず1バイトの容量を確保して変数を初期化。
10 は ASCII コードの改行を表す。

message: db "早めのパブロン", 10

出力するサイズも明示する必要がある。
文字列長を計算しているのがここ。
$がその行の先頭アドレスを表しているので、
そこから文字列の先頭アドレスを引けば自動的に文字列長が求められるのだとか。

length: equ $ - message

rax レジスタに格納している 1write システムコールの番号を表している。
システムコールの一覧は 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 を投げてくる。
これは... :thinking:

ソースコードができたのでこれをコンパイルして実行可能ファイルにします。
最終 Lambda に載せるので Amazon Linux ベースの Dockerを実行環境として使います。

Dockerfile
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 というファイルを作成する必要があります。
難しいことは考えずにチュートリアルのをコピペしてきてちょろっと修正します。

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 って言うらしいですね。

 2019-12-21 23.52.57.png

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

 2019-12-21 23.56.32.png

テストして動作確認。

 2019-12-21 23.56.56.png

終わり

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

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3