Serverless Framework とは AWS Lambda などの FaaS を構成管理するツールです。AWS 専用というわけではなくさまざなクラウドプロバイダの FaaS をサポートしています。
今回は AWS Lambda の構成管理を試したかったので下記のドキュメントを参考に素振りしてみました。
素振り
serverless
コマンドをインストールします。下記のワンライナーでインストールできます。
curl -o- -L https://slss.io/install | bash serverless --version
最小っぽい構成でデプロイしてみます。関数が未定義なので実質何もない構成です。
# serverless.yml service: serverless-aws-hello provider: name: aws region: ap-northeast-1
serverless deploy
でデプロイを実行します。
serverless deploy -v
Serverless Framework は serverless.yml の内容を元に CloudFormation のテンプレートを作って構成をデプロイします。-v
オプションを付けると CloudFormation によるデプロイの進捗も表示されます。
デプロイ後にマネジメントコンソールで CloudFormation を見てみると次のリソースが作成されていました。
- AWS::S3::Bucket
- AWS::S3::BucketPolicy
以下のドキュメントによると、この S3 バケットへ Lambda 関数のコードがパッケージングされた zip アーカイブがアップロードされ、CloudFormation で S3 オブジェクトをソースに Lambda へデプロイされます。
なので、何もない最小の構成でもこの S3 バケットは最初に作成されます。
次のように deploymentBucket.name
で既存の作成済バケットを指定すればこの S3 バケットを作成しないようにもできます。
ervice: serverless-aws-hello provider: name: aws region: ap-northeast-1 deploymentBucket: name: ore-no-serverless-deployment-bucket
初めの構成に Lambda 関数を追加してみます。
service: serverless-aws-hello provider: name: aws region: ap-northeast-1 runtime: nodejs12.x functions: hello: handler: handler.hello
次のリソースが追加されました。
- AWS::Logs::LogGroup
- AWS::IAM::Role
- AWS::Lambda::Function
- AWS::Lambda::Version
serverless invoke
や serverless logs
で Lambda 関数を実行したり、ログを表示したりできます。
# 実行 serverless invoke -f hello # ログ表示 serverless logs -f hello -t
serverless invoke local
でローカル実行できます。
# ローカル実行 serverless invoke local -f hello
js のコードを適当に書き換えで再デプロイしてみます。
vim handler.js
serverless deploy -v
次のリソースが追加/更新されました。Lambda 関数の新しいバージョンが作成されて LATEST がそのバージョンに更新されています。
- AWS::Lambda::Version (CREATE)
- AWS::Lambda::Function (UPDATE)
serverless deploy function
で関数名を指定してデプロイすると CloudFormation を経由せずに Lambda に直接アップロードされるため素早く更新できます。
serverless deploy function -v -f hello
REST API になるように構成を変更してみます。
functions: hello: handler: handler.hello events: - http: path: hello method: get
デプロイすると次のリソースが作成されました。
- AWS::Lambda::Function (UPDATE)
- AWS::ApiGateway::RestApi (CREATE)
- AWS::ApiGateway::Resource (CREATE)
- AWS::ApiGateway::Method (CREATE)
- AWS::ApiGateway::Deployment (CREATE)
- AWS::Lambda::Permission (CREATE)
HTTP で呼べるようになっています。
curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello
Cloud Watch Event からスケジュール実行させてみます。
functions: hello: handler: handler.hello events: - schedule: cron(* * * * ? *)
次のリソースが作成されました。
- AWS::Lambda::Function (UPDATE)
- AWS::Events::Rule (CREATE)
- AWS::Lambda::Permission (CREATE)
SNS をイベントソースにしてみます。
functions: hello: handler: handler.hello events: - sns: hello
次のリソースが作成されました。SNS トピック自体もこれだけで作成されています。
- AWS::Lambda::Function (UPDATE)
- AWS::Lambda::Permission (CREATE)
- AWS::SNS::Topic (CREATE)
serverless を実行するための最小の IAM ポリシー
serverless を実行する側の IAM に必要なポリシーについて、下記のドキュメントから、
下記の gist が参照されていました。
Lambda の Execution role を作るためには仕方ないですが IAM 関係のアクションが許可されているなら実質 AdministratorAccess と同じなのではという気がしないでもないです。
次のように作成済の Role を指定すれば新たに作成されることはないので、必要なものだけに絞ったもっと小さいポリシーにもできそうです。
service: serverless-aws-hello provider: name: aws region: ap-northeast-1 runtime: nodejs12.x role: arn:aws:iam::999999999999:role/service-role/ore-no-lambda-role functions: hello: handler: handler.hello
パッケージング
下記のドキュメントによると、
https://www.serverless.com/framework/docs/providers/aws/guide/packaging/
下記のパターンにマッチするファイルは Lambda にアップロードされるコードから自動的に除外されます。
.git/** .gitignore .DS_Store npm-debug.log .serverless/** .serverless_plugins/**
serverless.yml で exclude や include を指定して細かく制御もできます。
service: serverless-aws-hello provider: name: aws region: ap-northeast-1 runtime: nodejs12.x package: exclude: - .envrc - src/*.js include: - src/handler.js functions: hello: handler: handler.hello
exclude
には !src/handler.js
のような記述もできます。.gitignore
などでお馴染みですが、!
を前置すればそのパターンにマッチするファイルは他の exclude
のパターンにマッチしていたとしても除外されなくなります。
exclude
の !
の前置と、include
について、どちらも効果は似たようなものです。どちらも exclude
で除外したもののうち含めたいものを指定します。ただし後述の通り違いもあります。
デフォでは node_modules/
から devDependencies
のパッケージは自動的に除外されます。ただ include
に node_modules/**
などが含まれると devDependencies
のパッケージでもすべて含まれるようになります。exclude
で !node_modules/**
などとすれば devDependencies
は除外されたままです。
例えば、特定のディレクトリのみを含めるため、次のようにすべてを exclude
したうえで include
で必要なディレクトリを指定したとします。
package: exclude: - '**' include: - 'dist/**' - 'node_modules/**'
これだと devDependencies
が含まれてしまします。
次のように exclude
で !
前置で指定すれば期待した結果になります。
package: exclude: - '**' - '!dist/**' - '!node_modules/**'
resources
serverless.yml
の resources
に記載したないようはそのまま CloudFormation のテンプレートの内容になります。
また、serverless.yml
の内容は最終的には CloudFormation のテンプレートになるので、resources
以外でもいろいろなば場所で CloudFormation の記法が利用可能です。
例えば resources
で SQS キューを作成して、その ARN や URL を Lambda の環境変数に入れたりできます。
functions: hello: handler: handler.hello environment: # ↓で作成したリソースを参照する HELLO_SQS_QUEUE_URL: { Ref: HelloSqs } resources: # CloudFormation のテンプレートそのまま Resources: HelloSqs: Type: AWS::SQS::Queue Properties: QueueName: hello
iamRoleStatements
iamRoleStatements
で Lambda 関数が使用する IAM Role のポリシーが定義できます。
provider: name: aws runtime: nodejs12.x iamRoleStatements: - Effect: "Allow" Action: - "ssm:GetParameter" Resource: - "*"
ポリシーはこの内容でそのまま作成されるわけではなく、Lambda 関数がログ出力やイベントを処理するために必要なポリシーが自動的に追加されます。なので logs:PutLogEvents
などのポリシーは記述する必要はありません。
または role
で ARN を指定すれば既存の IAM Role が使用できます。この場合は自動でポリシーが追加されたりはしないので必要なポリシーを設定しておく必要があります。
provider: name: aws role: arn:aws:iam::9999999999:role/ore-no-lambda-role
destinations
destinations.onSuccess
や destinations.onFailure
で Lambda 関数の成功時や失敗時の後段の宛先を指定できます。
functions: hello: handler: handler.hello destinations: onSuccess: success onFailure: arn:aws:sns:ap-northeast-1:9999999999:ore-no-sns-topic success: handler: handler.success
この要素では CloudFormation の記法は使えず、以下のどちらかをベタに指定する必要があります。
- serverless.yml で定義された他の関数名
- SNS トピックや SQS キューなどの ARN をベタに記述
下記あたりで startsWith
メソッドが呼ばれているため文字列しか指定できないためです。CloudFormation の記法だと { Ref: Hoge }
みたいなオブジェクトになるのでエラーになります。
- https://github.com/serverless/serverless/blob/v1.72.0/lib/plugins/aws/package/compile/functions/index.js#L594
- https://github.com/serverless/serverless/blob/v1.72.0/lib/plugins/aws/package/compile/functions/index.js#L609
ので SNS トピックなどを指定しようとするとリージョン名やアカウント番号をベタに書く必要があります。
あるは Serverless Pseudo Parameters プラグインを使えばベタ書きを回避できます。
まずプラグインを次のようにインストールします。
serverless plugin install -n serverless-pseudo-parameters
次のようにリージョン名やアカウント番号に書き換えられます。
plugins: - serverless-pseudo-parameters functions: hello: handler: handler.hello destinations: onFailure: arn:aws:sns:#{AWS::Region}:#{AWS::AccountId}:ore-no-sns-topic
さいごに
いらなくなったら serverless remove
でキレイに消せます。
serverless remove -v
Lambda 関数を作るとき、手動でマネジメントコンソールからポチポチするのは辛いので AWS リソースは Terraform で管理しつつ Lambda 関数のコードは Makefile でデプロイ するようにしていました。
が、今日日は Serverless Framework とかのほうが主流なのかなー名前よく聞くし、と思ったので、試してみました。
試してみたものの、Serverless Framework は CloudFormation ありきだし、デプロイのために S3 バケットが作られたりするので、操作可能なポリシーが制限されている状況だと使えなさそうでした(IAM Role の作成などができない制限されたポリシーの範囲内で作業することがちょいちょいある)。
普通に CloudFormation が使える状況なのなら便利かもしれません。Terraform で API Gateway を管理するの、エンドポイントがたくさんあると怠すぎると思います・・ただ、Lambda 関数以外にいろいろリソースを作ろうとすると CloudFormation の知識が必要になってくるので、AWS の構成管理を Terraform に振り切ってる今のこの現状からあえて CloudFormation に切り替えるのも大変そうです。
簡単な Lambda 関数なら今後も Terraform + Makefike で良いかなと思います。
あるいは API Gateway とかと絡める必要があるなら Lambda 周りだけ Serverless Framework で、その他の resources
に書くような内容は Terraform で、のように使い分けるのも有りかもしれません。