原氏の自己紹介

原旅人氏:じゃあ始めます。私、株式会社ログラスでクラウドエンジニアというタイトルでやっている、原と申します。このたびは、このようなところに呼んでいただきありがとうございます。

今日は、「RustをAWSで活用しよう!」と。「AWS」って、実は「Lambda」の話なんですが、こういったことで話をしようと思います。

自己紹介は先ほどしていただいたので、ほぼ省略です。(スライドを示して)実は松本さん(松本健太郎氏)と私はここに書いてある検索エンジンの会社で一緒に働いていて、私がRustをやるきっかけを作ってくれたのも、実は司会者の松本さんです。

株式会社ログラスについて

今はログラスという会社にいて、クラウドエンジニアという名前で、クラウドインフラ、セキュリティ、開発・ビジネス支援、データ基盤、その他のそういうことをやっています。最近はAWS IaCとか「Terraform」とか、そういうところをけっこういろいろ凝ってやっています。

ログラスという会社ですが、経営管理クラウドと呼ばれるものを作っていて、企業さまの経営企画の方が各部署の予算と実績を比較したり、そういうことをもっと簡単にできるようにというところで開発をしています。

(スライドを示して)技術スタックとしてはこんな感じで、Kotlin、Spring Boot、TypeScript、Reactであったり、クラウドは全面的にAWSを使っているんですが、今日話したいのはLambdaの話です。

AWS Lambda関数とは

今日の話は、RustでAWS Lambda関数を書こうという話です。

Lambda関数がどういうものかという話で、知っている方がいたり、なじみがあまりない方がいるかもしれませんが、AWSのサービスの1つで、サーバーレスでプログラムを実行できるというものです。

通常、プログラムをなにか実行したい時には、サーバーなり、なんらかのPCなりで実行できるものを用意して、その上で実行するようなことをやるのが一般的です。しかしこのLambdaというサービスは、サーバーレス、いわゆる自分でサーバーを用意せず、プログラムだけ用意して「これを実行してね」とやると実行されて、できる。しかも課金は使っただけということで、非常に経済的にできます。

「EC2」インスタンスを立てて、いわゆるサーバーを立ててとなると、その稼働時間で課金がかかるので、けっこう無駄な時間も多いみたいな話になるんですが、Lambdaは使った限りです。

一方、大きなプログラムをずっと流しておくという性質のものではなくて、実行時間の上限は15分、メモリの上限は10GBになっています。

AWSにはいろいろなマネージドサービスがあるんですが、マネージドサービスと、あと少しの自分たちの環境にカスタマイズした機能をうまく組み合わせて、いろいろ使うことができます。私は今RustでLambda(関数)をたぶん50個とか60個とかたぶん動かしていると思います。

(スライドを示して)用途のほんの一例として書きましたが、例えば、stg(ステージング環境)はケチって夜は止めているんですが、そういうものをちゃんと自動で起動したり、停止したり。

あと、監視系で、外形監視に関する定期的なリクエストをLambdaから送ったり。または、サブスクリプションフィルターといって、ログのフィルターをして、ある文言とか構文があったら、それをパースしてメトリクスに変えて、場合によってはアラートを上げるとか。あとはデプロイの時になんらかの制御をするとか、そういうことは (Lambda 関数で)いろいろやっています。

あと、最後に書きましたが、最近はプロダクトの機能も1つ作っていて。弊社のプロダクトは、画面からなんらかのファイルをアップロードするというものがあったりするんですが、画面からじゃなくてETLのフローの中でファイルを自動的にファイルサーバーに上げたい、その処理をトリガーにしたいというお客さまもいたので、そういう機能をクラウドとLambdaで作ったようなこともやりました。

RustでLambdaを始める方法

「RustでLambdaってどうやってやるねん」という話ですが、実は非常にエコシステムが整っていて。(スライドを示して)ここに書いてあるURLに、「aws_lambda_rust_runtime」というものがあります。

Lambdaって、実はただ実行しているだけではなくて、しばらくはずっと動いていて。リクエストを受け付けて、そのリクエストを処理するようなループをしていて、その処理をランタイムと称して、言語ごとに用意されています。

Rustにもパッケージでランタイムが用意されていて、そのランタイムを使えるライブラリとか。あとはcargo lambdaというコマンド群が開発されていて、Lambdaのビルドとかデプロイとか。(スライドを示して)あとは下に書きましたが、プロジェクトの作成も簡単にできるようになっています。

通常Rustのプロジェクトを始める時はcargo newをすると思うんですが、cargo lambda newとすると……。普通にsrc/main.rsができるというのはcargo newと一緒ですが、「ここに書けばいいよ」というかたちでテンプレートを作ってくれます。

(スライドを示して)それがこの例です。ちょっと小さくて見づらいかもしれませんが、上のほうにあるのがRequest、いわゆる入力です。「どんな型でリクエストが来ますか」を規定するところ。Responseというところが、「どんなものを出力として返しますか」という型を自分で決める。

リクエストの型とかは自分で定義してもいいし、AWSのいろいろなサービスと組み合わせると、AWSのサービスがイベントを寄越してくるんですよね。イベントのスキーマとかは実はaws_lambda_eventsっていうクレートがあって、このクレートにみんな定義されているので、それをロードして指定するというかたちで指定することもできます。

指定した後、mainのところに手を入れることはまったくなくて。ランタイムを使うとなっているので、まったく手を加える必要はなく、プログラマーはhandlerの部分に自分のやりたいことを実装すればいい。

