はじめに
Step Functionsの開発を行っていたときに、ローカル上で実行確認が取りたいなと思ったのですが、そのための方法がパッと出てこず少し困ったので、その方法をまとめておきます。
目的
ローカル環境でStep Functionsの実行確認を行う。
そのために、SAM Localで実行しているlambdaをLocalStackのStep Functionsから呼び出し実行する。
実行環境
環境
- macOS Catalina 10.15.7
- aws-cli 1.18.185
- sam-cli 1.12.0
事前準備
ここでは詳細に書きませんが、aws-cliとsam-cliが実行できる環境が必要です。
また、LocalStackを使用するので、そのためのcredential情報をセットしてください。
$ vi ~/.aws/credentials
[localstack]
aws_access_key_id = dummy
aws_secret_access_key = dummy
$ vi ~/.aws/config
[profile localstack]
region = ap-northeast-1
output = text
やってみる
LocalStackの準備
まずはLocalStackのコンテナを立てていきます。
GitHubのページに幾つかの方法が記載されていますが 今回はdocker-composeで行っていきます。
$ git clone https://github.com/atlassian/localstack.git
$ cd localstack
この時、後々SAMで立てたlambdaをLocalStackのStep Functionsから呼び出す必要があるので、それらを呼び出せるようにlambdaのエンドポイントを指定する環境変数をdocker-compose.ymlに追記しておきます
version: '2.1'
services:
localstack:
container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
image: localstack/localstack
network_mode: bridge
ports:
- "4566:4566"
- "4571:4571"
- "${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}"
environment:
- SERVICES=${SERVICES- }
- DEBUG=${DEBUG- }
- DATA_DIR=${DATA_DIR- }
- PORT_WEB_UI=${PORT_WEB_UI- }
- LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
- LAMBDA_ENDPOINT=http://host.docker.internal:3001 ##追記
- STEPFUNCTIONS_LAMBDA_ENDPOINT=http://host.docker.internal:3001 ##追記
- KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
- DOCKER_HOST=unix:///var/run/docker.sock
- HOST_TMP_FOLDER=${TMPDIR}
volumes:
- "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
追記したら、コンテナを立てておきましょう
$ docker-compose up -d
lambda関数の作成
今回はStep Functionsでlambdaを実行できることを確認したいだけなので、lambda関数自体は非常に単純なものにします。
ということで、以下の2つの関数を作成していきます。
- HelloWorldFunction :
{入力パラメータ}+HelloWorld!!
を出力する関数 - GoodByeFunction :
{入力パラメータ}+GoodBye!!
を出力する関数
sam initでlambda関数を作成する
それでは適当なディレクトリにlambda関数を作成していきます。
sam init
で作成していきます。ダイアログの通りですが、runtime
はpython3.6、sfn-test
というプロジェクト名で作成しています。
$ sam init
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
Which runtime would you like to use?
1 - nodejs12.x
2 - python3.8
3 - ruby2.7
4 - go1.x
5 - java11
6 - dotnetcore3.1
7 - nodejs10.x
8 - python3.7
9 - python3.6
10 - python2.7
11 - ruby2.5
12 - java8.al2
13 - java8
14 - dotnetcore2.1
Runtime: 9
Project name [sam-app]: sfn-test
Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git
AWS quick start application templates:
1 - Hello World Example
2 - EventBridge Hello World
3 - EventBridge App from scratch (100+ Event Schemas)
4 - Step Functions Sample App (Stock Trader)
Template selection: 1
-----------------------
Generating application:
-----------------------
Name: sfn-test
Runtime: python3.6
Dependency Manager: pip
Application Template: hello-world
Output Directory: .
Next steps can be found in the README file at ./sfn-test/README.md
これで必要なファイル群が作成されました。
ディレクトリ構成は以下の通りです。
$ tree .
.
└── sfn-test
├── README.md
├── __init__.py
├── events
│ └── event.json
├── hello_world
│ ├── __init__.py
│ ├── app.py
│ └── requirements.txt
├── template.yaml
└── tests
├── __init__.py
└── unit
├── __init__.py
└── test_handler.py
lambdaを実行してみる
この状態で一度、lambda関数をsamで実行してみます。
$ cd sfn-test
$ sam local invoke --profile localstack
{"statusCode":200,"body":"{\"message\": \"hello world\"}"}
デフォルト値の出力がされました。問題なさそうです。
lambda関数の編集
今回は、入力を受け取ってから{入力}+HelloWorld!!
・{入力}+GoodBye!!
を返すことが目的なので、lambda関数の中身を編集しておきます。
まずはHelloWorldFunctionを作成します。 以下がapp.py中身です。
入力された値を受け取って、HelloWorld!!と返すだけの単純なものであることが分かるかと思います。
def lambda_handler(event, context):
print(str(event["input"]) + " HelloWorld!!")
return event
2つ目のlambda関数を作成
2つ目の{入力}+GoodBye!!
の方のlambda関数も同様に作成しておきます。
今回は、同じディレクトリにもう一つ関数を作成しておきます。
~/sfn-test
$ tree .
.
├── README.md
├── __init__.py
├── events
│ └── event.json
├── good_bye ##追加
│ ├── __init__.py ##追加
│ └── app.py ##追加
├── hello_world
│ ├── __init__.py
│ ├── app.py
│ └── requirements.txt
├── template.yaml
└── tests
├── __init__.py
└── unit
├── __init__.py
└── test_handler.py
こちらのlambdaも入力を受け取って、GoodBye!!と返すだけの単純なものです。
def lambda_handler(event, context):
print(str(event["input"]) + " GoodBye!!")
return event
template.yamlの編集
先ほど作成した2つの関数を呼び出せるように、template.yamlを修正していきます。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
sfn-test
Sample SAM Template for sfn-test
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.6
GoodByeFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: good_bye/
Handler: app.lambda_handler
Runtime: python3.6
Resources
内に、HelloWorldFunction
とGoodByeFunction
を定義します。
CodeUri
、Handler
で関数のパスを指定しているので、ここを工夫すれば別ディレクトリに作成してある関数も呼び出すことができます。
start-lambda
ここまででlambdaの準備はOKです!
Step Functionsから呼び出せるように、lambdaを実行しておきましょう。
$ sam local start-lambda --profile localstack
Starting the Local Lambda Service. You can now invoke your Lambda Functions defined in your template through the endpoint.
2021-01-17 14:55:11 * Running on http://127.0.0.1:3001/ (Press CTRL+C to quit)
エンドポイントはデフォルトでhttp://127.0.0.1:3001/
なので、Step Functionsからもこのエンドポイントで呼び出すことができます。
ステートマシンの作成
lambdaの準備ができたので、次はStep Functionsのステートマシンを作成していきます。
定義ファイルをdefine.json
という名前で作成します。
lambdaを実行しているのとは別のターミナルを開いて、以下を実行していきましょう。
$ touch define.json
{
"StartAt": "HelloWorld",
"States": {
"HelloWorld": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:HelloWorldFunction",
"Parameters": {
"input": "hogehoge"
},
"Next": "GoodBye"
},
"GoodBye": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:GoodByeFunction",
"End": true
}
}
}
ResourceのところにlambdaのARNを指定することで呼び出すことができます。ローカルで呼び出す場合はFunction名を正しくセットしておけば問題ないはずです。
定義内容は単純で、HelloWorldFunctionの後にGoodByeFunctionを呼び出すだけです。lambda内で出力することになるinput
というパラメータにはhogehoge
という文字列を渡しています。
それでは、aws-cliでLocalStackのコンテナ上にStep Functionsを作成していきます。
$ aws stepfunctions create-state-machine \
--name TestState \
--definition file://define.json \
--role-arn "arn:aws:iam::000000000000:role/DummyRole" \
--endpoint http://localhost:4566
TestState
という名前のステートマシンを、先ほど作成した定義ファイル通りにLocalStackのエンドポイントである4566
のポートで作成しています。
すると、以下のように出力されるはずです。
{
"stateMachineArn": "arn:aws:states:us-east-1:000000000000:stateMachine:TestState",
"creationDate": 1610863591.321
}
これでStep FunctionsのARNが作成されました。あとはこれを呼び出せば実行できるはずです!
実行してみる
今回は、aws-cliで先ほどのステートマシンを実行してみます。
$ aws stepfunctions start-execution \
--state-machine arn:aws:states:us-east-1:000000000000:stateMachine:TestState \
--endpoint http://localhost:4566
すると、lambdaを実行しているターミナルが動き、結果が出力されていくはずです。
Starting the Local Lambda Service. You can now invoke your Lambda Functions defined in your template through the endpoint.
2021-01-17 14:55:11 * Running on http://127.0.0.1:3001/ (Press CTRL+C to quit)
Invoking app.lambda_handler (python3.6)
Skip pulling image and use local one: amazon/aws-sam-cli-emulation-image-python3.6:rapid-1.12.0.
Mounting /sfn-test/hello_world as /var/task:ro,delegated inside runtime container
・
・
・
hogehoge HelloWorld!!
・
・
・
Mounting /sfn-test/good_bye as /var/task:ro,delegated inside runtime container
・
・
・
hogehoge GoodBye!!
・
・
・
無事、2つのlambdaを実行できました!
ARNを呼び出しているだけなので、アプリケーションからSDKなどで呼び出すこともできます。
実際にはこの形で、アプリケーションからStep Functionsと連携が取れるかをローカル環境で確認するために利用できそうです。
最後に
ということで、LocalStackとSAM Localを利用して、Step Functions(lambda)のローカル環境での実行確認が行えました。
AWS上にデプロイする前に、実行確認が取りたい場合などには有効利用できそうですね。
本記事は以上です。
どなたかの参考になれば幸いです!