[やってみた] Lambda@Edge のWorkshopでやったことをSAM一撃化してみた。 #NET307 #reinvent 2019

2019.12.20

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

re:Inventの「Lambda@Edgeによるコンテンツ配信のカスタマイズ #NET307」のワークショップを、勢いあまってSAM一撃化したので、共有します。

Lambdaを使うので、CloudFormationではなく、SAM(Serverless Application Model)化してます。 SAM化してるので残念ながら、マウスからポチっと一撃にはなってませんが コマンドラインからsam deploy一撃したあとはのんびりと見てれば以下が実施されます。

  1. IAM, S3, DynamoDBの作成
  2. S3へ必要なリソースのコピー(CloudFormationカスタムリソース)
  3. DynamoDBへ必要なリソースのコピー(CloudFormationカスタムリソース)
  4. Lambda@Edge関数(6つの作成)
  5. Lambda@EdgeのPublish(Version, Alias)
  6. CloudFrontの作成
  7. CloudFrontへ各種Behaviorを設定
  8. Output CloudFrontのホスト名(https://XXXXXXX.cloudfront.net)

ということで、最後のCloudFront作成と展開が10分程度かかるんですが、ワークショップの最終形態が1撃でできあがります。

メイキングに入る前に、各ソースは公式のAWS githbuのリポジトリをクローンして公開してますので、先にsam deployから行きます。

Workshopの概要

「Lambda@Edgeによるコンテンツ配信のカスタマイズ #NET307」のワークショップを参照して頂きたいのですが

https://github.com/aws-samples/aws-lambda-edge-workshops/blob/master/Workshop1/README.md

In this workshop you will learn how you can use Lambda@Edge to extend functionality of your web-application or a website.

Lambda@Edgeを使用したWebサイトに対して、Lamba@Edegeを用いたアプリケーションの拡張(セキュリティの強化、apiの実装等を行います)
ワークショップでは、AWSコンソールから1つ1つ、変更を加えていくのですが、今回は機能拡張した後の状態に一撃でもっていきます。

SAM Deploy

0. SAMの準備

詳細は割愛しますが、SAMは必須ですのでSAM CLIのインストールを実施してください。

参考 https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html

Linux

brew tap aws/tap
brew install aws-sam-cli
sam --version
SAM CLI, version 0.38.0

1. クローンとデプロイ

clone

SAMをまるっと公開しているのでリポジトリをクローンしてください

git clone https://github.com/cm-kajiwara-taishi/aws-lambda-edge-workshops.git
cd aws-lambda-edge-workshops/Workshop1/SAM

SAM deploy 初回

--guidedオプションを付けて、samconf.tomlを作成します (S3バケットの指定を更新します。S3バケットの指定、CAPABILITY_NAMED_IAMを指定すれば一撃デプロイできますがコマンドがながくなるので) Lamda関数はlambda@Edgeで使用しますので、リージョンはus-east-1指定してください

CAPABILITY_NAMED_IAM が必要な為、CAPABILITY_IAMでは初回のデプロイは失敗します

sam deploy -t sam-template.yml --guided

Configuring SAM deploy
======================

        Looking for samconfig.toml :  Found
        Reading default arguments  :  Success

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [WsLambdaAtEdgeAlienCardsSAM]:
        AWS Region [us-east-1]: 
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [y/N]:
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]:
        Save arguments to samconfig.toml [Y/n]: 

        Looking for resources needed for deployment: Found!

                Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-xxxxxxx
                A different default S3 bucket can be set in samconfig.toml

        Saved arguments to config file
        Running 'sam deploy' for future deployments will use the parameters saved above.
        The above parameters can be changed by modifying samconfig.toml
        Learn more about samconfig.toml syntax at
        https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

        Deploying with following values
        ===============================
        Stack name                 : WsLambdaAtEdgeAlienCardsSAM
        Region                     : us-east-1
        Confirm changeset          : False
        Deployment s3 bucket       : aws-sam-cli-managed-default-samclisourcebucket-xxxxxxxxxxx
        Capabilities               : ["CAPABILITY_IAM"]
        Parameter overrides        : {}

Initiating deployment
=====================

Waiting for changeset to be created..
Error: Failed to create changeset for the stack: WsLambdaAtEdgeAlienCardsSAM, ex: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state Status: FAILED. Reason: Requires capabilities : [CAPABILITY_NAMED_IAM]

