AWS Lambdaのソースコードを覗いて動作原理を知る試み
この記事は、Serverless Advent Calendar 2016の14日目の記事です。
昨日は工藤さん(level69)による、「Serverless Meetup Sapporoで話てきた」でした。
工藤さんは元々北海道出身という縁もあることから、先日のServerless Meetup Sapporoに自腹で遠征してくれました。本当にありがとうございます。
本題
さて、本題ですが、最近はApache (元IBM) Openwiskを筆頭に、IronFunctionsなど、OSSなFaaS実装が増えてきていますが、ふと「Lambdaはどうなってんだろーなー。あ、ていうかLambdaもPythonなら、スクリプト言語だからある程度まではソースコード読めるんでは?」と思い立ったので、じゃあやってみましょうという試みです。
まずはFunctionの呼び出し元を知る
Functionの中でスタックトレースを出力すれば、Functionの呼び出し元が分かるはずです。Lambdaのソースコードの旅はここから始まる!ということで、出力してみましょう。
以下のようなFunctionをアップロードして実行します。
import traceback def lambda_handler(event, context): traceback.print_stack()
結果を見てみましょう。
$ lamvery invoke {} START RequestId: ed0dbfd9-c134-11e6-92b1-ab90b641e77e Version: $LATEST File "/var/runtime/awslambda/bootstrap.py", line 426, in <module> main() File "/var/runtime/awslambda/bootstrap.py", line 423, in main handle_event_request(request_handler, invokeid, event_body, context_objs, invoked_function_arn) File "/var/runtime/awslambda/bootstrap.py", line 243, in handle_event_request result = request_handler(json_input, context) File "/Users/marcy/project/lambda-source/lambda_function.py", line 4, in lambda_handler END RequestId: ed0dbfd9-c134-11e6-92b1-ab90b641e77e REPORT RequestId: ed0dbfd9-c134-11e6-92b1-ab90b641e77e Duration: 0.55 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 15 MB
/var/runtime/awslambda/bootstrap.py
がLambda Function実行時の真の起点となるソースのようです。
起点のスクリプトをダウンロードしてみる
では、起点のスクリプトが分かったので取得してみましょう。Lambda:invokeFunction
のAPIで得られる標準出力の内容は一定の長さを超えると切られてしまうので、S3に入れてみることにします。
以下のようなFunctionをアップロードして実行します。
import boto3 s3 = boto3.resource('s3') def lambda_handler(event, context): s3.Object('marcy-terui', 'bootstrap.py').put( Body=open('/var/runtime/awslambda/bootstrap.py', 'rb'))
起点のスクリプトを読んでみる
さて、S3に入ったファイルを落として読んでみましょう。すると、まず冒頭でPythonの標準モジュールではないものをimportしています。特にimport前にsys.path.append
等を行っている記述はないので、同じディレクトリ内にあるはず。
同じディレクトリ内のファイルを全て取得する
以下のようなFunctionをアップロードして実行します。
import boto3 import os s3 = boto3.resource('s3') def lambda_handler(event, context): for f in os.listdir('/var/runtime/awslambda/'): s3.Object('marcy-terui', f).put( Body=open('/var/runtime/awslambda/{}'.format(f), 'rb'))
これで全て取得できました。
え?ソースコードを見せろって?
はい。おっしゃる通りで、ソースコードを見ながらあーでもないこーでもないっていうのをやりたかったんですが、ソースコードに思いっきり Copyright (c) 2013 Amazon. All rights reserved.
って書いてあるので、勝手に公開したら怒られかねないなと思いとどまりました。。。
気になる方は是非上記方法を試して見てください。一部 so
化されていますが、大部分は普通のPythonコードです。まだ全てをちゃんと読み込んでないですが、起動されたLambdaプロセスが呼び出しを待ち受ける方法が1つではないことが分かったりして、中々面白い発見があると思いますよ。何か最適化のヒントとなる情報などが読み取れたりするかも?(これくらいは言っても怒られないはず?)
簡単ですが、以上です
本当は、以下のようなのをある程度完成させてお披露目したかったんですが、Serverless Meetup SapporoやYAPC::Hokkaidoの諸々やら仕事がわりと立て込んでたりで進捗ダメです・・・年内には目処を付けたい。。。
SAMとStep Functionsを良い感じにIntegrateしてLamveryの大型アップデートしたい(というか根本から作り直したい)ので一週間くらいそれだけやる時間ください
— Masashi Terui (@marcy_terui) December 5, 2016
とりあえず、こちらからは以上です。