AWS re:Invent 2018 の Keynote の中で AWS Lambda や Serverless 関連のアップデートが色々と発表されましたが、その中に AWS Lambda で Ruby がサポートされたという発表がありました。
下記のように AWS のブログでチュートリアルも公開されているので、今回はとりあえず Ruby で Lambda を動かしてみます。
現在サポートされている Ruby のバージョンは 2.5 で、 AWS SDK for Ruby はデフォルトで使えるようになっているようです。
まずは Hello World
とりあえずは Lambda Function を作成して実行してみます。関数の作成画面では下記画像のようにランタイムとして Ruby2.5 が選択できるようになっています。
関数名やロール名は任意に決めて下記のような内容で作成します。
デフォルトで生成されるコードは下記のような内容です。
require 'json' def lambda_handler(event:, context:) # TODO implement { statusCode: 200, body: JSON.generate('Hello from Lambda!') } end
テストイベントを下記のように空のリクエストで設定します。
正しく実行できていれば下記のように成功レスポンスが表示されます。
ログは下記のような出力になります。
Response: { "statusCode": 200, "body": "\"Hello from Lambda!\"" } Request ID: "04397307-f507-11e8-abba-97121f20de35" Function Logs: START RequestId: 04397307-f507-11e8-abba-97121f20de35 Version: $LATEST END RequestId: 04397307-f507-11e8-abba-97121f20de35 REPORT RequestId: 04397307-f507-11e8-abba-97121f20de35 Duration: 10.04 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 31 MB
RubyGems を使う(Zipアップロード)
Ruby を使うからにはやっぱり gem が使いたくなります。AWS ブログのチュートリアルでは SAM でアップロードする方法が紹介されていますが、先にシンプルに zip に固めたものをアップロードする方法を試してみます。
まずは作業用ディレクトリを作ります。
$ mkdir lambda_gem_sample $ cd lambda_gem_sample/
gem は bundler を使って管理するので、下記の内容で Gemfile を作成します。今回はとりあえず gem が使えることが確認できれば良いので、 uuid という gem を使ってみます。
source 'https://rubygems.org' gem 'uuid'
bundler のインストール等については説明は割愛しますが、 インストールされているものとして下記コマンドを実行します。ローカルで使うだけであれば bundle install
だけで使えますが、 zip に固めてアップロードするには gem のファイルもローカルにダウンロードしておく必要があるので、 bundle install --deployment
も実行します。
$ bundle install
$ bundle install --deployment
実際に実行する Ruby スクリプトは下記のような内容で lambda_function.rb というファイル名で作成します。生成した UUID をレスポンスの文字列に含めているだけのものになります。
require 'json' require 'uuid' def lambda_handler(event:, context:) uuid = UUID.new { statusCode: 200, body: JSON.generate("Generated UUID: #{uuid.generate}") } end
ここまでで必要なものは用意できたので、作業ディレクトリのファイルを zip に固めます。
$ zip -r lambda_gem_sample.zip ./*
作成した zip ファイルを Lambda のコンソールからアップロードします。正しくアップロードされていれば下記のようにアップロードしたファイルがコンソールに表示されます。
空のテストイベントを作成してテストを実行すると、下記のように UUID の gem を使用したコードが実行され、生成された UUID が含まれた文字列がレスポンスとして返ってきます。
RubyGems を使う(SAM)
チュートリアルで紹介されている SAM を使う方法も試してみます。まずは作業ディレクトリを作成します。
$ mkdir hello_lambda_ruby $ cd hello_lambda_ruby/
Gemfile は下記のような内容で作成します。 aws-record は DynamoDB を操作するための gem です。
source 'https://rubygems.org' gem 'aws-record', '~> 2'
Gemfile を作成したら bundle install
を実行します。
$ /Users/akanuma/.rbenv/shims/bundle install
$ /Users/akanuma/.rbenv/shims/bundle install --deployment
Ruby のスクリプトは下記のような内容で hello_lambda_ruby_record.rb というファイル名で作成します。ちなみに AWS ブログに掲載されているコードをそのままコピペすると、 ENV[‘DDB_TABLE’]
のシングルクォートが正しくない(アポストロフィーになってる?)ので実行時にエラーになります。
require 'aws-record' class DemoTable include Aws::Record set_table_name ENV['DDB_TABLE'] string_attr :id, hash_key: true string_attr :body end def put_item(event:,context:) body = event["body"] item = DemoTable.new(id: SecureRandom.uuid, body: body) item.save! # raise an exception if save fails item.to_h end
AWS SAM はサーバレスアプリケーションの構成を管理するためのツールで、 Lambda アプリケーションの構造やセキュリティーポリシーの定義、AWSリソースの作成や管理を行うことができます。今回は DynamoDB を使用するのでそのための設定も含んでいます。設定ファイルは YAML で下記のような内容を template.yaml として作成します。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: 'sample ruby application' Resources: HelloLambdaRubyRecordFunction: Type: AWS::Serverless::Function Properties: Handler: hello_lambda_ruby_record.put_item Runtime: ruby2.5 Policies: - DynamoDBCrudPolicy: TableName: !Ref RubyExampleDDBTable Environment: Variables: DDB_TABLE: !Ref RubyExampleDDBTable RubyExampleDDBTable: Type: AWS::Serverless::SimpleTable Properties: PrimaryKey: Name: id Type: String Outputs: HelloLambdaRubyRecordFunction: Description: Hello Lambda Ruby Record Lambda Function ARN Value: Fn::GetAtt: - HelloLambdaRubyRecordFunction - Arn
AWS SAM のテンプレートは CloudFormation のコンソールか、 AWS CLI、 AWS SAM CLI のいずれかを使ってデプロイすることができます。このチュートリアルでは AWS SAM CLI でデプロイしていますので、まだインストールしていない場合はインストールしておきます。また、 s3 のバケット作成に AWS CLI も使用していますので、こちらもまだであればインストールしておきます。
CLI の準備ができたら、アプリケーションのコードをホストするための s3 のバケットを作成します。
$ aws s3 mb s3://hello-lambda-ruby make_bucket: hello-lambda-ruby
AWS SAM CLI を使用して、アプリケーションをパッケージングします。これによって packaged-template.yaml というファイル名のテンプレートファイルが作成されます。
$ sam package --template-file template.yaml \ > --output-template-file packaged-template.yaml \ > --s3-bucket hello-lambda-ruby Uploading to 02055cc5fec807d137f97cd532f60cd5 993290 / 993290.0 (100.00%) Successfully packaged artifacts and wrote output template to file packaged-template.yaml. Execute the following command to deploy the packaged template aws cloudformation deploy --template-file /Users/akanuma/workspace/hello_lambda_ruby/packaged-template.yaml --stack-name <YOUR STACK NAME>
続いて AWS SAM CLI を使ってアプリケーションをデプロイします。この時にAWSユーザに CloudFormation の権限が足りていないと下記のようにエラーになります。
$ sam deploy --template-file packaged-template.yaml \ > --stack-name helloLambdaRubyRecord \ > --capabilities CAPABILITY_IAM An error occurred (AccessDenied) when calling the CreateChangeSet operation: User: arn:aws:iam::365361468908:user/hiroaki.akanuma is not authorized to perform: cloudformation:CreateChangeSet on resource: arn:aws:cloudformation:ap-northeast-1:365361468908:stack/helloLambdaRubyRecord/*
とりあえず CloudFormation のフルアクセス権限を追加したかったのですが、ポリシーを検索してもそれに当たるものが見つからなかったので、インラインポリシーで直接フルアクセス権限を追加してみました。
追加後に実行すると下記のようにデプロイが成功しました。
$ sam deploy --template-file packaged-template.yaml --stack-name HelloLambdaRubyRecord --capabilities CAPABILITY_IAM Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - HelloLambdaRubyRecord
デプロイが終わると Lambda のアプリケーションコンソールに下記のようにアプリケーションが表示されます。
アプリケーションのリソースには Lambda Function と DynamoDB のテーブルが含まれています。
Lambda Function の方を選択して Lambda のコンソールを開き、下記のような内容のテストイベントでテストを実行します。
正しくデプロイされていてコードの内容に間違いがなければ、下記のように成功レスポンスが返ってきます。
DynamoDB のコンソールからテーブルの中身を見てみると、テストイベントで送信したリクエストの内容が保存されています。
まとめ
Lambda で Ruby が使えるようになったことで、 Ruby エンジニアにとってはかなりハードルが下がりましたね。bundler 管理で gem を使うこともできますので、色々と便利に使えそうです。ローカルでの開発と比べると、開発途中での gem の追加の容易さや、修正から実行確認の手返しの良さは劣るところになりそうですが、サーバレス環境での開発方法も最適な方法を探していきたいと思います。