背景
- AWS SAM を使って、API Gateway + Lambda のデプロイを自動化しています
- ステークホルダーや各種制約等々の都合上、複数環境に対してそれぞれ異なる構成を実現する必要がありました
- [dev|staging|prd] 環境 × [(無印)|r1|r2] 環境 = 合計9環境…!
- パラメータやマッピング、条件 などを使うことで実現出来そうなことはすぐに分かりましたが、細かい yaml の書き方などで度々詰まったので、具体的な設定例を共有します
実現したこと
- パラメータ、マッピング、条件(と組み込み関数)を併用して、「環境ごとに異なる設定を適用」できるようにしました
- 名前やタグは全環境で異なる値となるようにしたほか、以下の設定は、要件に従い特定環境だけ設定を変えています
環境 | dev | dev-r1 | dev-r2 | staging | staging-r1 | staging-r2 | prd | prd-r1 | prd-r2 |
---|---|---|---|---|---|---|---|---|---|
アクセス許可IPアドレス | 設定A | 設定A | 設定A | 設定B | 設定B | 設定B | 設定C | 設定C | 設定C |
Lambda の段階的デプロイ | AllAtOnce | AllAtOnce | AllAtOnce | Linear | Linear | Linear | Linear | Linear | Linear |
Lambda のプロビジョニング | しない | しない | しない | する | しない | しない | する | しない | しない |
パラメータ・マッピング・条件の設定
-
template.yaml
冒頭で、環境変数である「パラメータ」と環境ごとの設定である「マッピング」、及びそれらを用いた「条件」を定義します
パラメータ ( Parameters )
- [dev|staging|prd] 環境を
EnvProfile
、[(無印)|r1|r2] 環境をSystemType
としました
* 空白は設定できない&分かりにくいので、(無印)環境はgeneral
としました - これらの値はデプロイ時に
samconfig.toml
で明示的に指定していますが、指定漏れに備えてデフォルト値はdev
など安全なものを設定した方が安心です
template.yaml
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: test for qiita
# 環境変数
Parameters:
EnvProfile:
Type: String
AllowedValues:
- dev
- staging
- prd
Default: dev
SystemType:
Type: String
AllowedValues:
- general # 無印
- r1 # r1 環境
- r2 # r2 環境
Default: general
マッピング ( Mappings )
- 続いて、マッピングを定義します。要は HashMap や連想配列なので、キーを環境名とします
-
EnvProfile
である [dev|staging|prd]、SystemType
である [general|r1|r2] をキーとして、各環境固有の設定を列挙していきます - 例えば「Lambda のデバッグモードは dev では有効だけど、staging と prd では無効」など
-
template.yaml
Mappings:
# dev / staging / prod 環境ごとの固有値
EnvProfileMap:
dev:
DeploymentPreferenceType: AllAtOnce
DebugMode: true
WhiteList:
- "111.111.111.111/32"
staging:
DeploymentPreferenceType: Linear10PercentEvery1Minute
DebugMode: false
WhiteList:
- "222.222.222.222/32"
prd:
DeploymentPreferenceType: Linear10PercentEvery1Minute
DebugMode: false
WhiteList:
- "333.333.333.333/32"
# 無印 / r1 / r2 環境ごとの固有値
SystemTypeMap:
general:
ApiGwName: test-apigw-for-qiita
ApiGwAccessLogGroupName: /aws/apigateway/test-apigw-for-qiita/
LambdaName: lambda-handler-for-qiita
VariablePassToLambda: <lambda variable on general>
r1:
ApiGwName: test-apigw-for-qiita-r1
ApiGwAccessLogGroupName: /aws/apigateway/test-apigw-for-qiita-r1/
LambdaName: lambda-handler-for-qiita-r1
VariablePassToLambda: <lambda variable on r1>
r2:
ApiGwName: test-apigw-for-qiita-r2
ApiGwAccessLogGroupName: /aws/apigateway/test-apigw-for-qiita-r2/
LambdaName: lambda-handler-for-qiita-r2
VariablePassToLambda: <lambda variable on r1>
条件 ( Conditions )
- ここでは以下のような条件を定義しています
- 複数箇所で使われる判定条件
- 「dev環境かどうか」、「無印環境かどうか」
- ちょっと複雑な判定が必要な条件
- 「Lambda のプロビジョニング環境かどうか」=ここでは「staging 無印環境か prd 無印環境か」
- 複数箇所で使われる判定条件
- 後続のリソース設定でも書けますが、ここで定義しておいた方が可読性が良く&修正しやすくなります
template.yaml
Conditions:
IsDev : !Equals [!Ref EnvProfile, "dev"]
IsGeneral : !Equals [!Ref SystemType, "general"]
# Lambda のプロビジョニング環境:staging 無印 と prd 無印のみ
IsLambdaProvisioningEnv : !And [ !Not [Condition: IsDev], Condition: IsGeneral ]
リソース設定
- API Gateway や Lambda といった AWS リソース設定の内、環境固有の設定を適用している部分を抜粋してご紹介します
API Gateway のアクセスログ用ロググループ
- ロググループ名を
SystemTypeMap
からSystemType
をキーとして取得します
template.yaml
Resources:
(略)
ApiGwAccessLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !FindInMap [ SystemTypeMap, !Ref SystemType, ApiGwAccessLogGroupName ]
RetentionInDays: 180
API Gateway
- 名前を
SystemTypeMap
からSystemType
をキーとして取得します - タグは環境名を付与した形で設定します
- 例えば dev-r1 環境だと
dev-test-apigw-for-qiita-r1
と設定され、判別しやすくなります
- 例えば dev-r1 環境だと
- リソースポリシーによる IP アドレス許可リストを
EnvProfileMap
からEnvProfile
をキーとして取得します-
aws:SourceIp:
を忘れずに
-
template.yaml
TestApiGatewayForQiita:
Type: AWS::Serverless::Api
(略)
Properties:
Name: !FindInMap [ SystemTypeMap, !Ref SystemType, ApiGwName ]
StageName: stage # ステージ名は全環境固定
TracingEnabled: true
OpenApiVersion: 3.0.2
Tags:
Name: !Sub ${EnvProfile}-test-apigw-for-qiita-${SystemType}
Env: !Ref EnvProfile
(略)
# 公開 API の仕様
DefinitionBody:
(略)
x-amazon-apigateway-policy:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: "*"
Action: execute-api:Invoke
Resource:
- "execute-api:/*"
Condition:
IpAddress:
aws:SourceIp: !FindInMap [ EnvProfileMap, !Ref EnvProfile, WhiteList ]
Lambda
- 関数名を
SystemTypeMap
からSystemType
をキーとして取得します - Lambda の段階的なデプロイ設定
DeploymentPreference
をEnvProfileMap
から取得します - Lambda のプロビジョニング設定は、条件
IsLambdaProvisioningEnv
を満たす(環境)ならプロビジョニングを実施するよう設定します- 条件を満たさない場合、
!Ref "AWS::NoValue"
を設定して本プロパティを削除し、プロビジョニングしないようにする必要があります - 空白や他の表記だとうまく動かなかったので要注意です
- 条件を満たさない場合、
- Lambda に渡す環境変数として
SystemType
、VariablePassToLambda
、DebugMode
を設定します - こちらもタグは環境名を付与した形で設定します
- 例えば dev-r1 環境だと
dev-lambda-handler-for-qiita-r1
と設定されます
- 例えば dev-r1 環境だと
template.yaml
LambdaHandlerForQiita:
Type: AWS::Serverless::Function
Dependson: LambdaHandlerForQiitaLogGroup
Properties:
FunctionName: !FindInMap [ SystemTypeMap, !Ref SystemType, LambdaName ]
(略)
DeploymentPreference:
Enabled: true
Type: !FindInMap [ EnvProfileMap, !Ref EnvProfile, DeploymentPreferenceType ]
ProvisionedConcurrencyConfig:
!If
- IsLambdaProvisioningEnv
- ProvisionedConcurrentExecutions: 100
- !Ref "AWS::NoValue" # 対象環境以外はプロビジョニングしない
(略)
Environment:
Variables:
SYSTEM_TYPE: !Sub ${SystemType}
VARIABLE_PASS_TO_LAMBDA: !FindInMap [ SystemTypeMap, !Ref SystemType, VariablePassToLambda ]
DEBUG_MODE: !FindInMap [ EnvProfileMap, !Ref EnvProfile, DebugMode]
Tags:
Name: !Sub ${EnvProfile}-lambda-handler-for-qiita-${SystemType}
Env: !Ref EnvProfile