3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UUUMAdvent Calendar 2018

Day 21

Serverless FrameworkとRubyでLambda Layers

Posted at

はじめに

この記事は、UUUM Advent Calendar 2018 21日目の記事です。

すでに出遅れている感がありますが、今年のre:Inventで発表されたLambdaのruby対応(Custome runtimes)とLambda LayersServerless Frameworkで試してみました!

AWS Lambda Layers

今までは各Lambda関数が共通で使用するライブラリであっても、各関数のパッケージに同梱する必要があったためライブラリの管理が手間だったりパッケージのサイズが肥大化したりなどといった問題がありました。
AWS Lambda Layersが登場したことで共通的な処理をLayerとしてまとめて切り出すことができ、管理がしやすくなり、各パッケージに同じ処理を毎回同梱させる必要がなくなりました。

Layerの用意

まず、Layer側から用意していきます。
今回はDB(DynamoDB)処理周りをlayerとして切り出してみました。

serverless.yaml

Layerに関する定義と、併せてDynamoDBの定義も記載しておきます。
当然のことですが、LayerとFunctionでregionは同じにする必要があるので明示的にap-northeast-1を指定しておきます。

service: aws-ruby-layers

provider:
  name: aws
  region: ap-northeast-1

layers:
  dbLayer:
    path: layers
    compatibleRuntimes:
      - ruby2.5

resources:
  Resources:
    FoodsDynamoDbTable:
      Type: 'AWS::DynamoDB::Table'
      Properties:
        AttributeDefinitions:
          -
            AttributeName: name
            AttributeType: S
        KeySchema:
          -
            AttributeName: name
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: "foods"

layerのソースコード

DynamoDB上のfoodテーブルからnameを指定してevaluationを取得するコードです。

require 'aws-sdk'

class Food
  def initialize
    @dynamoDB = Aws::DynamoDB::Resource.new(region: 'ap-northeast-1')
  end

  def evaluation(food_name)
    table = @dynamoDB.table('foods')
    resp = table.get_item({
      key: { 'name' => food_name }
    })

    resp.item['evaluation']
  end
end

deploy

これでLayer側は完成なので、deployします。
sls deploy と入力するだけで諸々のリソースが構築されます。
また、最後に出てくるarnは後から使うのでメモしておきます。

$ sls deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (376 B)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
.........
Serverless: Stack update finished...
Service Information
service: aws-ruby-layers
stage: dev
region: ap-northeast-1
stack: aws-ruby-layers-dev
api keys:
  None
endpoints:
  None
functions:
  None
layers:
  dbLayer: arn:aws:lambda:ap-northeast-1:xxxxxxxxxxx:layer:dbLayer:1

Functionの用意

続いてLayerの処理を呼び出すFunctionを構築していきます。
まず sls create で雛形を作成します。


$ sls create --template aws-ruby --name lambda-ruby-func
Serverless: Generating boilerplate...
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.35.1
 -------'

Serverless: Successfully generated boilerplate for template: "aws-ruby"

これで、以下のようなファイルが生成されます。

.
├── handler.rb
└── serverless.yml

serverless.yaml

生成されたserverless.yamlを編集していきます。
DynamoDBを利用するためのIAMRoleの設定は、layer側ではなくfunction側で定義します。
また、functionにlayerを利用する旨定義し、layerのdeploy時に記載されていたarnを定義しておきます。

service: lambda-ruby-func

provider:
  name: aws
  runtime: ruby2.5
  region: ap-northeast-1
  iamRoleStatements:
    - Effect: "Allow"
      Resource: "arn:aws:dynamodb:ap-northeast-1:*:*"
      Action:
        - "dynamodb:*"

functions:
  hello:
    handler: handler.hello
    layers:
      - arn:aws:lambda:ap-northeast-1:xxxxxxxxx:layer:dbLayer:1

Functionのソースコード

Layerを呼び出す際のコードです。
ここで少しハマったのですが、Layerのコードは/opt配下に格納されます。
Pythonなどではlayerの/opt配下は自動的にライブラリ検索パスに追加されるとのことだったのですが、
rubyの$LOAD_PATHには追加されていませんでした。。
そのためとりあえず絶対パスでrequireして読み込んでいます。
(もっといいやり方があったらどなたか教えてください!)

require 'json'
require '/opt/dynamodb_client'

def hello(event:, context:)
  food = Food.new
  name = 'カレー'
  evaluation = "#{name}#{food.evaluation(name)}"
  { statusCode: 200, body: JSON.generate(evaluation) }
end

deploy & invoke

今回はlambdaのイベントソースを特に指定していないので、ローカルから直接invokeします。
DynamoDBにはあらかじめ適当なデータを登録してあります。

$ sls deploy
 〜省略〜
$ sls invoke --function hello
{
    "statusCode": 200,
    "body": "\"カレーは最高に美味い\""
}

まとめ

共通処理をLayerに切り分けることができるようになり、サーバーレス関連の設計がより柔軟にできるようになったと感じました。
今回は雑な切り分けでLayerを分けましたが、今後はより良いLayerの切り分け方を模索していけたらと思います。

宣伝

UUUMではエンジニアを募集しています!!
詳しくは下記のリンクをご参照ください。

https://www.wantedly.com/projects/9783
https://www.wantedly.com/projects/25995

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?