エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

AWS Lambda でも Rails で Web 開発

本日はコンシューマチームのブログリレー2日目です。

エムスリーエンジニアリングG コンシューマチームの松原(@ma2ge)です。 今回は以前筆者が M3 Tech Talk で話した AWS Lambda での Web アプリ開発に Rails を使う内容について、 若干内容を変えつつ Tech Blog の方でも紹介をしたいと思います。

最近使っているキーボードの様子
最近使っているキーボードの様子

現在のチームで担当している主要なアプリは Rails で書かれていて、ほとんどがコンテナ化され Amazon ECS(ECS) と Fargate を使って運用されています。 そんな中今年の初めに AWS Lambda(Lambda) に適したプロジェクトが話にあがりました。ただ Rails で Lambda しかも Web アプリとなるとあまり採用事例を耳にしません。 とはいえ使い慣れている Rails をそのまま生かして Lambda でも開発したいですよね。今は Lambda もコンテナに対応していますし、Fargate と同じような開発ができると体験が良さそうです。 そこで便利なのが Lamby という gem です。

Lamby とは

Lamby は Lambda とやりとりする薄い Rack アダプターとして振る舞ってくれる gem です。 Lambda で Web API を作る場合には API Gateway や Application Load Balancer(ALB) などと組み合わせる必要がありますが、それらとのやりとりに関わる差分を吸収してくれ Rack に対応したアプリ*1へ繋いでくれます。 そのため Rails で開発するアプリは、Lambda を意識することなく開発ができ*2、それをそのまま Lambda 上で動かすことまでできてしまいます。かっこいいですね! 上手く使えば ECS と Lambda で動かすコンテナイメージを同じものにできるので、コンテナ化してしまえば後は運用が適しているインフラに適材適所で使い分けられてとても便利です。

動かし方

簡単に動かし方を紹介です。詳細は公式ドキュメントをご覧ください。

Gemfile に Lamby を追加し、

# Gemfile 抜粋
gem 'aws_lambda_ric'
gem 'lamby'

Docker イメージで動かす場合は Dockerfile に以下のコマンドを並べれば OK です。

# Dockerfile 抜粋
# 現時点で最新の Ruby 3.2.2 を使った例ですが、できる限り新しいものを使っていきましょう。
FROM ruby:3.2.2-slim

...省略...

ENTRYPOINT [ "/usr/local/bundle/bin/aws_lambda_ric" ]
CMD ["config/environment.Lamby.cmd"]

後はイメージをビルドしたものを Amazon Elastic Container Registry にプッシュして Lambda 側で使えば良いです。今回は触れませんが、Lambda を Web API として機能させるために API Gateway や ALB と組み合わせる作業の方が大変かもしれないですね。

秘匿情報の扱い

Lambda の場合に困るのが秘匿情報として扱う必要のある SECRET_KEY_BASE のような環境変数ですが、これは Rails の初期化時に AWS Systems Manager Parameter Store 経由で取得するコードを書いて対応しました。できれば ECS のタスク定義パラメータにある secrets のように Lambda 側で指定できて、アプリ側は意識せずに済むと嬉しいので、いつか実装されることを心待ちにしています。

Lambda と Rails の組み合わせで検討したこと

Cold Start 問題

Lambda について回るのは Cold Start の問題です。Rails も起動はそれほど早くないので、何も設定をしないと 3, 4 秒以上かかるケースもありました。 これを改善する必要があるのですが、起動時間を短縮するためには公式ドキュメントにもあるように Bootsnap を有効化して事前の precompile やアプリのロードをしてキャッシュを作っておくことが有効でした。これらの設定をすることで遅くても2秒かからない程度になることが確認できたので、これは対応しておくのが良いかと思います。

実際に運用しているサービスの直近のデータを見てみましたが、1日のアクセス数が約4万の時で、1,2回程度2秒に満たない Cold Start による処理遅延が見られました。それ以外のほとんどは 200 msec 以内におさまっており、当プロジェクトにおいては許容できるレベルと判断しています。 時間にシビアなサービスでは困る可能性はありますが、ある程度許容されるレベルのサービスであれば使いどころがありそうです。

Lambda のサポートする Ruby バージョン

Ruby 向けの Runtime Interface Client(RIC) が Ruby 3 系に対応しないままの状態であったので懸念事項でした。ただ少なくとも今回のプロジェクトではエラー等は特に発生せずに使えており、また Lambda runtime API の仕様やコードも公開されていることから最悪 fork するなどで対応できそうだったので進めることにしました。幸い開発途中で Lambda での Ruby 3.2 のサポートが行われるニュースもあり RIC も対応するだろうと安心していたのですが、まだ 3 系に対応したものがリリースされていないので今後対応されることを期待しています。

まとめ

以上 Lambda と Rails を組み合わせた事例の紹介をしました。Lambda での開発でも、Rails の知識をそのまま活用できるのはありがたいです。 また使ってみて意外と便利だと感じたのは、QA 環境のコストが動かしていない時はほぼ 0 なことです。Fargate 等で作る場合は、QA 環境を使っていない時間帯はタスクを 0 にしておくためのスケジュールを作る手間があったり、コストも不要にかかったりということがあるのですが、その点を気にせず必要な時に起動してくれるのはとても助かりました。

Lamby の他に AWS Lambda Web Adapter も検討したのですが、長文になってしまうので今回は取り上げませんでした。こちらも面白い仕組みで Tech Talk の動画では少し紹介しているので、もし興味があればこちらもご覧ください。

www.youtube.com

We are hiring!

エムスリーでは技術が好きなエンジニアを募集しております。ご興味がありましたら、お気軽にお問い合わせください。

jobs.m3.com

*1:例えば Sinatra や Rails など

*2:Lambda でできないことは気にする必要はあります