- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2019-09-05T12:02:58+00:00","","")
#author("2019-10-28T04:51:33+00:00","","")
#mynavi(AWSメモ)
#setlinebreak(on);
* 目次 [#d56f3755]
#contents
- 関連
-- [[AWS CloudFormationメモ]]
-- [[CloudFormationでカスタムドメイン対応の API Gateway を作成する]]
- 参考
-- https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md
* template.yml [#xcc16348]
#html(<div style="padding-left: 10px;">)
** Lambda単体 [#nd9938d3]
#html(<div style="padding-left: 10px;">)
AWS::Lambda::Function リソースを使用して定義した場合、&color(red){コード変更だけ行った場合に aws cloudformation deploy}; では更新されない(※)ので、素直に AWS::Serverless変換 を使用した方が良さそう。
※「No updates are to be performed」と怒られる。( cloudformation update-stack でも同様 )
※ aws lambda update-function-code であれば多分大丈夫。
参考
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/transform-aws-serverless.html
https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
#mycode2(){{
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Sample Lambda
Resources:
CloudFormationLambda2:
Type: "AWS::Serverless::Function"
Properties:
FunctionName: CloudFormationLambda2
Description: "CloudFormationで作成したLambda2(AWS::Serverless変換)"
Runtime: python3.6
Handler: index.handler
# CodeUri に指定したパス配下がアップロードされる
# ※S3のPATHを指定する事も可能。(自分でS3にアップロードする必要あり)
# ※省略した場合はカレント配下のファイルが全てアップロード対象となる。
CodeUri: ./src
MemorySize: 128
Timeout: 60
Role: !GetAtt CloudFormationLambda2Role.Arn
CloudFormationLambda2Role:
Type: "AWS::IAM::Role"
Properties:
RoleName: CloudFormationLambda2Role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "lambda.amazonaws.com"
Action: "sts:AssumeRole"
Policies:
- PolicyName: "CloudFormationLambda2Policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
}}
#html(</div>)
** Lambda & API Gateway (AWS::Serverless 変換を利用する場合) [#ddf93482]
#html(<div style="padding-left: 10px;">)
AWS::Serverless 変換を使用しない場合と比べるとかなりシンプルに書ける。
#mycode2(){{
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Test serverless application.
Resources:
SampleApi:
Type: AWS::Serverless::Function
Properties:
FunctionName: CloudFormationApiLambda2
Description: "CloudFormationApiLambda2"
Handler: index.handler
Runtime: python3.6
CodeUri: ./src
Events:
ListApi:
Type: Api
Properties:
Path: /
Method: get
GetApi:
Type: Api
Properties:
Path: /{id}
Method: get
Role: !GetAtt CloudFormationApiLambda2Role.Arn
CloudFormationApiLambda2Role:
Type: "AWS::IAM::Role"
Properties:
RoleName: CloudFormationApiLambda2Role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "lambda.amazonaws.com"
Action: "sts:AssumeRole"
Policies:
- PolicyName: "CloudFormationApiLambda2Policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
Outputs:
SampleBookApiUri:
# 作成したAPIのURIをエクスポートしておく(AWS::Serverless::Function を使用した場合、ステージは Stage,Prod の2つが作成される)
# ※AWS::Serverless変換を使用した場合の RestApi は "ServerlessRestApi" という論理名で定義されるので、これを使用してAPIのIDを取得する事が可能。
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
Export:
Name: CloudFormationApiLambda2Uri
}}
※実際の運用ではカスタムドメインを使用してベースパスマッピングを設定する事になるが、ここでは割愛する。(関連: [[CloudFormationでカスタムドメイン対応の API Gateway を作成する]])
#html(</div>)
** Lambda & API Gateway (AWS::Serverless 変換を利用しない場合) [#j5ee9a92]
#html(<div style="padding-left: 10px;">)
上記にも記載したが、AWS::Lambda::Function リソースを使用して定義した場合、&color(red){コード変更だけ行った場合に cloudformation deploy}; では更新されない為、
Lambda自体の定義は AWS::Serverless::Function を使用し、API部分のみ AWS::Serverless変換をしない形で書いてみた。
#mycode2(){{
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Sample API Gateway template.
Parameters:
StageName:
Type: "String"
Default: "dev"
Resources:
# Lambda本体
SampleFunction1:
Type: "AWS::Serverless::Function"
Properties:
FunctionName: SampleFunction1
Runtime: python3.6
Handler: index.handler
CodeUri: ./src
MemorySize: 128
Timeout: 60
Role: !GetAtt SampleFunction1Role.Arn
# Lambdaのロール
SampleFunction1Role:
Type: "AWS::IAM::Role"
Properties:
RoleName: SampleFunction1Role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "lambda.amazonaws.com"
Action: "sts:AssumeRole"
Policies:
- PolicyName: "SampleFunction1Policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
# RestApiの定義
# (AWS::ApiGateway::Resource と AWS::ApiGateway::Method に分けて書くこともできるが、
# ここでは Body プロパティに全て定義した。)
SampleRestApi1:
Type: 'AWS::ApiGateway::RestApi'
Properties:
Body:
swagger: '2.0'
info:
version: '1.0'
title: !Ref 'AWS::StackName'
paths:
/:
get:
# Lambdaプロキシ統合をONにする場合は x-amazon-apigateway-integration オブジェクトを使用して type: aws_proxy を指定する
# (参考) https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-swagger-extensions-integration.html
x-amazon-apigateway-integration:
httpMethod: POST # 統合リクエストで使用されるHTTP メソッド。Lambdaの場合はPOSTを指定
type: aws_proxy # Lambdaプロキシ統合
uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${SampleFunction1.Arn}/invocations"
responses: {}
'/{id}':
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${SampleFunction1.Arn}/invocations"
responses: {}
# API GatewayへのLambda実行許可
SampleRestApi1Permission:
Type: 'AWS::Lambda::Permission'
Properties:
Action: 'lambda:invokeFunction'
Principal: apigateway.amazonaws.com
FunctionName: !Ref SampleFunction1
Principal: "apigateway.amazonaws.com" # SourceArn でURI毎に個別定義する事もできる
#SourceArn: !Sub
# - >-
# arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/{id}
# - __Stage__: Prod
# __ApiId__: !Ref SampleRestApi1
# デプロイ対象ステージの作成
# (無くても通る。不要?)
#SampleRestApi1ProdStage:
# Type: 'AWS::ApiGateway::Stage'
# Properties:
# DeploymentId: !Ref SampleRestApi1Deploy
# RestApiId: !Ref SampleRestApi1
# StageName: !Ref StageName
# ステージへのデプロイ
SampleRestApi1Deploy:
Type: 'AWS::ApiGateway::Deployment'
#DependsOn: "SampleApiMethod"
Properties:
RestApiId: !Ref SampleRestApi1
StageName: !Ref StageName
Outputs:
SampleRestApi1Uri:
Export:
Name: SampleRestApi1Uri
Value: !Sub "https://${SampleRestApi1}.execute-api.${AWS::Region}.amazonaws.com/${StageName}/"
}}
#html(</div>)
#html(</div>)
* デプロイ用シェル [#x58d7da5]
#html(<div style="padding-left: 10px;">)
デプロイ処理をシェルやMakefileにしておくと、開発、運用ともに楽。
build.sh
#mycode2(){{
#!/bin/bash
MODE=$1
echo ""
# テンプレートファイル
TEMPLATE=template.yml
# スタック名
cd `dirname $0`
STACK_NAME=`basename \`pwd\``
if [ -e ".git" ]; then
STACK_NAME=`cat .git/config | grep url | head -1 | awk -F"/" '{print $NF}'`
fi
STACK_NAME=`echo $STACK_NAME | sed 's/_/-/g' | sed 's/Repo$/Stack/'`
echo "Stack: ${STACK_NAME}"
# アカウントIDの取得
ACCOUNT_ID=`aws sts get-caller-identity | grep Account | awk '{print $2}' | sed -e "s/[^0-9]//g"`
# スタック作成時のイベント確認
if [ "${MODE}" == "desc" ]; then
aws cloudformation describe-stack-events --stack-name $STACK_NAME
exit 0
fi
# 削除
if [ "${MODE}" == "del" ]; then
aws cloudformation delete-stack --stack-name $STACK_NAME
aws cloudformation wait stack-delete-complete --stack-name $STACK_NAME
exit 0
fi
# S3バケットがない場合は作る(バケット名は世界で唯一である必要がある為、末尾にアカウントID等を付与しておく)
BUCKET_NAME=my-cloudformation-templates-${ACCOUNT_ID}
BUCKET_COUNT=`aws s3api list-buckets | grep -e "\"${BUCKET_NAME}\"" | wc -l`
if [ "${BUCKET_COUNT}" == "0" ]; then
echo aws s3api create-bucket --create-bucket-configuration '{"LocationConstraint": "ap-northeast-1"}' --bucket $BUCKET_NAME
fi
# ソースコードをzip圧縮しS3にアップロード
# (Serverless変換を使用する場合、package 時に自動でソースのzip圧縮とS3アップロードを行ってくれる為、自分で行なう必要はない)
#cd src && zip ../src.zip * && cd ../
#aws s3api put-object --bucket $BUCKET_NAME --key ${STACK_NAME}.zip --body src.zip
#rm -rf src.zip
# 検証&パッケージング&デプロイ(成功時は作成したAPIのURIを表示する)
aws cloudformation validate-template --template-body file://${TEMPLATE} \
&& aws cloudformation package --template-file $TEMPLATE --s3-bucket $BUCKET_NAME --output-template-file packaged-template.yml \
&& aws cloudformation deploy --template-file packaged-template.yml --stack-name $STACK_NAME --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
&& echo "" \
&& echo "### Exported Value ###" \
&& aws cloudformation describe-stacks --stack-name $STACK_NAME \
| awk 'BEGIN{key=""}{ if ($1 == "\"OutputKey\":") key=$2; if ($1 == "\"OutputValue\":") print key" : "$2 }' \
| sed 's/[",]//g' \
&& echo "######################/" \
&& echo ""
}}
[[CloudFormation実行用のシェル]] を参照
#html(</div>)
* デプロイと動作確認 [#bf71a038]
#html(<div style="padding-left: 10px;">)
#myterm2(){{
# デプロイ
./build.sh
# スタック状況確認
./build.sh desc
./build.sh events
# アンデプロイ
./build.sh del
./build.sh delete
}}
#html(</div>)