Serverless Frameworkのserverless.ymlが長くなってきたので外部ファイルから読み込むようにしてみる
どうも!大阪オフィスの西村祐二です。
サーバーレスアプリケーションを開発するときにServerless Frameworkを使って開発している人は多いのではないでしょうか。
Lambda関数、プラグインの設定、AWSリソースが増えてくるとserverless.ymlがどんどん長くなってきます。
拡張やメンテナンスなど行いやすくするために、今回、serverless.ymlの設定を外部のファイルから読み込むようにしてみたいと思います。
環境
- OS : macOS Mojave 10.14.3
- Node.js : v10.15.1
- Serverless Framework: 1.37.0
対象のserverless.yml
今回は下記のようなサンプルを用意しました。
複数のLambda関数、プラグイン、S3、DynamoDBのAWSリソースを管理した構成になっています。
下記のサンプルをもとに設定していきます。
service: test-blog # NOTE: update this with your service name provider: name: aws runtime: python3.7 stage: v1 apiName: ${self:custom.env}-${self:service} environment: DYNAMODB_TABLE: ${self:custom.env}-${self:service} iamRoleStatements: - Effect: Allow Action: - dynamodb:Query - dynamodb:Scan - dynamodb:GetItem - dynamodb:PutItem - dynamodb:UpdateItem - dynamodb:DeleteItem Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}" plugins: - serverless-dynamodb-local custom: env: ${opt:env, 'itg'} dynamodb: start: port: 8000 inMemory: true migrate: true seed: true seed: development: sources: - table: ${self:provider.environment.DYNAMODB_TABLE} sources: [./data/test.json] package: individually: true include: - handler.py exclude: - "**" functions: hello: handler: handler.hello events: - http: path: sample/test method: get create: handler: handler.create events: - http: path: todos method: post cors: true list: handler: handler.list events: - http: path: todos method: get cors: true get: handler: handler.get events: - http: path: todos/{id} method: get cors: true update: handler: handler.update events: - http: path: todos/{id} method: put cors: true delete: handler: handler.delete events: - http: path: todos/{id} method: delete cors: true resources: Resources: testS3Bucket: Type: "AWS::S3::Bucket" Properties: BucketName: hoge-ynishimura-bucket testTable: Type: AWS::DynamoDB::Table Properties: TableName: ${self:custom.env}-${self:service} AttributeDefinitions: - AttributeName: email AttributeType: S KeySchema: - AttributeName: email KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 1 WriteCapacityUnits: 1
IAMに関する設定を外部ファイルから読み込むようにする
configというディレクトリを作り、そこの配下にiam.ymlを作成します。(これは任意で各々で適切な形で作成してください。)
そこに、IAMの設定を書き出します。
嬉しい点としては外部から設定を読み込む形にしても${self:provider.environment.DYNAMODB_TABLE}
など変数をきちんとパースして認識してくれるところです。
- Effect: Allow Action: - dynamodb:Query - dynamodb:Scan - dynamodb:GetItem - dynamodb:PutItem - dynamodb:UpdateItem - dynamodb:DeleteItem Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"
serverless.yml
は下記のようにfileプロパティを使うことで読み込むことができます。
environment: DYNAMODB_TABLE: ${self:provider.custom.env}-${self:service} iamRoleStatements: ${file(./config/iam.yml)} plugins: - serverless-dynamodb-local
どのように読込されるかの動作確認としては下記コマンドで簡単に確認できます。
$ sls print service: test-blog provider: stage: v1 apiName: itg-test-blog name: aws runtime: python3.7 environment: DYNAMODB_TABLE: itg-test-blog iamRoleStatements: - Effect: Allow Action: - 'dynamodb:Query' - 'dynamodb:Scan' - 'dynamodb:GetItem' - 'dynamodb:PutItem' - 'dynamodb:UpdateItem' - 'dynamodb:DeleteItem' Resource: 'arn:aws:dynamodb:us-east-1:*:table/itg-test-blog' plugins: - serverless-dynamodb-local . . .
プラグインの設定を外部から読み込む
先程と同様に下記のようにプラグインの設定を外出しします。
start: port: 8000 inMemory: true migrate: true seed: true seed: development: sources: - table: ${self:provider.environment.DYNAMODB_TABLE} sources: [./data/test.json]
serverless.yml
は下記のようにfileプロパティを使うことで読み込むことができます。
plugins: - serverless-dynamodb-local custom: env: dynamodb: ${file(./config/plugin/serverless-dynamodb-local.yml)} package: ・ ・ ・
Lambda関数のイベントに関する設定を外部から読み込む
今回、configディレクトリ配下にevents.yml
を作成し、そこから読み込むようにしてみました。
下記のように、各Lambdaのイベントの設定を書き出します。
hello_event: - http: path: sample/test method: get create_event: - http: path: todos method: post cors: true list_event: - http: path: todos method: get cors: true get_event: - http: path: todos/{id} method: get cors: true update_event: - http: path: todos/{id} method: put cors: true delete_event: - http: path: todos/{id} method: delete cors: true
serverless.yml
は下記のようにfileプロパティのあとにキーを指定することで個別に読み込むことができます。
functions: hello: handler: handler.hello events: ${file(./config/events.yml):hello_event} create: handler: handler.create events: ${file(./config/events.yml):create_event} list: handler: handler.list events: ${file(./config/events.yml):list_event} get: handler: handler.get events: ${file(./config/events.yml):get_event} update: handler: handler.update events: ${file(./config/events.yml):update_event} delete: handler: handler.delete events: ${file(./config/events.yml):delete_event} resources:
AWSリソースのcfnテンプレートを外部から読み込む
今回、configディレクトリ配下にcfnディレクトリを作りs3.yml
とdynamodb.yml
に分けて、そこから読み込むようにしてみました。
下記のように、cfnテンプレートの設定を書き出します。
Resources: testS3Bucket: Type: "AWS::S3::Bucket" Properties: BucketName: hoge-ynishimura-bucket
Resources: testTable: Type: AWS::DynamoDB::Table Properties: TableName: ${self:custom.env}-${self:service} AttributeDefinitions: - AttributeName: email AttributeType: S KeySchema: - AttributeName: email KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 1 WriteCapacityUnits: 1
注意点としてはResources
ではじまるように記述しないといけないところです。
https://serverless.com/framework/docs/providers/aws/guide/variables#multiple-configuration-files
serverless.yml
は下記のようにfileプロパティを使ってファイルを指定するだけでcfnテンプレートを読み込むことができます。
delete: handler: handler.delete events: ${file(./config/events.yml):delete_event} resources: - ${file(./config/cfn/s3.yml)} - ${file(./config/cfn/dynamodb.yml)}
最終的なserverless.yml
最終的なserverless.ymlは下記のようになりました。
ある程度スッキリしたのではないでしょうか。
service: test-blog # NOTE: update this with your service name provider: name: aws runtime: python3.7 stage: v1 apiName: ${self:custom.env}-${self:service} environment: DYNAMODB_TABLE: ${self:custom.env}-${self:service} iamRoleStatements: ${file(./config/iam.yml)} plugins: - serverless-dynamodb-local custom: env: ${opt:env, 'itg'} dynamodb: ${file(./config/plugin/serverless-dynamodb-local.yml)} package: individually: true include: - handler.py exclude: - "**" functions: hello: handler: handler.hello events: ${file(./config/events.yml):hello_event} create: handler: handler.create events: ${file(./config/events.yml):create_event} list: handler: handler.list events: ${file(./config/events.yml):list_event} get: handler: handler.get events: ${file(./config/events.yml):get_event} update: handler: handler.update events: ${file(./config/events.yml):update_event} delete: handler: handler.delete events: ${file(./config/events.yml):delete_event} resources: - ${file(./config/cfn/s3.yml)} - ${file(./config/cfn/dynamodb.yml)}
また、分割した設定ファイルとserverless.ymlのディレクトリ構成は下記のようになっています。(他の関連ファイルは非表示)
. ├── config │ ├── cfn │ │ ├── dynamodb.yml │ │ └── s3.yml │ ├── events.yml │ ├── iam.yml │ └── plugin │ └── serverless-dynamodb-local.yml └── serverless.yml
さいごに
いかがだったでしょうか。
serverless.ymlの設定を外部のファイルから読み込むようにしてみました。
イベント、AWSリソースだけでも外部ファイルから読み込むようにするとかなりスッキリするのではないでしょうか。
また、他にもいろいろ試していたのですが、かなり柔軟性があり、コマンド引数によって動的に読み込む設定を切り替えたりもできますので、興味のある方は是非試してみてください。
誰かの参考になれば幸いです。