CloudFormationでLambdaを作成する
CloudFormationでLambdaを作成する場合、大別すると以下の3パターンあります。
- S3バケットにコードをアップロードする
- インラインでコードを記載する
- コンテナイメージを用意する
今回はこの3パターンで同様の内容のLambdaを作成し、流れを確認します。
構築する環境
パターンごとにLambda関数を作成しますが、実行する内容は共通です。今回はLambda関数からAWSリソースにアクセスする検証ということで、SSM Parameter Storeにアクセスし、保存されているパラメータを取得します。
またランタイム環境も、共通してPython3.8とします。
環境構築用のCloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。以下のURLにCloudFormationテンプレートを配置してます。
https://github.com/awstut-an-r/awstut-fa/tree/main/020
テンプレートファイルのポイント解説
今回のアーキテクチャを構成するための、各テンプレートファイルのポイントを取り上げます。
コードを圧縮したZipファイルをアップロードするS3バケットを作成する
Lambda関数1用のコードをアップロードするS3バケットを作成します。
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "${Prefix}-bucket"
AccessControl: Private
Code language: YAML (yaml)
特に特別な設定はしません。
コンテナイメージをプッシュするECRリポジトリを作成する
次にLambda3用のイメージをプッシュするECRリポジトリを作成します。
Resources:
ECRRepository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: !Sub ${Prefix}-repository
Code language: YAML (yaml)
こちらも特別な設定は不要です。
SSM Parameter Storeに試験用パラメータを登録する
検証用として、3つのLambda関数からアクセスするSSM Parameter Storeを定義します。
Resources:
SSMParameter:
Type: AWS::SSM::Parameter
Properties:
Name: !Ref Name
Type: String
Value: !Ref Value
Code language: YAML (yaml)
こちらも特別な設定は行いません。文字列型のパラメータを1つ定義します。
Lambda関数用のIAMロール
Lambda関数用のIAMを定義します。ポイントは、自分自身を実行するため権限と、SSMにアクセスするための権限の2つを満たす必要があるという2点です。
Resources:
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
Policies:
- PolicyName: GetSSMParameter
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ssm:GetParameter
Resource:
- !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${SSMParameter}"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Code language: YAML (yaml)
ポイントの1つ目は、Lambda関数を実行するために必要な権限を付与する必要があるという点です。今回はAWS管理ポリシーAWSLambdaBasicExecutionRoleを使用して付与します。このポリシーでは、以下の権限が含まれています。
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
CloudWatch Logsにログを保存するために必要な権限です。
ポイントの2つ目は、SSM Parameter Storeに関する権限です。今回はLambdaからAWSリソースにアクセスするための検証ということで、SSM Parameter Storeに保存されているパラメータを取得する関数を作成します。そのために必要な権限をIAMロールに付与する必要があるということです。Resourceプロパティに先述のパラメータを指定し、Actionプロパティでssm:GetParameterを指定します。
S3バケットにアップロードしたZipファイルからLambda関数を作成する
Lambda関数1を確認します。S3バケットにコードをアップロードして、関数を定義する方法を確認します。
Resources:
Function1:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: !Ref S3Bucket
S3Key: !Ref S3Key
Environment:
Variables:
ssm_parameter_name: !Ref SSMParameter
region_name: !Ref AWS::Region
FunctionName: !Sub ${Prefix}-function1
Handler: !Ref Handler
MemorySize: !Ref MemorySize
PackageType: Zip
Runtime: !Ref Runtime
Role: !Ref LambdaRoleArn
Code language: YAML (yaml)
S3BucketプロパティおよびS3Keyプロパティで、先述のS3バケットおよびアップロードしたZipファイル名を指定します。またPackageTypeプロパティで「Zip」を指定します。上記の通り、コードをZip化してアップロードするためです。
Environmentプロパティで関数内で参照可能な環境変数を定義できます。環境変数を経由して、CloudFormation側の値を、関数側に受け渡すことができます。今回はSSM Parameter Storeに保存したパラメータの名前と、リージョン名を渡します。
Handlerプロパティで実行するモジュール名を指定し、Runtimeプロパティでランタイム環境を指定します。
MemorySizeプロパティで、Lambda関数の実行時に割り当てるメモリサイズを指定します。この値に応じて、関数実行時に割り当てられるCPUも決定されます。
Roleプロパティで先述のIAMロールを指定します。
2ファイルをアップロードします。
1つ目はmymodule.pyです。
import boto3
import os
def get_ssm_parameter(parameter_name, client=None):
if not client:
client = boto3.client('ssm', region_name=os.environ['region_name'])
return client.get_parameter(Name=parameter_name)['Parameter']['Value']
Code language: Python (python)
2つ目はindex.pyです。
import json
import os
import mymodule
def lambda_handler(event, context):
ssm_parameter_name = os.environ['ssm_parameter_name']
ssm_parameter = mymodule.get_ssm_parameter(ssm_parameter_name)
return {
'statusCode': 200,
'body': json.dumps(ssm_parameter)
}
Code language: Python (python)
このように関数本体を定義したファイルに加え、別モジュールファイルもアップロードして使用することができます。
インラインに記載したコードからLambda関数を作成する
Lambda関2を確認します。インラインでコードを記載し、関数を定義する方法を確認します。
Resources:
Function2:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import boto3
import json
import os
def lambda_handler(event, context):
ssm_parameter_name = os.environ['ssm_parameter_name']
region_name = os.environ['region_name']
ssm = boto3.client('ssm', region_name=region_name)
ssm_parameter = ssm.get_parameter(
Name=ssm_parameter_name)['Parameter']['Value']
return {
'statusCode': 200,
'body': json.dumps(ssm_parameter)
}
...
Code language: YAML (yaml)
ランタイム環境によっては、ZipFIleプロパティを使用して、インラインでコードを記載することができます。
(Node.js and Python) The source code of your Lambda function. If you include your function source inline with this parameter, AWS CloudFormation places it in a file named index and zips it to create a deployment package.
AWS::Lambda::Function Code
今回はランタイム環境をPython3.8としますので、インラインでコードを用意する条件を満たします。加えて自動的に作成される関数用のファイル名(index.py)の条件を満たすように、Handlerプロパティを設定します。
このようにZipFileプロパティを使用する方法は、インラインでコードを記載できますので、簡易的な関数を作成したい場合には便利です。
コンテナイメージからLambda関数を作成する
Lambda関3を確認します。コンテナイメージを用意し、関数を定義する方法を確認します。
Resources:
Function3:
Type: AWS::Lambda::Function
Properties:
Code:
ImageUri: !Ref ImageUri
...
Code language: YAML (yaml)
ImageUriプロパティで先述のECRリポジトリを指定します。
今回実行するコンテナイメージは、AWSが公式にLambda関数用として提供しているPython3.8バージョンのイメージをベースに作成します。Dockerfileで作成するイメージを定義します。
FROM public.ecr.aws/lambda/python:3.8
COPY index.py ${LAMBDA_TASK_ROOT}
COPY mymodule.py ${LAMBDA_TASK_ROOT}
CMD [ "index.lambda_handler" ]
Code language: Dockerfile (dockerfile)
コンテナ内で実行するコードは、1つ目の関数と同一とします。2ファイルをイメージ内にコピーします。
イメージタイプのLambda関数の利点ですが、イメージのサイズが10GBまでサポートされています。通常のLambda関数ですと、圧縮前が50MB、展開後が250MBが上限ですので、大きな差があります。例えば容量の大きなバイナリファイルを実行する場合は、イメージタイプのLambda関数を採用することが検討できると思います。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。今回は2回に分けてCloudFormationスタックを作成します。
S3バケットとECRリポジトリを作成する
Lambda関数を作成する前に、S3バケットとECRリポジトリを作成後、Zipファイルの設置やイメージのプッシュを実行します。2リソースの作成ですが、以下のコマンドは、AWS CLIからCloudFormationスタックを作成する例です。
$ aws cloudformation create-stack \
--stack-name fa-020-s3-and-ecr \
--template-url https://[bucket-name].s3.ap-northeast-1.amazonaws.com/fa-020-s3-and-ecr.yaml
Code language: Bash (bash)
今回作成された2リソースの情報は以下の通りです。
- S3バケット名:fa-020-bucket
- ECRリポジトリ名:fa-020-repository
まずS3バケットに対する作業ですが、ソースコードをZip化し、バケットにアップロードします。
$ ls
index.py mymodule.py
$ zip deploy_package.zip *
adding: index.py (deflated 51%)
adding: mymodule.py (deflated 39%)
$ ls
deploy_package.zip index.py mymodule.py
$ aws s3 cp deploy_package.zip s3://fa-020-bucket
upload: ./deploy_package.zip to s3://fa-020-bucket/deploy_package.zip
Code language: Bash (bash)
次にECRリポジトリに対する作業ですが、イメージを作成し、リポジトリにプッシュします。詳細は以下のページをご確認ください。
AWS Management Consoleを確認します。まずS3バケットです。
ソースコードを圧縮したZipファイルが設置されていることがわかります。次にECRリポジトリです。
作成したイメージがプッシュされていることがわかります。
残りのCloudFormationスタックを作成する
CloudFormationスタックを作成します。引き続きスタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- SSMパラメータの名前:fa-020-parameter
- SSMパラメータの値:hello, awstut !
- Lambda関数1:fa-020-function1
- Lambda関数2:fa-020-function2
- Lambda関数3:fa-020-function3
作成されたリソースをAWS Management Consoleから確認します。まずSSM Parameter Storeを確認します。
「fa-020-parameter」というパラメータに「hello, awstut !」という文字列が保存されていることがわかります。次に1つ目のLambda関数を確認します。
2つのソースコードが確認できます。Zipファイルに含めた2ファイルです。続いて2つ目のLambda関数です。
1つのソースコードが確認できます。前述の仕様通り、インラインで記載したコードが、index.pyという名前のソースコードとして作成されています。
ECRリポジトリからイメージがプルされ、関数が作成されていることがわかります。
Lambda関数実行1
準備が整いましたので、順番に関数を実行します。まず1つ目の関数です。関数の実行もAWS Management Consoleから行います。
「Test」タブを選択後、「Test」ボタンを押下します。今回のTest eventはデフォルトのもので構いません。
同じページに実行結果が表示されます。「hello, awstut !」という文字列が返ってきました。このことから、正常にSSM Parameter Storeからパラメータを取得することができたことがわかります。CloudWatch Logsでも、関数実行時のログを確認することができます。
CloudFormationテンプレートファイルでは、CloudWatch Logsロググループの定義はしていませんが、自動的にロググループとストリームが作成され、書き込みが行われます。
Lambda関数実行2
続いて2つ目の関数です。先ほどと同様に実行します。
正常に動作しました。インライン定義したコードでも同様に動作しました。
Lambda関数実行3
最後に3つ目の関数を実行します。
こちらも正常に動作しました。イメージタイプでも問題なく動作しました。
まとめ
CloudFormationでLambdaを作成する場合の3パターンを構築し、挙動を確認しました。
S3バケットにソースコードをアップロードする場合は、複数のモジュールをZipファイルで圧縮してアップロード可能ですので、本格的な関数の開発に適しています。
インラインでコードを記載する場合は、ソースコードのZip化やアップロードが不要ですので、テストコードを簡易的に試験したい場合などに適しています。
コンテナイメージタイプの関数の場合は、イメージの容量の上限は10GBですので、大容量のバイナリファイルを実行したい場合に適しています。