Blog

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 プロバイダを指定します。

main.tf

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

次に S3 関連の設定を書いてしまいます。今回は入力・出力用に 2 つのバケットを用意します。また、Put イベントで Lambda を呼び出すように設定します。
{your-prefix-} には、他の人とぶつからなさそうな、自分だけの好きな文字列を入れてください。S3 の名前は全世界で重複しない必要があるらしいので、私が作った S3 バケットと衝突してエラーになってしまいます。

s3.tf

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

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

  • runtime: provided.al2
  • handler: bootstrap

また、後で Cargo Lambda によりコード部分を上書きするわけですが、それを後でまたダミーに書き戻されてしまっては困ります。これを避けるため、source_code_hashignore_changes に指定して、その差分を無視してもらうようにします。

lambda.tf
そして、Terraform ではダミーの Lambda をデプロイします。
環境に provided.al2bootstrap を指定しているので、その環境で動くようにダミーの方から合わせてあげる必要があります。要するにダミーには Python とかは使えないわけですが、まあダミーなので中身はなんでも大丈夫です。シェルスクリプトで bootstrap というファイルを作って、簡単にエラー終了するようにしておきました。

インフラをデプロイする

terraform apply をして、AWS 環境を確認すると…

S3 バケットあります。

ダミーの Lambda もあります。いい感じです!
この時点でイベントのセットアップなども完了しているので、バケットにファイルを入れると CloudWatch Logs にエラーが流れるはずです。

うまく動いていそうですね!

cargo lambda new で Lambda を作成

次はお待ちかね、Rust での Lambda の作成です。前述の通り、Cargo Lambda というツールを使います。
このツール、インストール方法がしっかり整備してあって、簡単に使い始めることができます。
※ 情報が古くなっている可能性もあるので、公式サイトも合わせてご確認ください。
macOS / Linux をお使いの方であれば、homebrew から簡単に入ります。

Windows の方は scoop から入れられるらしいですね。

PC 環境を汚したくない方に向けては Docker イメージも提供されていますので、公式サイトを確認してみてください。

インストールが終われば、次は早速プロジェクトを作成していきましょう。
今回は actual_lambda という名前でプロジェクトを作っていきます。おもむろに cargo lambda new actual_lambda としてみてください。すると…

画面上のいろいろと質問に答えるだけでプロジェクトが完成、最低限のボイラープレートも全部書いてくれています。なんと便利な。
ここから先は、もう普通の Rust プログラミングです。

Rust プロジェクトに AWS SDK を追加する

今回は、Lambda 内から S3 にアクセスしたいんでした。プロジェクトに AWS SDK を追加します。ちなみに Rust の AWS SDK はサービスごとに分離されています。 使いたいサービスを探し、必要なライブラリを追加してください。

今回は S3 なので、ターミナルにこんな感じで打ち込めば完了です。

何気なく aws-config も追加していますが、これは設定の読み込みのためのライブラリで、どのサービスを使うにしても必要なものっぽいです。

本体を Rust で書く

こんどこそ処理を編集して、実際に S3 から S3 へ画像が移動するようにしてみましょう。Rust は src/main.rs ファイルからスタートなので、このファイルを書き換えていくことになります。
S3 から S3 へ移動させるサンプルコードを下に掲載します。
あっ、コピペしたくなりますよね。わかります。全然コピペでもいいんですけど…。
ただ、もう少しだけ時間があるなら、せっかくなので手で写経してみませんか。Rust の最強支援機能、Rust Analyzer をぜひ使ってみてください。補完から型ヒントまで、とても気持ち良く書けるんです。

VSCode をお使いの方なら、Rust Analyzer は拡張機能から簡単にインストールできます。

actual_lambda/src/main.rs
入力側 S3 からオブジェクトを取得して、出力側 S3 に書き込んで、入力側 S3 のオブジェクトを削除する、というコードになっています。

Cargo Lambda でビルドする

出来上がったら、次はビルドをしましょう。

--target では、Lambda のタイプを指定します。

  • x86_64-unknown-linux-gnu : いわゆる普通の Lambda です。
  • aarch64-unknown-linux-gnu : ちょっと安い ARM (Graviton) タイプの Lambda です。

こんなスイッチ一つ切り替えるだけでいい感じにネイティブバイナリが作れる、これも Rust のいいところだと思っています。

Cargo Lambda でデプロイする

ビルドがうまくいけば、デプロイです。

最後の引数 example-aws-terraform-rust は Lambda の名前です。忘れないでくださいね!これを忘れてしまうと全く新しく actual_lambda という名前の Lambda が生成されてしまいます。そうすると IAM ロールなども自動で作成されてしまってゴミが増えるのでご注意を… (n 敗)。
ちなみにサンプルリポジトリの方には Docker を使うバージョンのコマンドを Makefile に書いてあるので、参考になるかもしれません。

さて、デプロイが完了したらもう動くようになっているはずです!

お楽しみの動作確認

入力用の S3 バケットに適当なファイルをアップロードすると….

出力用の S3 バケットに出力されました!わーい。

まとめ

今回は AWS 上に Terraform でインフラを作成し、その中の Lambda を Rust で実装していきました。
Terraform ではダミーの Lambda を作成し、Cargo Lambda を使ってデプロイすることで、インフラとコードのデプロイを分離することに成功しました。また、Rust 側のほとんどの作業は Cargo や Cargo Lambda が面倒を見てくれることも紹介しました。
どうでしょう。Rust で Lambda と聞くと難しそうな印象もあったかもしれませんが、思ったより簡単そうだなと思ってもらえたら幸いです。
ぜひ一度 Rust で書いてみてください。そして Rust を広めていきましょう!!

ニフティでは、
さまざまなプロダクトへ挑戦する
エンジニアを絶賛募集中です!
ご興味のある方は以下の採用サイトより
お気軽にご連絡ください!

ニフティに興味をお持ちの方は
キャリア登録をぜひお願いいたします!

connpassでニフティグループに
参加いただくと
イベントの
お知らせが届きます!