AWS SAMでs3 eventをトリガーにしたlambdaを実装する
やりたいこと
前回記事で作成したlambdaの親lambdaをs3eventをトリガーにして起動したい。 s3イベントの条件は、pngデータがuploadされたとき。
template.yaml
変更したのはtemplate.yamlのみ。
sam-example-put-objectというバケットを作成して、 同バケットにpngファイルが保存されたのをトリガーにHelloWorldFunctionを起動するようにしている。
lambdaの処理は適当。
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" Parameters: BucketName: Type: String Default: sam-example-put-object FuncParentName: Type: String Default: HelloWorldFunctionParent FuncChildName: Type: String Default: HelloWorldFunctionChild DataSuffix: Type: String Default: png Resources: DataBucket: Type: "AWS::S3::Bucket" Properties: BucketName: !Sub "${BucketName}" HelloWorldFunction: Type: AWS::Serverless::Function Properties: PackageType: Image FunctionName: !Sub "${FuncParentName}" Environment: Variables: CHILDLAMBDAARN: !GetAtt HelloWorldFunctionChild.Arn Events: BucketEvent: Type: S3 Properties: Bucket: Ref: DataBucket Events: - "s3:ObjectCreated:*" Filter: S3Key: Rules: - Name: suffix Value: !Sub "${DataSuffix}" Policies: - CloudWatchPutMetricPolicy: {} - LambdaInvokePolicy: FunctionName: !Sub "${FuncChildName}" - S3CrudPolicy: BucketName: !Sub "${BucketName}" Metadata: Dockerfile: Dockerfile DockerContext: ./hello_world DockerTag: python3.8-v1 HelloWorldFunctionChild: Type: AWS::Serverless::Function Properties: PackageType: Image FunctionName: !Sub "${FuncChildName}" Metadata: Dockerfile: Dockerfile DockerContext: ./hello_world_child DockerTag: python3.6-v1 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 HelloWorldFunctionRole.Arn HelloWorldFunctionChildIamRole: Description: "Implicit IAM Role created for Hello World function" Value: !GetAtt HelloWorldFunctionChildRole.Arn
tips
- resouce外でバケット名を作っておく
- こうしておかないとpolicyのリソース指定とかで循環参照エラーが発生する。
- 親lambdaにpolicyを設定
- 子lambdaを叩けるようにpolicyをアタッチ。
- この時、cloudwatchのput metricsをつけておかないとログが見れない
- parameterの設定
- 関数名とかは外だししてみた。
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
DVCとkedro
背景
自分でMLを開発している時に、ワークフロー管理(データ準備、前処置、保存、学習、評価)までが蛸壷状態になっていることが多い。
DVCは前から知っていたのだが、kedroを知ったので、改めて調べて見た。
まとめ
DVCはデータバージョン管理が主であり、ワークフロー管理だけするのは違う気がした。 比較することが間違いだったかもしれない。
個人でワークフロー管理するだけなら、ワークフロー管理が主のkedroを利用するのが良いと思った。 しかし、まだv0.16.4なのでその辺りが気がかり。
DVCは、データがコロコロ変わるような環境でMLopsをしっかり行っていく場合に良いツールだと思う。 CMLと組み合わせることで、良いのだろう。
前提
MLOps
機械学習システムの開発でも、CI/CD適用しましょうね、という話。
詳しくは下記ブログを参照。 - データ、モデル、コードの3要素がMLシステムには含まれている - データサイエンティスト、データエンジニア、デベロッパーそれぞれでMLシステムで担保箇所が異なる - 有用なツール(例:DVC、mlflow)
などなどいろんなことが載っている。
上記、ワークフローがごちゃごちゃ、みたいな状態も再現性が担保できないという意味で、MLOpsとしてよくない状態。
DVC
概要
その名の通り、Data Version Controlのためのツール。
データバージョン管理だけでなく、ワークフローの管理から、metricsの可視化までできる。
- data versioning
- 文字通りデータのバージョン管理。キャッシュ利用している。また、DVCリポジトリを設定することで、データ共有も可能。
- data access
- S3, GCS, sshなど多様な接続先からデータの取得が可能
- data pipeline
- ワークフロー管理。コマンドで設定すると自動でyamlが生成されるぽい。
- experiments
- 実験管理。ブランチ間で指標比較が可能。
後述の同コミュニティがCMLというツールも開発している。 こちらは、プルリク時にmasterとtarget branch間との指標比較の自動化をしてくれるっぽい。 同コミュニティということもあり、dvc pullを利用してgitリポジトリ外のデータを引っ張ってくることもできる。
コミュニティ
バックは企業で、DVCとCML の開発を行っている。 というかこれのための企業?
活発な印象でOSS選定する時に懸念するようなことはない気がする。
所感
結構、使うのが厳しそう。 gitと密に連携しているのがエンジニアに取っては良いところでもあり、非gitユーザーにとってのハードルの高さでもある気がする。
また、data versioningは果たしてどこまで使うのだろうか。 結構な頻度かつ、知らぬ間に学習データが変わっている、ということがなければ必要ないのかもしれない。 それこそ私の職場では、学習データがほとんど更新されない(年2回程度)ので、調べながら実際のユースケースが少ないと思った。 しかし、webアプリ系の企業の場合は日毎に学習と評価を繰り返すことがあって、それに対してdata versioningが有用なのかもしれない。
リポジトリ間を使って、データ共有できる、というのは結構メリットあると思うけど、多人数でMLシステムを開発したことがないので、メリットの実感が沸かない。 s3もあるし。 再現性という意味では一番適していいるのかもしれないが。
ロールバック機能もある(多分。)
さらに言うと、gitの開発も頻繁なのになぜかワークフロー管理の日本語ブログ記事が出てこない。大体は使ってみた程度。 ただの予想だが、DVCは個人用途に向かないからそもそもブログで書かない、エンジニアよりすぎるツールでとっつきにくい、と言うのが背景にあるのではないだろうか。
kedro
github.com kedro.readthedocs.io
概要
MLシステムのワークフロー管理のためのpythonフレームワーク。 ワークフロー管理に特化しているので、実験管理などは含まれない。 データカタログという機能があって、データ取得は様々なストレージに対応、管理できる。 独自のパイプラインも定義することができるので拡張性も高い。
使い方
これらのブログ見てもらった方が良い。とてもよく纏まっていて参考になります。
小さく始めて大きく育てるMLOps2020 | AI tech studio
コミュニティ
こちらも背景にあるのは、企業。 DVCよりは少ないけども、gtiの開発も安定している。 OSS選定としては問題ない気もする。
所感
pythonのフレームワークで拡張性が高いという点、ディレクトリテンプレートがある点などいろいろと使いやすそう。 DVCと比較すると開発コミュニティが小さい気もするけど、許容範囲だとは思う。 残念なのはまだversion 0.16.4だということ(2020/08/14時点)。
今度使ってみようと思う。
参考
- OSS選定について
k8sの勉強に使えるサイト
VPC設定してec2立てる
これ参照。