AWS LambdaのCustom RuntimeでRustを実行してみた #reinvent
re:Invent 2018のKeynote Day2、ついにLambdaでCustom Runtimeがサポートされることが発表されました!自分でカスタマイズしたRuntimeでLambdaが利用できるようになります。やったぜ!!
【アップデート】 もう言語で悩まない!AWS LambdaでCustom Runtimeが利用できるようになりました! #reinvent
ということで早速、LambdaでRustファンクションを実行してみました!!
やってみた
開発環境
- macOS: 10.12.6
- rustup: 1.15.0 (f0a3d9a84 2018-11-27)
- Rust: 1.30.1 (1433507eb 2018-11-07)
Rustの開発環境セットアップ
以下ブログを参考にRustの開発環境をセットアップします。
ローカルでRustファンクション作成
公式ブログを参考にRustファンクションを作成していきます。
プロジェクトを作成
Lambdaファンクション用のプロジェクトを作成します。
$ cd MY_WORKSPACE $ cargo new my_lambda_function --bin $ cd my_lambda_function $ tree . ├── Cargo.toml └── src └── main.rs 1 directory, 2 files
Cargo.tomlを編集
Lambdaで実行するためにCargo.tomlを編集します。
[package] name = "my_lambda_function" version = "0.1.0" authors = ["jogan.naoki <jogan.naoki@classmethod.jp>"] autobins = false [dependencies] lambda_runtime = "^0.1" serde = "^1" serde_json = "^1" serde_derive = "^1" log = "^0.4" simple_logger = "^1" [[bin]] name = "bootstrap" path = "src/main.rs"
dependencies
にはLambda実行に関する依存情報を記述します。
- serde: Lambda関数のイベントとレスポンスをシリアル化およびデシリアライズする
- log、simple_logger: ログを出力する
カスタムランタイムを使用する場合、Lambdaは展開パッケージにbootstrapという実行可能ファイルが含まれることを期待しています。
その対応のためpackage
にはautobins = false
をbin
にはname = "bootstrap"
をそれぞれ追加します。
コンパイル環境構築
コンパイル環境を構築します。
$ rustup target add x86_64-unknown-linux-musl $ brew install filosottile/musl-cross/musl-cross $ mkdir .cargo $ echo '[target.x86_64-unknown-linux-musl] linker = "x86_64-linux-musl-gcc"' > .cargo/config $ ln -s /usr/local/bin/x86_64-linux-musl-gcc /usr/local/bin/musl-gcc
メイン関数を修正
メイン関数を以下のように修正します。
$ cat src/main.rs #[macro_use] extern crate lambda_runtime as lambda; #[macro_use] extern crate serde_derive; #[macro_use] extern crate log; extern crate simple_logger; use lambda::error::HandlerError; use std::error::Error; #[derive(Deserialize, Clone)] struct CustomEvent { #[serde(rename = "firstName")] first_name: String, } #[derive(Serialize, Clone)] struct CustomOutput { message: String, } fn main() -> Result<(), Box<dyn Error>> { simple_logger::init_with_level(log::Level::Info)?; lambda!(my_handler); Ok(()) } fn my_handler(e: CustomEvent, c: lambda::Context) -> Result<CustomOutput, HandlerError> { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); return Err(c.new_error("Empty first name")); } Ok(CustomOutput { message: format!("Hello, {}!", e.first_name), }) }
ファンクションをビルド
以下のコマンドでLambda用のアプリケーションをビルドします。
$ cargo build --release --target x86_64-unknown-linux-musl
Lambdaにアップロードするため、ファイルをzip化します。
$ zip -j rust.zip ./target/x86_64-unknown-linux-musl/release/bootstrap
Lambdaファンクション作成
Lambdaファンクションを作成します。
ランタイムには独自のランタイムを指定します。ロールはLambda_basic_execution
で問題ありません。
先ほど作成したzipファイルをアップロードし、ファンクションを保存します。
テスト実行
テストイベントを作成します。
{ "firstName": "Rustacean" }
これで準備完了です。最後にテストを実行してみましょう!!
LambdaでRustが実行できました!!
まとめ
LambdaでCustom Runtimeがサポートされたことにより、Lambdaの可能性がまた大きく広がりました!!ランタイムが対応していなくてLambda上での実行を諦めていた、あんなスクリプトやこんなスクリプト、これを機にLambda対応してみてはいかがでしょうか。
ランタイムとして現在利用可能なものは以下になります。
サードパーティにより順次以下のランタイムが追加される予定です。
- Erlang (Alert Logic)
- Elixir (Alert Logic)
- Cobol (Blu Age)
- N|Solid (NodeSource)
- PHP (Stackery)