はじめに
やること
Pythonのプログラムを記述したLambda関数を、CloudFormationでデプロイします。
CloudFormationのコードはYAMLで記述し、コード中の各プロパティについて、Lambda関数のどの設定値に紐付くか記載します。
また、YAMLコードの中にPythonコードをベタ書きするパターンと、PythonコードをS3にzipファイルで配置しておき、そこから参照するパターンを記載します。
対象の読者
- CloudFormationをとりあえず使ってみたい。
- CloudFormationでLambda関数をデプロイしたい。
- Lambda関数のハンドラーの意味を知りたい。(本記事で少し詳しく解説しています)
Lambda関数
設定値
今回スタックから作成するLambda関数の設定値は以下のようになっています。
項目 | 設定値 |
---|---|
関数名 | デプロイ時に入力する。 |
Lambdaハンドラー | index.lambda_handler |
Lambda関数の説明 | Function that put items to DynamoDB |
ランタイム | Python 3.9 |
メモリ | 128 MB |
タイムアウト | 5秒 |
アーキテクチャ | x86_64 |
エフェメラルストレージ | 512 MB |
ロール | CloudWatch Logsに書き込む権限を付与。 DynamoDBへのすべての操作を許可。 |
CloudFormationテンプレート①(Pythonコードベタ書き)
YAMLコード
スタック作成に使うYAMLで記述したテンプレートは以下で、「Lambda.yml」という名前で保存しました。
---
AWSTemplateFormatVersion: 2010-09-09
Description: "IaC for Lambda"
Parameters:
NameOfFunction:
Type: String
Description: "Name of Lambda Function"
Resources:
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Ref NameOfFunction
Description: "Function that put items to DynamoDB"
Runtime: "python3.9"
MemorySize: "128"
Timeout: "5"
Role: "arn:aws:iam::XXXXXXXXXXXX:role/service-role/putDynamoFunc-role-l6trp0uq"
Handler: index.lambda_handler
Architectures: [ "x86_64" ]
EphemeralStorage:
Size: "512"
Code:
ZipFile: |
import json
import boto3
def lambda_handler(event, context):
# Lambdaのテストから受け取ったデータを取り出して変数に格納
email = event['email']
name = event['name']
gender = event['gender']
inquiry = event['inquiry']
# DynamoDBに投入するためのアイテムをセット。JSONを作成して変数に格納
DynamoItems = {
'Email': email,
'Name': name,
'Gender': gender,
'Inquiry': inquiry
}
# DynamoDBにデータを投入
table = boto3.resource('dynamodb').Table('inquiry-table')
response = table.put_item(
Item = DynamoItems
)
return {
'statusCode': 200,
'body': json.dumps(response)
}
コード解説
コードのプロパティを、上から解説します。
AWSTemplateFormatVersion
形式バージョンになり、固定値になります。「2010-09-09」を指定します。
Description
CloudFormationのスタックの説明になります。CloudFormationダッシュボードのスタックの説明欄に記載されます。
Parameters
CloudFormationでスタックを作成するときに、ユーザが手動で指定するパラメータを定義します。
ここでは、「Lambda関数の名前」をスタック作成時に指定するようにしています。ユーザーが入力した文字列(Lambda関数名)は、ResourcesプロパティのFunctionNameで参照しています。(後述)
Resources
Type
Lambda関数をデプロイするため、「AWS::Lambda::Function」を指定しています。
FunctionName
Lambda関数の関数名です。ユーザーがParametersプロパティで入力した文字列を「!Ref」で参照して受け取っています。
Runtime
Lambda関数のランタイムを指定します。ここでは「Python 3.9」です。
MemorySize
Lambda関数のメモリです。ここでは128MBにしています。
Timeout
Lambda関数のタイムアウトです。ここでは5秒です。
Role
Lambda関数にアタッチするIAMロールです。ARNで指定しています。IAMロールの内容は本記事に書いていませんが、ざっくり言うとCloudWatch Logsへの書き込み権限とDynamoDBへの全権限を付与しています。
Handler
Lambda関数のハンドラーを指定します。
Lambda関数のハンドラーとは、Lambda関数が呼び出されたときに、どのファイルのどの関数を呼び出すか定義したものとなります。
例えば、以下のようにハンドラーが「lambda_function.lambda_handler」と設定されていたときは、「lambda_function.py」の中の「lambda_handler」関数が呼び出されることになります。
Lambda関数のCloudFormationでデプロイすると、Pythonファイル(上でいう「lambda_function.py」)は「index.py」となるため(後述)、コード内のHandlerには「index.lambda_handler」としています。
Architectures
Lambda関数のアーキテクチャを記述します。「arm64」か「x86_64」を記述します。ここでは「x86_64」を指定しています。
EphemeralStorage
Lambda関数の「/tmp」のサイズです。ここでは512MBを指定してみました。
Code
Lambda関数にデプロイするパッケージを記述します。選択肢は以下のいずれかです。
- ImageUri(ECRに保存したコンテナイメージを指定する)
- S3Bucket(S3バケットに保存したパッケージを指定する)
- S3Key(パッケージのS3のキーを指定する)
- S3ObjectVersion(パッケージのバージョンを指定。パッケージがバージョン管理されている場合に使用)
- ZipFile(ソースコードをベタ書きする場合。PythonかNode.js。「index」というファイル名でデプロイする)
ここでは「ZipFile」を記述しています。
ですので、YAMLのコード上にPythonコードをベタ書きしています。そして、index.pyというファイル名でLambda関数にデプロイされるため、Lambdaハンドラーは「index.lambda_handler」となっています(繰り返しですが)。
デプロイ
デプロイします。
スタック名は「Stack-Lambda」で、Lambda関数名は「testFunc」としています。
成功しました。
テスト
テストしてみます。
デプロイしたLambda関数を動かすと、別の記事で作成したDynamoDBにアイテムがプットされます。
Googleフォームからデータを送信してDynamoDBに登録する
デプロイしたLambda関数から以下のようなJSONを渡してテストします。
200ステータスが返ってきました。DynamoDBのテーブルを見てみましょう。
テストで指定したJSONにある値がDynamoDBテーブルにPUTされています。
テストも成功です。
CloudFormationテンプレート②(PythonコードをS3に配置)
今度はPythonコードをCloudFormationテンプレートにベタ書きではなく、S3にzipファイルで配置します。
CloudFormationでデプロイするときに、S3のzipファイルから読み込みます。
Pythonコードをzip化
手順は下記サイトに書いてあります。
.zip ファイルアーカイブで Python Lambda 関数をデプロイする
「index.py」のコードは以下になります。
import json
import boto3
def lambda_handler(event, context):
# Lambdaのテストから受け取ったデータを取り出して変数に格納
email = event['email']
name = event['name']
gender = event['gender']
inquiry = event['inquiry']
# DynamoDBに投入するためのアイテムをセット。JSONを作成して変数に格納
DynamoItems = {
'Email': email,
'Name': name,
'Gender': gender,
'Inquiry': inquiry
}
# DynamoDBにデータを投入
table = boto3.resource('dynamodb').Table('inquiry-table')
response = table.put_item(
Item = DynamoItems
)
return {
'statusCode': 200,
'body': json.dumps(response)
}
「index.py」を「package.zip」にパッケージ化しました。
% pwd
/Users/atsushi/Document/AWS/Python
%
% ls -l
total 8
-rw-r--r-- 1 atsushi staff 742 5 24 17:14 index.py
%
% zip package.zip index.py
adding: index.py (deflated 43%)
%
% ls -l
total 16
-rw-r--r-- 1 atsushi staff 742 5 24 17:14 index.py
-rw-r--r-- 1 atsushi staff 590 5 24 17:20 package.zip
%
zipファイルをS3にアップロード
「s3://bucket-atsushi-00/python/」ディレクトリをS3上に作成して、そこにzipファイルをアップロードしました。
YAMLコード修正
修正後のYAMLコードは以下になります。
---
AWSTemplateFormatVersion: 2010-09-09
Description: "IaC for Lambda"
Parameters:
NameOfFunction:
Type: String
Description: "Name of Lambda Function"
Resources:
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Ref NameOfFunction
Description: "Function that put items to DynamoDB"
Runtime: "python3.9"
MemorySize: "128"
Timeout: "5"
Role: "arn:aws:iam::XXXXXXXXXXXX:role/service-role/putDynamoFunc-role-l6trp0uq"
Handler: index.lambda_handler
Architectures: [ "x86_64" ]
EphemeralStorage:
Size: "512"
Code:
S3Bucket: "bucket-atsushi-00"
S3Key: "python/package.zip"
修正箇所はCodeプロパティのみです。「ZipCode」から「S3Bucket」と「S3Key」に変えています。
「S3Bucket」にはZip化したPythonプログラムが保存されているS3バケット名を記述します。
「S3Key」には、Zip化したPythonプログラム(ここではzipファイル)までのパスを記述します。「bucket-atsushi-00」バケットの下に「python」ディレクトリを作成しているので、ここでは「python/package.zip」となっています。
デプロイとテスト
ここから先は、「CloudFormationテンプレート①(Pythonコードベタ書き)」の時と同じです。
省略いたします。
コメント