Posted at
UUUMDay 21

Serverless FrameworkとRubyでLambda Layers


はじめに

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

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


AWS Lambda Layers

https://aws.amazon.com/jp/blogs/news/new-for-aws-lambda-use-any-programming-language-and-share-common-components/

今までは各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