Terraform と Rust で Lambda してみた


みなさんこんにちは、ニフティ株式会社新卒 1 年目の中井です。突然ですが皆さん、業務の新しいバッチ処理を Rust で書いてみようと思ったことはありませんか?ありますよね。私も入社して半年の 9 月に達成しました!!わーい!
実は元々ニフティ社内で Rust が使われていたわけではありません。それなのになぜ入社してこんなにすぐ Rust を導入することができたのかというと、他にも Rust を導入しようと企んでいる先輩がすでにオープン社内 Rust 勉強会を開いてくださっていたからなんです。そこで学んだ知識も活かして、今回はぜひ Rust を書きたいと主張して書かせていただきました!
以前は退勤を失敗して対策を練ったりしていましたが、今回は今風な技術を使ってみたというお話です。やらかしてばかりでもないのです。

・Rust とは


Rust という名前だけは聞いたことがある方もいらっしゃるのではないでしょうか。自分も数々のストリーマーがギャンブルに興じている動画を見たことがあります。いえ、そちらではなくて、プログラミング言語の Rust です。
公式サイトが https://www.rust-lang.org/ にあるのですが、簡単にいうと、「速い」「安全な」そして「使いやすい」言語です。言語の習得がちょ〜〜っとだけ難しいという話もありますが、エコシステムとしては本当に完成度が高いです。

  • Cargo というパッケージマネージャー兼ビルダーの存在

  • Rust Analyzer という随一のエディタ支援機能


Rust の良さは無限に語れてしまうのですが、本題ではないので、とりあえず最強の言語があるということだけ覚えてください。

・お題: 画像を S3 から S3 へ移動させる Lambda をつくれ


ニフティには AWS の練習用アカウントを用意して月 100 ドルまで使わせてくれる実弾演習場という制度があります。ということで、Rust でどうやって Lambda を書いていくのか、小さいアプリケーションを使って実際にデプロイしてみましょう。

...待ってください、たしかにこれだけだとただの暇を持て余した人の遊びみたいな感じですが、この Lambda 部分で画像を加工してみたり、文章を要約してみたり、いろいろ夢がひろがるじゃないですか!

・Cargo Lambda と Terraform を組み合わせる


作るものは決まったとして、問題はデプロイ方法です。
Lambda を zip ファイルに固めて terraform apply する、という方法なら比較的簡単です。ただ自動化が大変ですし、そもそも毎回 Terraform を実行するのは怖いです。何かのタイミングでインフラを壊しそうで…。
かといって、世の中には SAM とか Serverless Framework とかいうものもあるとは聞きますが、難しそうなので何も理解していません。
今回は Cargo Lambda を採用します。これは Cargo という Rust のパッケージマネージャーを拡張して、Lambda 用のコマンドを多数増やしてくれるツールです。
https://www.cargo-lambda.info/
これを使うと、デプロイまでの流れはこんな感じ。簡単なので私でも使えます。

  1. cargo lambda new でプロジェクトを作成

  2. cargo lambda build で Lambda 用のバイナリをビルド

  3. cargo lambda deploy で AWS 上にデプロイ


本当に Lambda 単体がただ動けばよいだけならこれだけで OK です。最低限の IAM ロールを含め、全てをデプロイしてくれます。
ただし今回は、インフラ側は Terraform で管理した上で、うまく Cargo Lambda と組み合わせることにしました。というのも、どうせ S3 が必要になるし、また S3 へのアクセス権など IAM ロール自体の調整もあるからです。すなわち….

  1. Terraform で S3 や IAM ロール、ダミーの仮の Lambda まで作成してしまう

  2. Cargo Lambda でホンモノの Lambda を上書きする


これで、Lambda を含めたインフラ全体を Terraform で管理しつつ、日常的な Lambda のデプロイには Terraform を利用しない形にできます。
...ちなみに私は AWS 初心者なので、他にもっといい方法がある気がしています。よければ教えてください。やっぱり SAM 勉強したほうが良いですか?

・実際につくってみる


何はともあれ、実際に作ってみましょう。ちなみに完成品はこちらにおいておきます。

・Terraform のメインファイルを書く


まずはお決まりのやつです。AWS を使いたいので aws プロバイダを指定します。
provider "aws" {
region = "ap-northeast-1"
shared_credentials_files = ["~/.aws/credentials"]
profile = "{your-aws-profile}"
default_tags {
tags = {
managed_by = "terraform"
}
}
}
main.tf

・S3 のバケットやイベントとの連携を用意する


次に S3 関連の設定を書いてしまいます。今回は入力・出力用に 2 つのバケットを用意します。また、Put イベントで Lambda を呼び出すように設定します。
{your-prefix-} には、他の人とぶつからなさそうな、自分だけの好きな文字列を入れてください。S3 の名前は全世界で重複しない必要があるらしいので、私が作った S3 バケットと衝突してエラーになってしまいます。
# 入力側
resource "aws_s3_bucket" "input" {
# 名前は適当なプレフィックスを付けるなど、必ず変更すること
bucket = "{your-prefix-}example-aws-terraform-rust-input"
}
# 出力側
resource "aws_s3_bucket" "output" {
# 名前は適当なプレフィックスを付けるなど、必ず変更すること
bucket = "{your-prefix-}example-aws-terraform-rust-output"
}
# 入力側の S3 の Put イベントで Lambda を呼び出す
resource "aws_s3_bucket_notification" "put_notification" {
bucket = aws_s3_bucket.input.id
lambda_function {
lambda_function_arn = aws_lambda_function.lambda.arn
events = ["s3:ObjectCreated:Put"]
}
}
resource "aws_lambda_permission" "allow_s3_invoke" {
statement_id = "AllowS3Invoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.lambda.function_name
principal = "s3.amazonaws.com"
source_arn = aws_s3_bucket.input.arn
}
s3.tf

・ダミーの Lambda を Terraform で作成する


さて、ここからがトリックの 1 つ目、ダミーの Lambda の作成です。
後で本物の Lambda をデプロイすることになるので、いろいろなオプションは本物の Lambda が動く基準に合わせて作ります。

  • runtime: provided.al2

  • handler: bootstrap


また、後で Cargo Lambda によりコード部分を上書き...

続きは NIFTY engineering

  • 記事にコメントを書いてみませんか?