yqを利用してSAMのテンプレートファイルを分割する
データアナリティクス事業本部の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
は以下のように記載します。
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にアタッチするロールをそれぞれ記載しています。
# 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)をそれぞれ記載しています。
# 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
の名前を変更するのみにします。
# 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
を作成します。記載内容は以下の通りです。
#!/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のテンプレートファイルを分割してみました。
参考になりましたら幸いです。