AWS Lambda を計算リソースとして使う

AWS Lambda を計算リソースとして使う

Amazon EC2 は 1 時間単位,Google Compute Engine も 10 分単位でしか借りられず,試行錯誤しながらプログラムを書いている時に計算リソースを使い切ることは困難です.そこで 100 ミリ秒単位で使える AWS Lambda を計算リソースとして使ってみました.
AWS Lambda は表向きは Node.js あるいは Java が使えると書かれていますが,実際には最近流行りのコンテナ技術を使っており言語レベルでの制限はかけられておらず,exec 関数を呼び出すことにより(静的リンクされていれば)任意のバイナリを実行することができます.そこで C++ で書かれたプログラムを実行する方法について紹介します.
Linux 上で x86-64 向けにコンパイル・静的リンクされたバイナリがあれば,それを AWS Lambda 上で実行することは簡単にできます.コンパイル・静的リンクの頻度が低ければ EC2 上か手元の Linux 上で行うだけで十分ですが,この記事ではさらなる自動化のためコンパイル・静的リンクも AWS Lambda 上で行う方法を紹介しています(静的リンクした gcc を GitHub 上に置いているのでそれをソースコードとして含める方法を紹介しています).

実際の効果

2015 年 9 月 25 日現在,東京リージョンでは,100 並列で動かしたところ 288 ECU,500 並列で動かしたところ 994 ECU 相当の計算リソースが得られました(デフォルトでは 100 並列で制限されていますが,Amazon に問い合わせると上限を引き上げることができます).994 ECU は c3.large 142 台相当であり,オンデマンドならば 2180 円 / 時間,スポットならば 290 円 / 時間かかるところが,AWS Lambda での 10 秒間の実行ならば 12.5 円 / 回で実行できます.欲しいと思った時に 1 秒以内に起動するので,起動に 5 分前後必要な EC2 スポットインスタンスと比べ起動に必要な心理的ハードルが非常に低く,気軽に大量の計算リソースが利用できます.加えて月に 800 円分が無料で使えるので,実行頻度が高くなければ安価で大量の計算リソースが使えると言えます.

AWS Lambda の制限事項

AWS Lambda は書き込みが許されているのが /tmp のみで 500MB 以内という制限がかけられています.実行時間にも 60 秒以内という制限があり,途中で停止させることはできません.ライブラリ等もインストールされているものは最低限ですので,もし自前のバイナリを実行する場合は Amazon EC2 (Amazon Linux) 上で静的リンクを行いソースアーカイブに含める必要があります.またソースアーカイブは 50 MB 以内という制限があるので,それを超えるバイナリを使う場合は実行時に Amazon S3 からダウンロードする必要があります.

AWS Lambda のセットアップ

Step 1

AWS Lambda のトップページに行くと,"Get Started Now" ボタンが現れるので,それを押すと新しい関数を作る画面に移動します.ここで使いたいリージョンを画面の右上で正しく設定しておきましょう.imos-lambda は nodejs で動くので nodejs で最もシンプルなテンプレートである hello-world を選びます.
図 1: 初期テンプレートの選択

Step 2

imos-lambda は関数名を元に動作を決めているので,Name は exec を設定してください(C++ コンパイラ用の関数はここで gcc と入れます).Description は自由に設定してください.関数コードは AWS CLI を用いて後ほど設定するのでそのままにします.Role は S3 execution rule を使って新たに作ってください.(コンパイル時に生成したバイナリを Amazon S3 に置くので,C++ コンパイラを使う場合は必要です.使わない場合は Basic execution rule でも大丈夫です.)Memory の設定は必須ではありませんが,大きなサイズのメモリを設定すると CPU 速度も向上するので,ここでは最大の 1536 MB を設定しています.Timeout は 1 回のリクエストで実行できる最長の長さが設定でき,なるべく柔軟性を高くするため最大の 60 秒に設定します.
図 2: 関数の設定

Step 3

確認画面です."Create Function" ボタンを押すと関数が作られます.
図 3: 確認画面

imos-lambda のセットアップ

exec のセットアップ

