404 not found

備忘録です

AWS SAMで複数lambdaを作成する

やりたいこと

AWS SAMで複数のlambdaをコンテナイメージから作成&管理したい。

また、親labmdaから子lambdaをinvokeしたい。

動機

処理の都合上、lambdaが複数必要だった。 こういう場合、STEP FUNCTIONSでやるべきでは?とは思っているが、 とりあえずLAMBDAでやってみる。

ディレクトリ構成

各lambdaのdockerfileやapp.pyを各ディレクトリで管理しているだけ。

.
├── README.md
├── __init__.py
├── events
│   └── event.json
├── hello_world
│   ├── Dockerfile
│   ├── __init__.py
│   ├── app.py
│   └── requirements.txt
├── hello_world_child
│   ├── Dockerfile
│   ├── __init__.py
│   ├── app.py
│   └── requirements.txt
├── local-enviroment.json
├── samconfig.toml
├── template.yaml
└── tests
    ├── __init__.py
    └── unit
        ├── __init__.py
        └── test_handler.py

template.yaml

HelloWorldFunction側でHelloWorldFunctionChildのArnと名前を取得するようにしている。

また、childをinvokeするroleも設定している。

各lambdaでどのdockerfileを参照するかは、dockercontextで簡単に指定可能。

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  Sample SAM Template for multiple lambda

Globals:
  Function:
    Timeout: 3
    Environment:
      Variables:
        SAMPLACE: "AWS"

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
      Role: !GetAtt InvokeLambdaIamRole.Arn
      Environment:
        Variables:
          CHILDLAMBDAARN: !GetAtt HelloWorldFunctionChild.Arn
          CHILDLAMBDANAME: !Ref HelloWorldFunctionChild
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./hello_world
      DockerTag: python3.8-v1
  HelloWorldFunctionChild:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
      FunctionName: "HelloWorldFunctionChild"
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./hello_world_child
      DockerTag: python3.6-v1
  InvokeLambdaIamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "invoke_lambda"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "lambda:InvokeFunction"
                Resource: !GetAtt HelloWorldFunctionChild.Arn

Outputs:
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionChild:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunctionChild.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt InvokeLambdaIamRole.Arn
  HelloWorldFunctionChildIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionChildRole.Arn

local-enviroment.json

これは、sam local invoke --env-vars local-enviroment.jsonとして与える設定ファイル。

これを元にlambda呼び出しのlocal invokeを実行したかったが、よくわからなかった。

CHILDLAMBDANAMEもそのために環境変数として設定下が、結局使っていない。

mockでやるしかないかな。

{
    "Parameters": {
        "SAMPLACE": "LOCAL"
    }
}

lambda

dockerfileはほとんどsam initそのままなので割愛。 一応、pythonのversionをかえてみたりしているが、問題はない。

hello_world

app.py

import json
import os
import boto3

place = os.environ["SAMPLACE"]
child_lambda_arn = os.environ['CHILDLAMBDAARN']
child_lambda_name = os.environ['CHILDLAMBDANAME']

print('place is:', place)
print('child lambda arn is:', child_lambda_arn)
print('child lambda name is:', child_lambda_name)
if place == "AWS":
    lambda_client = boto3.client('lambda')
else:
    # mock client for local test
    lambda_client = boto3.client('lambda')


def lambda_handler(event, context):

    input_event = {
        "param1": 1,
        "param2": 2,
        "param3": 3
    }
    Payload = json.dumps(input_event)
    res = lambda_client.invoke(
        FunctionName=child_lambda_arn,
        InvocationType='RequestResponse',
        Payload=Payload
    )

    print("---02: response:", res)

    body = json.loads(res['Payload'].read())
    print("---03: body:", body)

    return {
        "statusCode": 200,
        "body": json.dumps(
            {
                "message": "hello world",
            }
        ),
    }

hello_world_child

app.py

import json

def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "body": json.dumps(
            {
                "message": "hello world from child",
            }
        ),
    }

build

sam build

local invoke

sam local invoke HelloWorldFunction

sam local invoke HelloWorldFunctionChild

が、親の方はlocalでlambda実行できない問題のためエラーが出る。

deploy

sam deploy

最初だけは sam deploy --guided

この時、ECRのリポジトリはちゃんと個別に入力しないといけない。親切。

できなかったこと

親lambdaのlocal invokeができない。

mockじゃなくて直接invokeしたいけど、正直よくわからない。

参考

AWS LambdaからLambdaを非同期で呼び出す(Python) - Qiita

【AWS SAM 入門⑥】IAM ロールの作成と Lambda Function への割り当て - log4ketancho

AWS SAMでローカル環境でS3とDynamoDBを扱うLambdaを実行する - Qiita

python - How to invoke AWS lambda from another lambda within SAM local? - Stack Overflow