はじめに
serverless frameworkを利用してクラウドリソースを管理する場合、小規模なうちはserverless.ymlにすべての内容を記載すると思います。
しかし対象リソースが増えてきた場合、一つのymlにすべての設定を記述するととても見づらくなるため、リソース種類ごと等の単位でymlファイルを分割し、serverless.ymlから該当ファイルを参照することで見通しの良いIaCが実現できます。
ただ、ファイルを分割してディレクトリを分ける場合、変数や外部ファイルへの参照パスも変えなきゃいけないのか…と思う方もおられるかもしれません。
自分自身疑問に思いましたので、実際に検証してみました。
結論
結論から申し上げると、分割したymlテンプレート側であっても、パスの指定は読込先のserverless.ymlから見たパス
を指定します。
検証
以下のような、AWSのLambda関数を管理するプロジェクトがあるとします。
env
ディレクトリにLambdaの環境変数を管理するyml、functions
ディレクトリにLambda関数のtypescriptファイル、slsTemplates
ディレクトリにserverless.ymlから読み込むテンプレートを配置します。
例えばIAMロールなど、別のAWSリソースをslsで管理したくなった際も、slsTemplates
下にiam.yml
等のテンプレートファイルを追加することで管理がしやすくなります。
├─serverless.yml
├─src
│ ├─env
│ │ ├─dev.yml
│ │ └─prod.yml
│ ├─functions
│ │ ├─foo.ts
│ │ └─bar.ts
└─slsTemplates
└─lambdaFunctions.yml
このプロジェクトの場合、serverless.ymlとlambdaFunctions.ymlはそれぞれ以下のように定義することになります。
service:
name: test-service
custom:
webpack:
webpackConfig: ./webpack.config.js
includeModules: true
defaultStage: dev
environment:
dev: ${file(./src/env/dev.yml)}
prod: ${file(./src/env/prod.yml)}
dynamoStreamArn:
dev: arn:aws:dynamodb:ap-northeast-1:999999999999:table/User/stream/2020-06-23T07:49:58.352
prod: arn:aws:dynamodb:ap-northeast-1:999999999999:table/User/stream/2020-06-23T07:49:58.352
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage, self:custom.defaultStage}
region: ap-northeast-1
environment: ${self:custom.environment.${self:provider.stage}}
apiName: ${self:service}-${self:provider.stage}
functions:
- ${file(slsTemplates/lambdaFunctions.yml)}
foo:
handler: src/functions/foo.handler
timeout: 30
memorySize: 256
role: arn:aws:iam::999999999999:role/test-role
events:
- stream:
type: dynamodb
arn: ${self:custom.dynamoStreamArn.${self:provider.stage}}
parallelizationFactor: 10
batchSize: 1000
maximumRetryAttempts: 10
bar:
handler: src/functions/bar.handler
timeout: 30
memorySize: 256
role: arn:aws:iam::999999999999:role/test-role
ご覧の通り、lambdaFunctions.ymlにおいて、関数fooのhandlerの指定はsrc/functions/foo.handler
として、serverless.ymlからのパスで指定できています。
また、関数fooでeventsに指定しているDynamoDBのstream arnをserverless.ymlのcustomから参照する部分も、serverless.yml内と同様の指定方法で参照できることが分かります。
なぜこのような指定が可能かというとserverless printをしてみると分かります。
serverless print
は、変数等で指定した部分を解決した状態でコンソール上にserverless構成を出力するコマンドです。
上記プロジェクトでは、出力は以下のようになります。
service:
name: test-service
custom:
webpack:
webpackConfig: ./webpack.config.js
includeModules: true
defaultStage: dev
environment:
dev:
STAGE: dev
APPSYNC_ENDPOINT: https://xxxxxxxxxxxxxxxxxxxxxxxx.appsync-api.ap-northeast-1.amazonaws.com/graphql
prod:
STAGE: prod
APPSYNC_ENDPOINT: https://xxxxxxxxxxxxxxxxxxxxxxxx.appsync-api.ap-northeast-1.amazonaws.com/graphql
dynamoStreamArn:
dev: arn:aws:dynamodb:ap-northeast-1:999999999999:table/User/stream/2020-06-23T07:49:58.352
prod: arn:aws:dynamodb:ap-northeast-1:999999999999:table/User/stream/2020-06-23T07:49:58.352
provider:
name: aws
runtime: nodejs12.x
stage: dev
region: ap-northeast-1
environment:
STAGE: dev
APPSYNC_ENDPOINT: https://xxxxxxxxxxxxxxxxxxxxxxxx.appsync-api.ap-northeast-1.amazonaws.com/graphql
apiName: test-service-dev
functions:
- foo:
handler: src/functions/foo.handler
timeout: 30
memorySize: 256
role: arn:aws:iam::999999999999:role/test-role
events:
- stream:
type: dynamodb
arn: arn:aws:dynamodb:ap-northeast-1:999999999999:table/User/stream/2020-06-23T07:49:58.352
parallelizationFactor: 10
batchSize: 1000
maximumRetryAttempts: 10
bar:
handler: src/functions/bar.handler
timeout: 30
memorySize: 256
role: arn:aws:iam::999999999999:role/test-role
このように、serverless frameworkでは、serverless.ymlから外部のテンプレートファイルを読み込む場合、各テンプレートファイル側でパスや変数の解決をするのではなく、serverless.yml上で一つのテンプレートとして統合したうえで変数やパスの解決を行います。
そのため、変数やパスはserverless.yml上から解決できるものを指定することになります。
最後に
今回はserverless frameworkでテンプレートファイルを分割する際に引っかかりがちなパスの指定について見てきました。
IaCの目的の一つは、インフラの状態を人間がぱっと見で把握しやすくすることであるので、テンプレートファイルの見易さは重要な要素になります。
serverless.ymlは知らず知らずのうちに膨れ上がってしまいがちで、パスを書き換えるのもめんどくさいからディレクトリの整理は後回しでいいか…と思われている方もおられるかと思いますが、パスの書き換えが不要であると分かっていただけたことで、整理へのハードルが一つ下がったんじゃないかな、と思います。