Serverless FrameworkとRubyでLambda Layers
Serverless FrameworkとRubyでLambda Layers:
この記事は、UUUM Advent Calendar 2018 21日目の記事です。
すでに出遅れている感がありますが、今年のre:Inventで発表されたLambdaのruby対応(Custome runtimes)とLambda LayersをServerless Frameworkで試してみました!
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側から用意していきます。
今回はDB(DynamoDB)処理周りをlayerとして切り出してみました。
Layerに関する定義と、併せてDynamoDBの定義も記載しておきます。
当然のことですが、LayerとFunctionでregionは同じにする必要があるので明示的にap-northeast-1を指定しておきます。
DynamoDB上のfoodテーブルからnameを指定してevaluationを取得するコードです。
これでLayer側は完成なので、deployします。
また、最後に出てくるarnは後から使うのでメモしておきます。
続いてLayerの処理を呼び出すFunctionを構築していきます。
まず
これで、以下のようなファイルが生成されます。
生成されたserverless.yamlを編集していきます。
DynamoDBを利用するためのIAMRoleの設定は、layer側ではなくfunction側で定義します。
また、functionにlayerを利用する旨定義し、layerのdeploy時に記載されていたarnを定義しておきます。
Layerを呼び出す際のコードです。
ここで少しハマったのですが、Layerのコードは/opt配下に格納されます。
Pythonなどではlayerの/opt配下は自動的にライブラリ検索パスに追加されるとのことだったのですが、
rubyの$LOAD_PATHには追加されていませんでした。。
そのためとりあえず絶対パスでrequireして読み込んでいます。
(もっといいやり方があったらどなたか教えてください!)
今回はlambdaのイベントソースを特に指定していないので、ローカルから直接invokeします。
DynamoDBにはあらかじめ適当なデータを登録してあります。
共通処理をLayerに切り分けることができるようになり、サーバーレス関連の設計がより柔軟にできるようになったと感じました。
今回は雑な切り分けでLayerを分けましたが、今後はより良いLayerの切り分け方を模索していけたらと思います。
UUUMではエンジニアを募集しています!!
詳しくは下記のリンクをご参照ください。
https://www.wantedly.com/projects/9783
https://www.wantedly.com/projects/25995
はじめに
この記事は、UUUM Advent Calendar 2018 21日目の記事です。すでに出遅れている感がありますが、今年のre:Inventで発表されたLambdaのruby対応(Custome runtimes)とLambda LayersをServerless 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
コメント
コメントを投稿