主なコードは Github の imos/imos-lambda に置いてあります.imos-lambda は最新の aws コマンドと php が必要です.AWS Lambda は最近作られたサービスであり API が若干変わってきているので,aws コマンドは pip を用いてインストール・アップグレードすることをおすすめします.
# AWS の認証情報が設定されている必要があるので設定したことがなければします.
$ aws configure
# imos-lambda をチェックアウトします
$ git clone https://github.com/imos/imos-lambda
$ cd imos-lambda
# us-west-2 リージョンの exec コマンドの実行コードを上書きします
$ make install-exec REGION=us-west-2
# cli ディレクトリにパスを通します(通さず直接実行しても大丈夫です)
$ export PATH="$(pwd)/cli:${PATH}"

使い方

# us-west-2 の exec で uname -a を実行します.
$ imos-lambda --region=us-west-2 uname -a
Linux ip-10-0-116-148 3.14.48-33.39.amzn1.x86_64 #1 SMP Tue Jul 14 23:43:07 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
REPORT RequestId: ee9dcb9f-61e2-11e5-871a-758370ef4d00
Duration: 17.38 ms
Billed Duration: 100 ms
Memory Size: 1536 MB
Max Memory Used: 17 MB
Elapsed time: 17 ms
Price: 0.0003 JPY

gcc のセットアップ

AWS Lambda 上で gcc 関数を exec 関数と同様に追加します.
# ap-norteast-1 リージョンの gcc コマンドの実行コードを上書きします
$ make install-gcc REGION=ap-northeast-1

使い方

# --bucket でコンパイル後のオブジェクトを置く場所を指定することによりコンパイル・実行が可能です.
$ time ./cli/imos-lambda --alsologtostderr --compiler=gcc \
    --input=sample/hello.cc --bucket=lambda.imoz.jp
I0924 02:11:50.860107 9265 imos-lambda:42] Compiling...
I0924 02:11:51.788474 9265 imos-lambda:50] Successfully compiled: ephemeral/2e5b0e60-6216-11e5-8b25-81a8e743b35c
I0924 02:11:51.796413 9265 imos-lambda:59] Invoking function.
Hello world!
REPORT RequestId: 2ee8a8ad-6216-11e5-a46c-b34664c0aa4d
Duration: 166.26 ms
Billed Duration: 200 ms
Memory Size: 1536 MB
Max Memory Used: 52 MB
Elapsed time: 165 ms
Price: 0.0006 JPY
I0924 02:11:52.260542 9265 imos-lambda:79] Completed.

real  0m1.499s
user  0m0.335s
sys 0m0.093s
# --replicas フラグを利用することにより並列実行が可能です.
# trip.cc はトリップを探索するプログラムです.
$ time ./cli/imos-lambda --alsologtostderr --region=us-west-2 \
    --compiler=gcc --input=sample/trip.cc \
    --bucket=lambda.imoz.jp --compiler_flags='-lcrypt -O2' \
    --arguments=imos --replicas=90 2>/dev/null
olunfpnc imosUVXN6M
twnzekyc imosOkBO9w
yvmxkhci imosJ7WMaY
waetzigk imosnC.OMI
zwgilupk imosKI4urQ

real  0m37.351s
user  0m18.814s
sys 0m3.613s

付録

gcc-min.tar.xz の生成方法

/tmp/gcc をインストール先に指定して gcc 4.8 をコンパイルして生成しました.ただしこの状態では xz 圧縮しても 50 MB を超えるので,容量をとっており必須ではない /tmp/gcc/libexec/gcc/*/(lto1|cc1) を削除しています.また glibc 等必要なライブラリ・ヘッダーがあるため,rpm -ql コマンドを使い kernel-headers glibc-headers glibc-static のパッケージに含まれるファイルをコピーしています.libc.so が絶対パスを指定しているので,テキストエディタで編集し /tmp/gcc 内のファイルを参照するように変更しています.また,Cent OS の libcrypt.a は壊れているため Ubuntu からコピーして使っています.

AWS Lambda のベンチマーク

UnixBench の Dhry を利用し整数の演算速度を東京リージョンで測定しました.AWS Lambda の限界を使いきっているわけではなさそうですが,並列度が上がると CPU の使用率が下がるようです.
  • c4.large … 0.426 Mlps (3.5 ECU)
  • AWS Lambda (単一) … 0.351 Mlps (2.88 ECU), 合計 0.351 Mlps (2.88 ECU)
  • AWS Lambda (100並列時) … 0.264 Mlps (2.17 ECU), 合計 26.4 Mlps (288 ECU)
  • AWS Lambda (500並列時) … 0.242 Mlps (1.99 ECU), 合計 121 Mlps (994 ECU)

参考文献