SAM deploy 2回移行

sam deploy -t sam-template.yml --capabilities CAPABILITY_NAMED_IAM

もしくはsamconfig.tomlのcapabilitiesを変更し

capabilities = "CAPABILITY_NAMED_IAM"  // CAPABILITY_IAM から書き換え
sam deploy -t sam-template.yml

で、2回目以降は、JavaScriptを更新すれば、sam deploy -t sam-template.ymlで行えます。

deploy 実行中

        Deploying with following values
        ===============================
        Stack name                 : WsLambdaAtEdgeAlienCardsSAM
        Region                     : us-east-1
        Confirm changeset          : False
        Deployment s3 bucket       : aws-sam-cli-managed-default-samclisourcebucket-xxxxxxxxxxx
        Capabilities               : ["CAPABILITY_NAMED_IAM"]
        Parameter overrides        : {}

Initiating deployment
=====================

Waiting for changeset to be created..

CloudFormation stack changeset
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------   
Operation                                       LogicalResourceId                               ResourceType                                     
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add   AlienCardsCloudFrontDistribution                 AWS::CloudFront::Distribution
+ Add   AlienCardsCloudFrontOAI                       AWS::CloudFront::CloudFrontOriginAccessIdentity   
+ Add   AlienCardsDynamoDBTable                       AWS::DynamoDB::Table                           
+ Add   AlienCardsS3BucketPolicy                         AWS::S3::BucketPolicy
+ Add   AlienCardsS3Bucket                             AWS::S3::Bucket                                
+ Add   BootstrapCustomResource                       Custom::BootstrapFunction                     
+ Add   BootstrapFunction                               AWS::Lambda::Function                           
+ Add   IamLambdaExecutionRoleBasic                   AWS::IAM::Role
+ Add   IamLambdaExecutionRoleFullAccess                 AWS::IAM::Role
+ Add   IamLambdaExecutionRoleReadOnly                 AWS::IAM::Role                                  
+ Add   LambdaEdgeAddSecurityHeadersAliaslive           AWS::Lambda::Alias                             
+ Add   LambdaEdgeAddSecurityHeadersVersionba8ccad248   AWS::Lambda::Version
+ Add   LambdaEdgeAddSecurityHeaders                     AWS::Lambda::Function                          
+ Add   LambdaEdgeApiLikeAliaslive                     AWS::Lambda::Alias                              
+ Add   LambdaEdgeApiLikeVersione3f0e31a43             AWS::Lambda::Version                          
+ Add   LambdaEdgeApiLike                               AWS::Lambda::Function                           
+ Add   LambdaEdgeCustomizeCssAliaslive               AWS::Lambda::Alias                               
+ Add   LambdaEdgeCustomizeCssVersion0f1abd1872       AWS::Lambda::Version                           
+ Add   LambdaEdgeCustomizeCss                         AWS::Lambda::Function                            
+ Add   LambdaEdgeGenerateCardPageAliaslive           AWS::Lambda::Alias
+ Add   LambdaEdgeGenerateCardPageVersion1140f6810d   AWS::Lambda::Version                           
+ Add   LambdaEdgeGenerateCardPage                     AWS::Lambda::Function                            
+ Add   LambdaEdgeGenerateHomePageAliaslive           AWS::Lambda::Alias                               
+ Add   LambdaEdgeGenerateHomePageVersione27d7b8356   AWS::Lambda::Version
+ Add   LambdaEdgeGenerateHomePage                     AWS::Lambda::Function
+ Add   LambdaEdgeRedirectAliaslive                   AWS::Lambda::Alias
+ Add   LambdaEdgeRedirectVersion99ca93e354           AWS::Lambda::Version                           
+ Add   LambdaEdgeRedirect                             AWS::Lambda::Function                            
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------   

Changeset created successfully. arn:aws:cloudformation:us-east-1:xxxxxxxxxx:changeSet/samcli-deploy1576813459/317ce883-2970-4a65-8c8a-xxxxxxxxxxx


2019-12-20 12:44:27 - Waiting for stack create/update to complete