入力は引数にeventというものがあって、LambdaEventのRequest……。Requestがジェネリックにまさに入っています。こいつのところからevent.payload.commandとかすると、まさに入力に来たコマンドを引き出して、そのコマンドを使ってなにかやるということができるようになっています。もうテンプレートまでできているので、それを埋めていけばいいかたちになります。

RustでAWS Lambdaを使うメリット

RustでAWS Lambdaをやると何がいいかっていう話なんですが、高速実行と、あとはみなさんよく知っていること(だと思うの)ですが、やはりメモリが小さい。

Lambdaにとってこれは非常に重要です。Lambdaって、実行回数で課金体系が1つあるのと、時間×メモリ使用量でも1つの課金体系があります。時間×メモリ使用量が小さくなるので、課金が安く済むという話が1個あります。

あと、静的型付き言語というところも大きくて。後で見せますが、先ほど言ったような、AWSからイベントが入ってくるときのイベントのスキーマっていろいろ複雑だったり名前が長かったりするんですが、そういうのも全部型がクレートで用意されているので、エディターの補完を使っていけば間違えることがない。Pythonなどでやる時は「間違えないように」とけっこうコピペを一生懸命するんですが、そういうことをしなくて済む。

あと、シリアライズとかデシリアライズの処理もクレートで全部持ってくれるので、(シリアライズとかデシリアライズのについて)なにも気にしなくて良いというところもあります。

あともう1つは、コンパイルして1つのバイナリができます。なので、このバイナリをLambdaとしてデプロイする時って、ZIPでアーカイブしてS3にアップロードするんですが……。(そもそも)この1個だけを扱えばいいのもすごく楽で。これがPythonとかほかの言語になると、パッケージをどう入れるかとか、そういうことも考えなきゃいけないのですが、その必要がないというのがあります。

高速に実行できてメモリの使用量が小さくて済むということは、古いんですが、2年ぐらい前に試したことがあって。

(スライドを示して)これも某LT会でしゃべったものをそのまま持ってきたんですけど。実はRustとかGoとかTypeScriptとかPythonで、DynamoDB の読み書きとかまあまあいろいろやっているAPIをだいたい同じようなパフォーマンスというか、できる限りのパフォーマンスが出るような書き方をこれらの言語でやって、その実行時間とか課金時間の頻度分布(を調べたもの)です。

やはりRustは時間がすごく短い。つまり左側にちゃんと分布があって、それに対してTypeScriptやPythonは少し時間がかかり気味になってくる。

GoとかRustのようなコンパイル言語がこういうものに強いということが実際に試してみてわかって。本当は最新版も作りたかったんですが、ほとんど今も変わっていないんじゃないかなと思います。

(スライドを示して)それと、先ほど言った静的型付き言語であるというところがここ(の話)です。例えば先ほどのevent.payloadで、例えば右にある「S3」のイベントを引っ張り出そう、参照しようとする時に、例えばaws_regionというものが、右側のJSONのawsRegionとまさに対応するわけですよね。event_nameもeventNameとか(に対応する)。

だから、点、ピリオドを押せばぱっと補完が出てきて、そこから選べば良いというところで、非常に対応が良くプログラムが書けます。スペルを間違えるとか、キャメルケース、スネークケースを間違えるとかもなくて、非常に安心して書けるのは1つの大きなメリットです。

それと、シングルバイナリの話は先ほどお話ししましたが、cargo lambda buildというコマンドでbootstrapというファイルができるので、これをZIPでアーカイブして、AWSのマネジメントコンソールからアップロードすれば終わりです。

実はcargo lambdaにはもっと便利な機能がついていて、cargo lambda deployってやると、一連のことを全部やってアップロードしてくれます。

これがほかの言語、先ほどお話ししたように、Pythonの場合、pipで入れるライブラリをどこに配置しておかなきゃいけないとか、いろいろ考えてZIPアーカイブを作らなきゃいけません。そういうことをなにも考えないで良いのは、すごく楽だなと思っています。

AWSサービスとLambdaを組み合わせたサービス

最後、実際にプロダクトを作りましたという話で。(スライドを示して)これが何物かというと、右のところのファイルをアップロードして……。「Transfer Family」ってAWSのサービス、SFTPサーバーを提供してくれているサービスですね。

サービスからファイルを受け取って。あと細かいことはもういいんですが……。見てもらってわかるのは、Lambdaがいっぱいあるということです。Lambdaとか「SQS」とかDynamoDBとか、いろいろAWSのサービスがあるんですが、Lambdaをふんだんに活用して。

あとはやはり認証情報とかメール作成とか、いろいろな複数のコンポーネントを必要とするようなことは、もう1つのLambda関数にして、Lambda関数がLambda関数を呼ぶようなかたち。一種のマイクロサービスみたいなかたちになっていますが、そういうかたちでLambdaが細かなタスクを分担して、場合によってはLambda関数がLambda関数にいろいろタスクを任せながらやるというシステムを作りました。

これは時間的にはけっこう短い時間でできて。アプリをイチから作るよりはたぶん簡単にできたんじゃないかなと思っています。

参考情報としては、AWS SDK for RustがGAになった。これはつい最近の話ですが、Lambdaで書く時にAWSのリソースを操作するのにAWS SDKを使うんですが、それが今はもうGAになって、Previewが取れたというところ。

あと、同じようなスタイルでFluent Builder Styleで書けるみたいなところで。統一的な感覚で全部書けるようになっていて、非常に書きやすくなっているので、興味のある方は見てもらえると良いと思います。

まとめになります。Lambdaを使えるようになると、AWS活用の自由度がすごく広がります。Lambdaを書いてみようと思ったら、みなさんぜひRustで書いてみると、お気軽にできるんじゃないかなと思っております。

私からは以上です。ありがとうございました。