【AWS SAM 入門②】Lambda詳細編

 AWS Serverless Application Model 入門ハンズオンシリーズ - log4ketancho の第2弾です。

 前回の記事では、SAMのセットアップを行いました。

www.ketancho.net

最後に簡単な Lambda Function も作りましたが、本当に最低限のものを構築したに過ぎません。この記事ではより細かい Lambda の設定について見ていきたいと思います。

 今回は、私が使いたかった下記の設定方法についてまとめました。(今後、使いたい機能が増えるたびに更新していこうと思います。)

  • 共通設定の定義
  • スケジュール実行
  • 外部モジュールの利用

共通設定の定義

 前回 Lambda の設定をした際に、 Properties 配下に利用言語(Runtime: python3.6)やタイムアウト時間(Timeout: 30)、を定義しました。Lambda Function が増えてくると、これらの設定を何度も書くのが煩わしくなってくると思います。もちろん、変更したい場合は個別に書けばいいのですが、利用言語などはシステム内で統一することがほとんどだと思います。

 このようなときに、全ての Lambda Function 間で共通の設定を Globals という領域で設定することができます。下の例は、利用言語、タイムアウト時間、メモリサイズ(MemorySize: 256)を共通で設定した例です。

AWSTemplateFormatVersion: '2010-09-09'
Description: Create Lambda function by using AWS SAM.

Globals:
  Function:
    Runtime: python3.6
    Timeout: 15
    MemorySize: 256
    #Environment:
    #  Variables:
    #    TABLE_NAME: data-table

Resources:
  SamSampleLambda:
    Type: AWS::Serverless::Function
    Properties:
      Handler: functions/lambda_function.lambda_handler
      Role: arn:aws:iam::4xxxxxxxxxxx:role/lambda_dynamo

Transform: AWS::Serverless-2016-10-31

この例では、Lambda Function をひとつしか定義していませんが、今後複数定義する場合は記述の量を減らすことができます。コメントアウトしていますが Environment で環境変数を定義することもできます。共通で使う DynamoDB のテーブル名などはここに持たせるのがいいでしょう。

 ちなみに「10あるうち9つの Lambda Function は Timeout を15秒で定義していいのだが、1つだけ30秒にしたい。」ということがあるかもしれません。その場合は、対象の Function に Timeout を設定してください。設定の優先度は、

  • 個々の Function の設定値
  • Globals の設定値
  • AWS のデフォルト値

という優先順位で決まります。そのため、個別に設定した場合は、そちらの設定が優先されます。

スケジュール実行

 次に、スケジュール実行の定義です。Lambda のスケジュール実行は大きくふたつあり、

  • 何分(時間|日)おきに実行する: rate(5 minutes)
  • cron 定義: cron(0 * * * ? *)

を設定することができます。SAM では下記のように定義することになります。

Resources:
  SamSampleLambda:
    Type: AWS::Serverless::Function
    Properties:
      Handler: functions/lambda_function.lambda_handler
      Role: arn:aws:iam::4xxxxxxxxxxx:role/lambda_dynamo
      Events:
        Timer:
          Type: Schedule
          Properties:
            Schedule: rate(5 minutes)
            #Schedule: cron(0 * * * ? *)

外部モジュールの利用

 最後に、Lambda Function の中で外部モジュールを使う場合の SAM での定義を見ていきます。手作業でやる場合は、下記の記事で紹介したように pip install と .zip 化が必要でした。

www.ketancho.net

途中までは、SAM も同じような手順となります。

ローカル環境で function の .zip を作成する

ディレクトリの作成

 まず、Lambda Function ごとに functions ディレクトリの下にディレクトリを作成します。(後述するのですが、ディレクトリ構成をどうするべきか迷っています。・・・★)

$ tree 
.
├── functions
│   └── sample_function
│       └── lambda_function.py
├── packaged-template.yaml
└── template.yaml
モジュールインストール

functions ディレクトリの下に、Lambda Function ごとのディレクトリを切り(ここでは sample_function)、その下に .py ファイルを格納します。

 続いて、 sample_function の中で外部モジュールをインストールします。今回は外部APIを呼ぶ例を流用するため requests モジュールをインストールします。

