AWS Serverless Application Model 入門 - log4ketancho の第2弾です。
前回の記事では、SAMのセットアップを行いました。
最後に簡単な 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 化が必要でした。
途中までは、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 は期待通りに定義できています。
共通設定も正しくできているようです。
スケジュールもいい感じです。
実行結果も完璧です。問題なさそうです。
所感&お困りごと
修正結構大変
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 連携を見ていきます。
この記事は、AWS Serveless Application Model 入門シリーズの第2弾です。