CloudFormationでLambdaを作成する3パータン(S3/インライン/コンテナ)

CloudFormationでLambdaを作成する3パターン

CloudFormationでLambdaを作成する

CloudFormationでLambdaを作成する場合、大別すると以下の3パターンあります。

  • S3バケットにコードをアップロードする
  • インラインでコードを記載する
  • コンテナイメージを用意する

今回はこの3パターンで同様の内容のLambdaを作成し、流れを確認します。

構築する環境

Diagram of 3 patterns for creating Lambda with CloudFormation.

パターンごとに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リポジトリに対する作業ですが、イメージを作成し、リポジトリにプッシュします。詳細は以下のページをご確認ください。

あわせて読みたい
CloudFormationでFargate入門 【CloudFormationでFargateに入門するための構成】 AWS FargateはサーバーレスでDockerコンテナを実行することができるサービスです。今回はFargate入門ということで、C...

AWS Management Consoleを確認します。まずS3バケットです。

Detail of S3 Bucket 1.

ソースコードを圧縮したZipファイルが設置されていることがわかります。次にECRリポジトリです。

The container image is pushed to the ECR repository.

作成したイメージがプッシュされていることがわかります。

残りのCloudFormationスタックを作成する

CloudFormationスタックを作成します。引き続きスタックの作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【CloudFormationのネストされたスタックで環境を構築する方法】 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を確認します。

SSM Parameter Store

「fa-020-parameter」というパラメータに「hello, awstut !」という文字列が保存されていることがわかります。次に1つ目のLambda関数を確認します。

The Lambda function has been created in the two files included in the Zip.

2つのソースコードが確認できます。Zipファイルに含めた2ファイルです。続いて2つ目のLambda関数です。

The code described inline has been created as index.py.

1つのソースコードが確認できます。前述の仕様通り、インラインで記載したコードが、index.pyという名前のソースコードとして作成されています。

A function was created from the container image.

ECRリポジトリからイメージがプルされ、関数が作成されていることがわかります。

Lambda関数実行1

準備が整いましたので、順番に関数を実行します。まず1つ目の関数です。関数の実行もAWS Management Consoleから行います。

Execution of Lambda function.

「Test」タブを選択後、「Test」ボタンを押下します。今回のTest eventはデフォルトのもので構いません。

Function 1 was successfully executed.

同じページに実行結果が表示されます。「hello, awstut !」という文字列が返ってきました。このことから、正常にSSM Parameter Storeからパラメータを取得することができたことがわかります。CloudWatch Logsでも、関数実行時のログを確認することができます。

The Lambda execution log is also written to CloudWatch Logs.

CloudFormationテンプレートファイルでは、CloudWatch Logsロググループの定義はしていませんが、自動的にロググループとストリームが作成され、書き込みが行われます。

Lambda関数実行2

続いて2つ目の関数です。先ほどと同様に実行します。

Function 2 was successfully executed.

正常に動作しました。インライン定義したコードでも同様に動作しました。

Lambda関数実行3

最後に3つ目の関数を実行します。

Function 3 was successfully executed.

こちらも正常に動作しました。イメージタイプでも問題なく動作しました。

まとめ

CloudFormationでLambdaを作成する場合の3パターンを構築し、挙動を確認しました。

S3バケットにソースコードをアップロードする場合は、複数のモジュールをZipファイルで圧縮してアップロード可能ですので、本格的な関数の開発に適しています。

インラインでコードを記載する場合は、ソースコードのZip化やアップロードが不要ですので、テストコードを簡易的に試験したい場合などに適しています。

コンテナイメージタイプの関数の場合は、イメージの容量の上限は10GBですので、大容量のバイナリファイルを実行したい場合に適しています。