10
8

More than 3 years have passed since last update.

SAM+CodeDeployでlambdaへデプロイする。

Posted at

前回まではCodeDeployを用いてEC2インスタンスへのデプロイを学習しました。
今回はデプロイ対象をlambdaでやってみたいと思います。CodeDeployのアプリケーション作成画面でlambdaを選択できるので同じ要領かと思っていましたが、SAMというやつを一緒に使うみたいです。

公式チュートリアルは普通に分かりやすいです。なので、今回はテンプレートyamlファイルの設定項目をここで指定していきましょう。

contents

  • AWS SAM
  • AWS CloudFormation
  • 公式チュートリアル実施時の備忘録/トラブルシューティング
  • template.ymlの解読(本題)

AWS SAM

AWS Serverless Application Model(AWS SAM)は、yaml形式で記述したテンプレートを元にして種々のAWSリソースを用いたアプリケーションを構築することができます。
ローカル上から利用するために、aws-sam-cliを利用します。

AWS CloudFront

  • テンプレート : アプリケーション全体の設計図(yamlベースもしくはjsonベース:コメントが書けるのでyaml推奨)
  • スタック : テンプレートを元にCloudFormationが構築したリソースの集合全体(=環境自体)

CloudFormationで生成する環境は、スタック単位で管理されスタックの破棄とともに紐づいたリソースを全て削除することができる。

公式チュートリアルやってみた

SAM CLIのインストールorアップグレード

公式には以下のようにありますが多分pipでいけると思います。
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html

アップグレード
$ pip install --upgrade awscli
$ pip install --upgrade aws-sam-cli
$ sam --version

awscliを先に最新版にしたほうがいいです。botocoreのバージョンが一致しないと以下のようにエラーします。

エラーメッセージ
ERROR: awscli 1.16.304 has requirement botocore==1.13.40, but you'll have botocore 1.13.44 which is incompatible.
Installing collected packages: botocore, boto3, aws-sam-translator, tomlkit, aws-sam-cli
  Found existing installation: botocore 1.13.40
    Uninstalling botocore-1.13.40:
ERROR: Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: 'RECORD'
Consider using the `--user` option or check the permissions.

デプロイの仕組み

最終的にはディレクトリごとzipにするので、デプロイ用のディレクトリを作り、以下の構成にします。

$ tree                      
SAM-Tutorial
├── afterAllowTraffic.js
├── beforeAllowTraffic.js
├── myFunction.js
└── template.yml
ファイル 説明
myFunction.js デプロイ対象のファイル
template.yml デプロイの流れを書いた仕様書(これが要)
beforeAllowTraffic.js AllowTraffic前に実行するテスト
afterAllowTraffic.js AllowTraffic後に実行するテスト