$ pip install requests -t ./
Zip 化

 同じく .zip にします。sample_function ディレクトリの下で実行します。

$ zip -r lambda_function.zip ./

テンプレートの修正

 ディレクトリ構造が変わったので、テンプレートを少し修正する必要があります。

CodeUri の追加

 これまでのテンプレートでは、Handler で .py ファイルの場所を指定していましたが、今回は Zip 化したので下記のように CodeUri を追加する必要があります。

      CodeUri: 'functions/sample_function/lambda_function.zip'
Handler

 CodeUri を追加したので、Handler 側も修正が必要です。元々は、

      Handler: functions/lambda_function.lambda_handler

という定義でしたが、CodeUri が .zip ファイルを直接指定したので、ディレクトリ指定する必要がなくなります。

      Handler: lambda_function.lambda_handler

のように修正してください。最終的には下記のような定義になります。

Resources:
  SamSampleLambda:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: 'functions/sample_function/lambda_function.zip'  # 追加
      Handler: lambda_function.lambda_handler # 修正
      Role: arn:aws:iam::4xxxxxxxxxxx:role/lambda_dynamo
      Events:
        Timer:
          Type: Schedule
          Properties:
            Schedule: rate(5 minutes)

パッケージ&デプロイしてみる

 コマンドは変わりません。プロジェクトルートで実施してください。

$ aws cloudformation package \
    --template-file template.yaml \
    --s3-bucket ketancho-sam-sample \
    --output-template-file packaged-template.yaml \
    --profile sam-sample
$ aws cloudformation deploy \
    --template-file packaged-template.yaml \
    --stack-name sam-sample-stack \
    --capabilities CAPABILITY_IAM \
    --profile sam-sample

動作確認する

 ここまで定義してきた内容が正しく反映できているかを見ていきます。Function は期待通りに定義できています。

f:id:ketancho_jp:20171230010126p:plain

 共通設定も正しくできているようです。

f:id:ketancho_jp:20171230010146p:plain

 スケジュールもいい感じです。

f:id:ketancho_jp:20171230010300p:plain

 実行結果も完璧です。問題なさそうです。

f:id:ketancho_jp:20171230010328p:plain

 

所感&お困りごと

修正結構大変

 Zip化して、Packageして、Deployして。ここもビルドツール化した方がいいんだろうな。

ディレクトリ構成をどうするか悩む

 ★にも書きましたが、下記の2パターンで悩みました。

案1:Lambda Function を同じディレクトリにおき、外部モジュールも並列に置く
$ tree 
.
├── functions
│   └──  lambda_function1.py
│   └──  lambda_function2.py
│   └──  外部モジュールs
├── packaged-template.yaml
└── template.yaml

メリット:どの Lambda Function でも同じ外部モジュールを使う場合 pip install する手間が省ける デメリット:Zip化したときに他の Lambda Funtcion の .py ファイルや、使っていない外部モジュールもパッケージングされる

案2:Lambda Function ごとにディレクトリ作る
$ tree 
.
├── functions
│   └── (Lambda function1のディレクトリ)
│       └── lambda_function1.py
│       └── 外部モジュールs
│   └── (Lambda function2のディレクトリ)
│       └── lambda_function2.py
│       └── 外部モジュールs
├── packaged-template.yaml
└── template.yaml

メリット:Lambda Function 間で影響が及ばないようになる。 デメリット:何回も pip install しないといけない。

結局、案2にしたのですが、案1だと Zip 化する手間が少なくていいのかなという気がしてきました。CodeUri も同じ .zip を指定できて(Globals に持っていけるのかな?後で調べよう)煩わしくない。SAMの学習が終わったら、アービトBot を SAM 化するので、その中でどちらがいいか検討してみようと思います。

まとめと今後

 何箇所か方式を悩んでいるのですが、Lambda まわりは自由に扱えるようになってきました。

 次は Dynamo DB 連携を見ていきます。

www.ketancho.net

 この記事は、AWS Serveless Application Model 入門シリーズの第2弾です。

www.ketancho.net