CloudFormation events from changeset
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------      
ResourceStatus          ResourceType              LogicalResourceId      ResourceStatusReason
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------      
CREATE_IN_PROGRESS      AWS::IAM::Role          IamLambdaExecutionRoleReadOnly                                    Resource creation Initiated
CREATE_IN_PROGRESS      AWS::IAM::Role          IamLambdaExecutionRoleBasic                                       Resource creation Initiated   
CREATE_IN_PROGRESS      AWS::IAM::Role          IamLambdaExecutionRoleFullAccess                                  Resource creation Initiated   
CREATE_IN_PROGRESS      AWS::DynamoDB::Table      AlienCardsDynamoDBTable    Resource creation Initiated    
CREATE_IN_PROGRESS      AWS::IAM::Role          IamLambdaExecutionRoleReadOnly                                    -                         
CREATE_IN_PROGRESS      AWS::IAM::Role          IamLambdaExecutionRoleFullAccess                                  -                         
CREATE_IN_PROGRESS      AWS::CloudFront::CloudFrontOriginAccessIdentity                   AlienCardsCloudFrontOAI    -                          
CREATE_IN_PROGRESS      AWS::IAM::Role          IamLambdaExecutionRoleBasic                                       -                         
CREATE_IN_PROGRESS      AWS::DynamoDB::Table      AlienCardsDynamoDBTable    -                          
CREATE_IN_PROGRESS      AWS::S3::Bucket        AlienCardsS3Bucket       -
CREATE_IN_PROGRESS      AWS::S3::Bucket        AlienCardsS3Bucket       Resource creation Initiated

後はCloudFrontの展開まで少々時間がかかるので、作成まで待ちます。

Stack WsLambdaAtEdgeAlienCardsSAM outputs:
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
OutputKey-Description                              OutputValue                                          
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    
AlienCardsCloudFrontDistributionDomainName -        https://xxxxxxxxx.cloudfront.net
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - WsLambdaAtEdgeAlienCardsSAM in us-east-1

確認

出力されたURL(https://xxxxxxxxx.cloudfront.net)にアクセスしてみてください。下記のようなワークショップで実施したLambda@Edgeでカスタマイズしたサイトができあがっていれば成功です

下記のサイトができあがります。期間限定公開(1週間程度で消します)猫好きなひとは、猫に+1してください https://d36ezg14dehmvr.cloudfront.net/

当然ながらobservatory.mozilla.org(セキュリティ評価サイト)でもA+をとれます。

やったこと

  1. re:Inventで約2時間のワークショップに参加します。内容を理解します
  2. AWSの公式リポジトリをクローンします
  3. ワークショップ同様にCloudFormationにLambda関数(Lambda@Edge用)5つをテンプレートに追加します
  4. SAM化します
  5. CloudFrontのビヘイビアに設定します

つまったところ

  • SAM可した際に、BootstrapのLambdaもSAM可された
    • cfn-response.jsが無くエラーが発生(CloudFormationでzipでコード化する時のみに追加される)
    • 1関数のみ除外する方法が不明だったためSAM化した
  • Lambda@Edge関数に環境変数が使えなかった
    • 変数(DynamoDBのテーブル名)を固定化した
  • Lambdaのバージョンをビヘイビアで指定するがソース更新に追従しない
    • AutoPublishAlias: live で対応し、バージョンを作り直すようにした
  • CloudFrontの作成時にLambda関数が存在しない旨のエラーが発生する
    • DependsOnにLambdaVersionを追加しようとするがSAMがランダムなリソース名が生成されるため指定できない
    • DependsOnにLambdaEdgeXXXXXXAliaslive(6つ)を追加

まとめ

Lambda関数のアップデートをした際に、sam deployだけで、反映されるのでかなり楽になりました。 かなり楽にはなったんですが、そこも面倒になってきて、pushするだけでならないかなと思い始めたので 次は機会があれば、パイプライン可(CI/CD可)をしてみようと思います。

Cleanup

リソースの削除は、CloudFormationのスタックを消すだけなのですが、Lambda@Edgeで使用されている関数は削除されるまで1時間程度かかりますので、1度削除してエラーが発生した場合は、時間をあけて、再度削除をお願いします。

aws cloudformation delete-stack --stack-name WsLambdaAtEdgeAlienCards --region us-east-1

エラー理由詳細 https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-delete-replicas.html

SAM可したtemplate

参考情報

AWS公式リポジトリ https://github.com/aws-samples/aws-lambda-edge-workshops