template.ymlを元にパッケージを作成します。実行すると、SAM-Tutorialディレクトリ内にpackage.ymlが生成し、S3バケットにはc2fe022d92156eeb459d45f02a766921`が上がっています。

$ sam package --template-file template.yml --output-template-file package.yml --s3-bucket YOUR_BUCKET
Uploading to c2fe022d92156eeb459d45f02a766921  4113 / 4113.0  (100.00%)

Successfully packaged artifacts and wrote output template to file package.yml.

続いてデプロイです。--capabilities CAPABILITY_IAMはCloudFrontからロールを作成するのに必要な権限付与みたいな感じ必要です。

sam deploy --template-file package.yml --stack-name my-date-time-app2 --capabilities CAPABILITY_IAM

    Deploying with following values
    ===============================
    Stack name                 : my-date-time-app2
    Region                     : None
    Confirm changeset          : False
    Deployment s3 bucket       : None
    Capabilities               : ["CAPABILITY_IAM"]
    Parameter overrides        : {}
#以下略

当たり前なのですが、cliを実行しているIAMユーザに適切に権限を与えていないと途中でエラーします。SAMは、実質CloudFormationを利用しているので、IAMユーザにはCloudFormationFullAccessを適応してください。(FullAccessでなくてもいいのかもしれないが面倒なので未調査)
なお、今回の検証用のユーザは以下のロール適用しました。
(多分不要なものも含んでます。CodeCommiteとか今回使ってないし)

  • AWSCodeCommitFullAccess
  • AWSLambdaFullAccess
  • IAMFullAccess
  • AmazonS3FullAccess
  • AWSCodeDeployFullAccess
  • AWSCloudFormationFullAccess

また、実行途中で失敗した場合、自動的にロールバックされ、ステータスがROLLBACK_COMPLETEになります。この状態はから再度sam deployはできません。一度CloudFormationコンソールから削除します(cliでもできるはず)。
ステータスの見方についての公式ページ→https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-console-view-stack-data-resources.html

エラーメッセージ
..../a417d920-239f-11ea-9fca-0ef015a9ecfc is in ROLLBACK_COMPLETE state and can not be updated.

デプロイ中はCodeDeployコンソールで進行状況が把握できる。

image.png

デプロイに成功したら、デプロイされたlambdaをコンソールから確認してみましょう。パッケージ名+関数名+英数字となっているのでarnを取得してcliからinvokeしてみる。

$ aws lambda invoke --function arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:my-date-time-app2-myDateTimeFunction-1S9F2G73JFP32 --payload "{\"option\": \"date\", \"period\": \"today\"}" out.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

$ SAM-Tutorial ls
afterAllowTraffic.js    myDateTimeFunction.js   package.yml
beforeAllowTraffic.js   out.txt         template.yml

$ SAM-Tutorial cat out.txt    
{"statusCode":200,"headers":{"Content-type":"application/json"},"body":"{\"month\":12,\"day\":21,\"year\":2019}"}%

この後、ローカルでmyDateTimeFunction.jsファイルを更新した場合、上記と同じ手順でaws packageし、aws deployする。

なお、CodeDeployを通してデプロイしたlambdaでもコンソール上で直接変更はできる。ただこの方法で更新したものはversionとして変更履歴に保存されないので注意。

ここからが本題

template.ymlの解読

template.ymlのオプション指定を解読していきます。

Transform(必須)

使用するSAMのバージョンを指定し、使用できるSAM構文とCloudFormationがそれをどのように処理するかを定義します。AWS::Serverless-2016-10-31を指定する。

Globals

グローバル領域。複数のリソースに対して共通の定義をできる。例えば全ての関数でランタイムはpython3.8でメモリは256を指定したいといった場合、Globalsに書くことで以降のAWS::Serverless::Functionの各Functionの項目に書かなくても継承してくれる。

Grobalsで一括定義できるのは以下のみ。

  • AWS::Serverless::Function
  • AWS::Serverless::Api
  • AWS::Serverless::SimpleTable

Grobalsをデフォルト値のようにしてAWS::Serverless::Function側にも定義した場合その関数だけは指定した値に上書きできるとかあるのだろうか?

なお、それぞれ何を指定できるかは公式参照→https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-specification-template-anatomy-globals.html

Description

その名の通り説明。

Metadata

メタデータ。追加情報です。まだ使い道がわからない。。。

Parameters

テンプレートを実行するときに、ユーザーに入力させるパラメータを定義できます。

Mappings

連想配列/辞書を定義できます。

Conditions

条件を設定します。真の場合にのみリソースを生成するなどの条件分岐が可能です。
パラメータと組み合わせた例が以下。

Parameters: 
  EnvType: 
    Description: Environment type.
    Default: test
    Type: String
    AllowedValues: 
      - prod
      - test
Conditions: 
  CreateProdResources: !Equals [ !Ref EnvType, prod ]
Resources: 
  # CreateProdResourcesがfalseの場合にはインスタンスは作られない。
  EC2Instance: 
    Type: "AWS::EC2::Instance"
    Condition: CreateProdResources
    Properties: 
      ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", AMI]

Output

スタック構築後に、指定した情報を出力する。

Resources(必須)

準備するリソースを指定する。いくつかピックアップして調べてみる。

AutoPublishAlias

チュートリアルのtemplate.yml
# Instructs your myDateTimeFunction is published to an alias named "live".      
      AutoPublishAlias: live

このAutoPublishAliasはデプロイ対象の関数が変更されたときに検出し、テンプレート上で命名したエイリアスを使用してデプロイするようにフレームワークに指示します。この指定は必須です。
やってみるとわかるのですが、sam deployするとlambdaはバージョン管理されます。$LATESTとは別にエイリアスをつけられます。

image.png

エイリアスとは、lambdaのバージョン番号に別名を付したものです。

DeploymentPreference Type

チュートリアルのtemplate.yml
      DeploymentPreference:
# Specifies the deployment configuration      
          Type: Linear10PercentEvery1Minute

ここでは新しいバージョンのlambdaへトラフィックを流す割合を定義しています。
Linear10PercentEvery1Minuteは1minのトラッフィクのうち10%を次バージョンへ回す設定です。

クラスメソッドの実際の実験がわかりやすいです。
https://dev.classmethod.jp/server-side/serverless/understanding-lambda-deploy-with-codedeploy-using-aws-sam/

また、このデプロイのタイプの設定はカナリアリリースなども選択可能であり同じく上記リンクに紹介があります。

DeploymentPreference Hooks

チュートリアルのtemplate.yml
# Specifies Lambda functions for deployment lifecycle hooks
          Hooks:
            PreTraffic: !Ref beforeAllowTraffic
            PostTraffic: !Ref afterAllowTraffic

HooksはCodeDeployでEC2へのデプロイ時の設定にもあった通り、各イベントの前後の適切なタイミングで指定したプログラムを実行するための設定です。lmabdaへのデプロイではtrafic allowの前後2つのタイミングを選択できます。公式には以下のように説明があります。(beforeAllowTrafficafterAllowTrafficはCloudFrontでの指定方法であり、SAMではそれぞれPreTrafficPostTrafficに対応しております。わかりにくい!)

AWS Lambda フックは、ライフサイクルイベントの名前の後の新しい行に文字列で指定された 1 つの Lambda 関数です。各フックはデプロイごとに 1 回実行されます。以下は、AppSpec ファイルに使用できるフックの説明です。

BeforeAllowTraffic – これを使用して、トラフィックがデプロイされた Lambda 関数のバージョンに移行する前にタスクを実行します。

AfterAllowTraffic – これを使用して、トラフィックがデプロイされた Lambda 関数のバージョンに移行した後でタスクを実行します。

用途としてはPreTrafficの段階で単体テストし、成功すればデプロイ、 PostTrafficで結合テストし、失敗すればロールバックさせるなど一連の流れを構築できそうです。

また、このRef!という記法はCloudFrontの組み込み関数で、以下のように読み換えられます。

# パラメータ参照
!Ref {パラメーター名}
# リソースの返り値を取得
!Ref {リソース名}

同じく、 !Subという表現も次に出てきます。
これは文字列とパラメータを組み合わせた値を作成する際に使用します。

参考) https://qiita.com/ryurock/items/766154e0afb8fdb629e2

関数ポリシー

チュートリアルのtemplate.yml
# Grants this function permission to call codedeploy:PutLifecycleEventHookExecutionStatus        
          Statement: 
          - Effect: "Allow"
            Action: 
              - "codedeploy:PutLifecycleEventHookExecutionStatus"
            Resource:
              !Sub 'arn:aws:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:${ServerlessDeploymentApplication}/*'
        - Version: "2012-10-17"

デプロイ前後のテスト用関数から対象の関数をinvokeするにはアクセス許可をする必要があります。(正直lambdaFullアクセスを与えてしまってもいいと思ってます。)

lambda環境変数の指定

チュートリアルのtemplate.yml
      Environment:
        Variables:
          NewVersion: !Ref myDateTimeFunction.Version
10
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
8