データアナリティクス事業本部のueharaです。
今回は、yqを利用してSAMのテンプレートファイルを分割してみたいと思います。
はじめに
皆さんはAWS Serverless Application Model (AWS SAM) をお使いでしょうか。
SAMのテンプレートファイルはCloudFormationベースで記載することができるので、個人的には比較的とっつきやすい印象を持っています。
しかし、SAMはそのテンプレートファイルであるtemplate.yaml
を別のyamlファイルに分割したい場合、必然的に別スタックとして作成し、スタックをネストさせる必要があります。
したがって、1つのスタックでデプロイしたい場合はどうしてもtemplate.yaml
が長くなりがちです。
長くなる1つのテンプレートファイルには、以下の問題が発生します。
- 可読性の低下・改修工数の増加
- 共同開発における競合解消の複雑化
今回はファイルを分割しつつ1つのスタックでデプロイすることを目標に、yqを利用して、何とかtemplate.yaml
を分割してみたいと思います。
前提
今回デプロイする構成
今回は以前私が以下のブログで紹介したLambda+Glue+Step Functionsの構成を2つ分デプロイしたいと思います。
yqのインストール
今回は上述の通りyqを利用します。
yqはHomebrewを利用して簡単にインストールすることができますので、インストールがまだの方は以下を実施して下さい。
$ brew install yq
なお、バージョンは4を利用します。
$ brew info yq
==> yq: stable 4.43.1 (bottled), HEAD
ファイル準備
フォルダ構成
今回のフォルダ構成は以下の通りです。
.
├── deploy.sh
├── glue_scripts
│ └── test_glue.py
├── handler
│ └── test_func.py
├── resources
│ ├── etl_job_1.yaml
│ ├── etl_job_2.yaml
│ └── iam_role.yaml
├── samconfig.toml
└── template.yaml
上記の内、glue_scripts
配下のtest_glue.py
ファイルと、handler
配下のtest_func.py
、およびsamconfig.toml
ファイルは先に紹介したこちらのブログと同じものになるので、今回は記載を割愛します。
template.yamlの作成
今回、SAMのテンプレートファイルとなるtemplate.yaml
は以下のように記載します。
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: "Test SAM Application"
Globals:
Function:
Timeout: 180 # 180 seconds
MemorySize: 128
Resources:
- $file: resources/iam_role.yaml
- $file: resources/etl_job_1.yaml
- $file: resources/etl_job_2.yaml
外部ファイルを読み込む際のキーは$file
としました。
上記の通りResources
セクションの記述を3つのファイルに分割しており、具体的にはIAM Roleと、ETLジョブ(Lambda+Glue+Step Functions)をそれぞれ別のyamlで記載します。
こうすることで、例えばETLジョブを複数人で並行して開発する場合でも、開発者はそれぞれのyamlにリソースを追記し、template.yaml
の編集を最小限に抑えることができます。
iam_role.yamlの作成
iam_role.yaml
は以下の通りです。Lambda, Glue, Step Functionsにアタッチするロールをそれぞれ記載しています。
iam_role.yaml
# Lambda Role
MyLambdaFunctionRole:
Type: AWS::IAM::Role
Properties:
RoleName: "uehara-sam-test-lambda-role"
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
# Glue Job Role
MyGlueJobRole:
Type: AWS::IAM::Role
Properties:
RoleName: "uehara-sam-test-glueJob-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: glue.amazonaws.com
Action: "sts:AssumeRole"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole
- arn:aws:iam::aws:policy/AmazonS3FullAccess
# Step Functions Role
MyStepFunctionsRole:
Type: AWS::IAM::Role
Properties:
RoleName: "uehara-sam-test-sf-role"
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- states.ap-northeast-1.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaRole
- arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole
記載方法は上記の通り、本来Resources
セクションに記載する内容をそのままyamlファイルに記載しています。
etl_job_1.yamlとetl_job_2.yamlの作成
etl_job_1.yaml
は以下の通りです。Lambda, Glue, Step Functions(+それを起動するEventBridge)をそれぞれ記載しています。
etl_job_1.yaml
# Lambda
MyLambdaFunction1:
Type: AWS::Serverless::Function
Properties:
FunctionName: "uehara-sam-test-lambda-1"
Role: !GetAtt MyLambdaFunctionRole.Arn
CodeUri: handler/
Handler: test_func.lambda_handler
Runtime: python3.9
Architectures:
- x86_64
# Glue Job
MyGlueJob1:
Type: AWS::Glue::Job
Properties:
Role: !GetAtt MyGlueJobRole.Arn
GlueVersion: '3.0'
Name: "uehara-sam-test-glueJob-1"
DefaultArguments:
"library-set": "analytics"
Command:
Name: pythonshell
ScriptLocation: "s3://cm-da-uehara/glue-scripts/test_script.py"
PythonVersion: "3.9"
ExecutionProperty:
MaxConcurrentRuns: 3
MaxCapacity: 0.0625
MaxRetries: 0
# Step Functions
MyStepFunctions1:
Type: AWS::Serverless::StateMachine
Properties:
Name: "uehara-sam-test-sf-1"
Definition:
Comment: "Test Step Functions"
StartAt: InvokeLambda
States:
InvokeLambda:
Type: Task
Resource: !GetAtt MyLambdaFunction1.Arn
Next: InvokeGlueJob
InvokeGlueJob:
Type: Task
Resource: "arn:aws:states:::glue:startJobRun.sync"
Parameters:
JobName: !Ref MyGlueJob1
End: true
Role: !GetAtt MyStepFunctionsRole.Arn
Events:
S3Event:
Type: EventBridgeRule
Properties:
RuleName: "uehara-sam-test-sf-event-1"
Pattern:
source:
- aws.s3
detail-type:
- "Object Created"
detail:
bucket:
name:
- "cm-da-uehara"
object:
key:
- prefix: "tmp/"
次にetl_job_2.yaml
です。
あくまで検証用なので、今回はetl_job_1.yaml
の名前を変更するのみにします。
etl_job_2.yaml
# Lambda
MyLambdaFunction2:
Type: AWS::Serverless::Function
Properties:
FunctionName: "uehara-sam-test-lambda-2"
Role: !GetAtt MyLambdaFunctionRole.Arn
CodeUri: handler/
Handler: test_func.lambda_handler
Runtime: python3.9
Architectures:
- x86_64
# Glue Job
MyGlueJob2:
Type: AWS::Glue::Job
Properties:
Role: !GetAtt MyGlueJobRole.Arn
GlueVersion: '3.0'
Name: "uehara-sam-test-glueJob-2"
DefaultArguments:
"library-set": "analytics"
Command:
Name: pythonshell
ScriptLocation: "s3://cm-da-uehara/glue-scripts/test_script.py"
PythonVersion: "3.9"
ExecutionProperty:
MaxConcurrentRuns: 3
MaxCapacity: 0.0625
MaxRetries: 0
# Step Functions
MyStepFunctions2:
Type: AWS::Serverless::StateMachine
Properties:
Name: "uehara-sam-test-sf-2"
Definition:
Comment: "Test Step Functions"
StartAt: InvokeLambda
States:
InvokeLambda:
Type: Task
Resource: !GetAtt MyLambdaFunction2.Arn
Next: InvokeGlueJob
InvokeGlueJob:
Type: Task
Resource: "arn:aws:states:::glue:startJobRun.sync"
Parameters:
JobName: !Ref MyGlueJob2
End: true
Role: !GetAtt MyStepFunctionsRole.Arn
Events:
S3Event:
Type: EventBridgeRule
Properties:
RuleName: "uehara-sam-test-sf-event-2"
Pattern:
source:
- aws.s3
detail-type:
- "Object Created"
detail:
bucket:
name:
- "cm-da-uehara"
object:
key:
- prefix: "tmp/"
deploy.shの作成
今回一番重要なdeploy.sh
を作成します。記載内容は以下の通りです。
deploy.sh
#!/bin/bash
set -eu
TMP_FILE='temp.yaml'
yq '(.. | select(has("$file"))) |= load(.$file) | .Resources = (.Resources[] as $item ireduce ({}; . * $item))' template.yaml > ${TMP_FILE}
sam deploy --template ${TMP_FILE}
rm ${TMP_FILE}
やっていることとしては、yqのloadを使用して別のyamlファイルからデータを読み込み、ただこの状態ではResources
が配列になっており本来のtemplate.yaml
のフォーマットになってないのでireduceしたテンプレートファイルでデプロイを行うという形になっています。
実行してみる
実行は以下で可能です。
$ bash deploy.sh
変更スタックを確認してみると、別々のyamlファイルに記載したリソースがきちんと読み込まれていることが伺えます。
そのまま進めばAWS環境にリソースがデプロイされます。
最後に
今回は、yqを利用してSAMのテンプレートファイルを分割してみました。
参考になりましたら幸いです。