Help us understand the problem. What is going on with this article?

日本で 128 番目くらいに Ruby で AWS Lambda を試したメモ

More than 1 year has passed since last update.

これは

初老丸アドベントカレンダー 2018 の 8 日目の記事になる予定です.

https://qiita.com/advent-calendar/2018/syoroumaru

加藤さん, 事件です.

ずっと待たれていたはず, もはや諦めかけていたかもしれない AWS Lambda のランタイムに Ruby が追加されたとの一報が我々の元に入りました.

https://aws.amazon.com/jp/blogs/compute/announcing-ruby-support-for-aws-lambda/

おお〜. なんちゃって Rubist の私もこれまで作ってきた Ruby スクリプトを Lambda で実行出来るかもしれないという興奮が今も続いております.

ということで

早速, 上記の URL に掲載されているサンプルを参考に簡単な Ruby スクリプトを Lambda 上で動かしてみたいと思います.

検証環境

以下のような環境で動作確認を取ります.

$ ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
$ python --version
Python 3.7.0
$ sam --version
SAM CLI, version 0.8.0
$ aws --version
aws-cli/1.16.66 Python/3.7.0 Darwin/17.7.0 botocore/1.12.56

初めて sam を使ってみましたが, とても簡単に Lambda ファンクションのデプロイまで出来ました. Serverless framework と比較すると機能不足は否めませんが, AWS 純正なので新サービスへの対応も早かったりするのかなということで, もう少し使い込んでみようと思います.

まずは, サンプルイベントを生成する

とりあえず, ローカルで Ruby で書いた関数を動かしてみたいので, Lambda をローカルで実行する際に利用するサンプルイベントを生成します.

$ sam local generate-event s3 put --bucket=foo --key=bar > event_file.json

これを実行すると, 以下のような JSON イベントが生成されます.

$ cat event_file.json
{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-1",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "foo",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::foo"
        },
        "object": {
          "key": "bar",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}

今回は, この JSON イベントから S3 のオブジェクトキーを抜き出す関数を Ruby で書いてみます.

祝・Ruby で書く Lambda ファンクション

す, すいません. とりあえず, 雑に書きました.

def hello(event:, context:)
  key = event["Records"][0]["s3"]["object"]["key"]
  key
end

なんのひねりも, エラー処理もない, 自分の Ruby スキルのレベルが伺えるスクリプトです.

sam に必要な template.yaml

template.yaml (.yml ではない) を以下のように書きました.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: 'serverless ruby sample.'

Resources:
  ServerlessRubySample:
    Type: AWS::Serverless::Function
    Properties:
      Handler: sample.hello
      Runtime: ruby2.5

Outputs:
  ServerlessRubySample:
    Description: Serverless Ruby Sample Lambda Function ARN
    Value:
      Fn::GetAtt:
      - ServerlessRubySample
      - Arn

なんとなく CloudFormation のテンプレートっぽい佇まいです. Runtimeruby2.5 が眩しいです.

まずはローカルでテスト

sam にもローカルで Lambda ファンクションを実行出来る local invoke というサブコマンドが用意されていますので, これを利用することで簡単にローカルで実行することが可能です.

$ sam local invoke ServerlessRubySample -e event_file.json
2018-11-30 08:33:13 Found credentials in shared credentials file: ~/.aws/credentials
2018-11-30 08:33:14 Invoking sample.hello (ruby2.5)

Fetching lambci/lambda:ruby2.5 Docker container image......
2018-11-30 08:33:17 Mounting /path/to/serverless-ruby-sample as /var/task:ro inside runtime container
START RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72 Version: $LATEST
END RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72
REPORT RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72  Duration: 5.64 ms       Billed Duration: 100 ms Memory Size: 128 MB     Max Memory Used: 18 MB
"bar"

"bar" が出力されることを確認出来ます. あっという間にローカル環境で Ruby で書いた Lambda ファンクションが動いてしまいました.

あとはデプロイ

引き続き, デプロイです. sam では CloudFormation のテンプレートを書き出して, そのテンプレートを使って Create Stack するような感じでデプロイするようです. もしかしたら, Serverless Framework も同じような挙動なのかもしれません. 以下のように 2 ステップでデプロイしました.

# packaged-template.yaml を生成
sam package --template-file template.yaml \
  --output-template-file packaged-template.yaml \
  --s3-bucket oreno-sam-bucket

# packaged-template.yaml を利用してデプロイ
sam deploy --template-file packaged-template.yaml \
  --stack-name ServerlessRubySample \
  --capabilities CAPABILITY_IAM

以下のように出力されました.

# packaged-template.yaml を生成
$ sam package --template-file template.yaml \
> --output-template-file packaged-template.yaml \
> --s3-bucket oreno-sam-bucket
Uploading to b55e36e493168dc4d6627ee148c1ac97  1275 / 1275.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/kawahara/sandboxies/sam/serverless-ruby-sample/packaged-template.yaml --stack-name <YOUR STACK NAME>

# packaged-template.yaml を使って Create Stack しているのかな...
$ sam deploy --template-file packaged-template.yaml \
> --stack-name ServerlessRubySample \
--capabilities CAPABILITY_IAM> --capabilities CAPABILITY_IAM

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - ServerlessRubySample

動作確認

動作確認も Ruby で書きましょう. 以下のように aws-sdk-lambda を使ったコードです. 事前に aws-sdk-lambda をインストールしておきましょう.

require 'aws-sdk-lambda'
require 'json'

client = Aws::Lambda::Client.new(region: 'ap-northeast-1')

payload =<<"EOS"
{
  "Records": [
    {
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "foo",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::foo"
        },
        "object": {
          "key": "bar",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}
EOS

resp = client.invoke({
                         function_name: 'ServerlessRubySample-ServerlessRubySample-xxxxxxxxxxx',
                         invocation_type: 'RequestResponse',
                         log_type: 'None',
                         payload: payload
                       })

res = JSON.parse(resp.payload.string)
puts res

function_name だけは, マネジメントコンソールで確認しましょう. このスクリプトを適当に invoke.rb とか名前をつけて保存しておきます.

$ bundle exec ruby invoke.rb

以下のように出力されました.

$ bundle exe ruby invoke.rb
bar

おお〜, いい感じです. CloudWatch Logs にもログが記録されていることを確認しています.

以上

Ruby で Lambda ファンクションが書けるなんて, 今年の頭に誰が予想していたでしょうか (結構, 予想していたりしたらすいません). 本当に嬉しい限りです.

レッツ Ruby on Lambda〜〜.

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away