AWS
docker
DynamoDB
sam
stepfunctions

Step Functions Local が出た!SAMやDynamoDB Localと一緒に使ってみる


はじめに

先日、Step Functions Local が利用できるようになり、

ワークフローをローカルで開発、テストすることが可能になりました。

DynamoDB Localと同じようにJAR パッケージおよび Docker イメージが提供されています。

AWS Step Functions ワークフローをローカルで開発してテストする

https://aws.amazon.com/jp/about-aws/whats-new/2019/02/develop-and-test-aws-step-functions-workflows-locally/

早速触ってみたのですが、単体やSAMと連携した動作は確認できたものの、

DynamoDB Localとの連携がまだ上手くいっておらず、原因を調べています。


事前準備

dockerのインストール方法や基本的な使用方法については割愛します。

またAWSCLIおよびAWS SAM CLIを使用します。

必要に応じてインストールまたはアップグレードしておきます。

# aws --version

aws-cli/1.16.101 Python/3.6.2 Linux/4.14.88-88.76.amzn2.x86_64 botocore/1.12.91

# sam --version
SAM CLI, version 0.11.0

Step Functions LocalのDockerイメージをダウンロードします。

# docker pull amazon/aws-stepfunctions-local:1.0.0

1.0.0: Pulling from amazon/aws-stepfunctions-local
6c5ec16d4336: Pull complete
fed0ccb4012f: Pull complete
018f3324324c: Pull complete
Digest: sha256:f9e6ae948b814f604ec313a15e00aef850bfa2cd085e33a68b84e6fc8fee0ba7
Status: Downloaded newer image for amazon/aws-stepfunctions-local:1.0.0

DynamoDB LocalのDockerイメージもダウンロードします。

# docker pull amazon/dynamodb-local:1.11.477

1.11.477: Pulling from amazon/dynamodb-local
638b75f800bf: Pull complete
7d727a2b79b4: Pull complete
c83cc16257dd: Pull complete
Digest: sha256:cbe14c530c08997a38da04efe3778242f5544e0cdb7e9002fbbc3682f2421c0c
Status: Downloaded newer image for amazon/dynamodb-local:1.11.477


まずは Hello World

Step Functions Localを起動します。

# docker run --name stepfunctions -p 8083:8083 -d amazon/aws-stepfunctions-local:1.0.0

050fe420af809354937caba0bd1cfd4bf200a79ec2d5af2e10dad7e39a2db132
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
050fe420af80 amazon/aws-stepfunctions-local:1.0.0 "java -jar StepFunct…" 40 seconds ago Up 38 seconds 0.0.0.0:8083->8083/tcp stepfunctions

以下のようなシンプルなステートマシンを実行してみます。

image.png

ASL定義は以下の内容です。


HelloWorld.json

{

"StartAt": "HelloWorld",
"States": {
"HelloWorld": {
"Type": "Pass",
"Result":"Hello World",
"End": true
}
}
}

ローカルで実行されているStep Functionsにアクセスするには AWS CLIを実行する際に

エンドポイントとしてローカルホストを指定します。

# aws stepfunctions create-state-machine \

> --endpoint http://localhost:8083 --definition file://HelloWorld.json \
> --name "HelloWorld" --role-arn "arn:aws:iam::012345678901:role/DummyRole"

{
"stateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorld",
"creationDate": 1549696926.307
}

作成したステートマシンのARNを指定して実行します。

# aws stepfunctions start-execution \

> --endpoint http://localhost:8083 \
> --state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorld \
> --name 01

{
"executionArn": "arn:aws:states:us-east-1:123456789012:execution:HelloWorld:01,
"startDate": 1549697048.004
}

executionArn を指定してステートマシンの状態を確認します。

"status": "SUCCEEDED" となっています。

# aws stepfunctions describe-execution \

> --endpoint http://localhost:8083 \
> --execution-arn arn:aws:states:us-east-1:123456789012:execution:HelloWorld:01

{
"executionArn": "arn:aws:states:us-east-1:123456789012:execution:HelloWorld:01",
"stateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorld",
"name": "01",
"status": "SUCCEEDED",
"startDate": 1549697048.004,
"stopDate": 1549697048.074,
"input": "{}",
"output": "\"Hello World\""
}


SAMやDynamoDB Localと組み合わせて使う

DynamoDB Localとの組み合わせはまだ上手くいっていません。。。


コンテナの再作成

先ほどのHelloWorldで作成したコンテナを破棄します。

# docker stop stepfunctions && docker rm -v stepfunctions

stepfunctions
stepfunctions

他のサービスのローカルエンドポイントを使用するにはコンテナ起動時に環境変数を指定します。

# docker run --name stepfunctions -p 8083:8083 -d \

> -e LAMBDA_ENDPOINT=http://<host_ip>:3001 \
> -e DYNAMODB_ENDPOINT=http://<host_ip>:8000 \
> amazon/aws-stepfunctions-local:1.0.0
83c9f085bbb329ad83751344a2d583a17afd2023d900545e3bd2a2020d18db98
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
83c9f085bbb3 amazon/aws-stepfunctions-local:1.0.0 "java -jar StepFunct…" 4 seconds ago Up 4 seconds 0.0.0.0:8083->8083/tcp stepfunctions


SAMとの連携

AWS SAM CLI を使用してHelloWorldFunctionを作成し、Local lambdaを起動します。

# sam init --runtime python

# cd sam-app
# sam local start-lambda
2019-02-09 08:20:01 Starting the Local Lambda Service. You can now invoke your Lambda Functions defined in your template through the endpoint.
2019-02-09 08:20:01 * Running on http://127.0.0.1:3001/ (Press CTRL+C to quit)

ローカルでLambda関数がinvokeできることを確認しておきます。

# aws lambda invoke \

> --endpoint http://localhost:3001 \
> --function-name HelloWorldFunction outputfile.txt
{
"StatusCode": 200
}

# python -m json.tool < outputfile.txt
{
"body": "{\"message\": \"hello world\"}",
"statusCode": 200
}

以下のようなASL定義を用意します。


HelloWorldFunction.json

{

"StartAt": "HelloWorld",
"States": {
"HelloWorld": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:HelloWorldFunction",
"End": true
}
}
}

HelloWorldFunction.jsonをもとにステートマシンを作成します。

# aws stepfunctions create-state-machine \

> --endpoint http://localhost:8083 --definition file://HelloWorldFunction.json \
> --name "HelloWorldFunction" --role-arn "arn:aws:iam::012345678901:role/DummyRole"

{
"stateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorldFunction",
"creationDate": 1549722746.87
}

起動しますが、失敗します。。。

# aws stepfunctions start-execution \

> --endpoint http://localhost:8083 \
> --state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorldFunction \
> --name 01

{
"executionArn": "arn:aws:states:us-east-1:123456789012:execution:HelloWorldFunction:01",
"startDate": 1549722930.414
}

# aws stepfunctions describe-execution \
> --endpoint http://localhost:8083 \
> --execution-arn arn:aws:states:us-east-1:123456789012:execution:HelloWorldFunction:01

{
"executionArn": "arn:aws:states:us-east-1:123456789012:execution:HelloWorldFunction:01",
"stateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorldFunction",
"name": "01",
"status": "FAILED",
"startDate": 1549722930.414,
"stopDate": 1549722931.197,
"input": "{}"
}

ログを確認すると、StepFunctionsコンテナから、SAMで起動しているLocal Lambdaに

アクセスする際にConnection refused になってしまいます。

# docker logs stepfunctions

2019-02-09 16:36:34.325: arn:aws:states:us-east-1:123456789012:execution:HelloWorldFunction:01
: {"Type":"LambdaFunctionFailed","PreviousEventId":4,"LambdaFunctionFailedEventDetails":
{"Error":"Lambda.SdkClientException","Cause":"Unable to execute HTTP request: Connect to
172.31.2.102:3001 [/172.31.2.102] failed: Connection refused (Connection refused)"}}

Dockerが稼働するホストOS上からは問題なくアクセスできるのですが、

コンテナのbridgeネットワーク経由だとダメなようです。(SAM CLI側の問題)

以下のようにStepFunctionsコンテナをhostネットワークで起動するとローカルホストで接続可能に

なりましたが、bridgeネットワークにも対応してほしいところ。

コンテナをhostネットワークで再作成し、

# docker stop stepfunctions && docker rm -v stepfunctions

stepfunctions
stepfunctions

# docker run --name stepfunctions -d --net host \
> -e LAMBDA_ENDPOINT=http://127.0.0.1:3001 \
> -e DYNAMODB_ENDPOINT=http://127.0.0.1:8000 \
> amazon/aws-stepfunctions-local:1.0.0
5043de7c67e990110caf4d708fd76a476f9866b3b95e52f07a678afdc13f6cc

同じようにステートマシンを作成&実行したところ今度は成功しました。

# aws stepfunctions create-state-machine \

> --endpoint http://localhost:8083 --definition file://HelloWorldFunction.json \
> --name "HelloWorldFunction" --role-arn "arn:aws:iam::012345678901:role/DummyRole"

{
"stateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorldFunction",
"creationDate": 1549903075.235
}

# aws stepfunctions start-execution \
> --endpoint http://localhost:8083 \
> --state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorldFunction \
> --name 01

{
"executionArn": "arn:aws:states:us-east-1:123456789012:execution:HelloWorldFunction:01",
"startDate": 1549903084.221
}

# aws stepfunctions describe-execution \
> --endpoint http://localhost:8083 \
> --execution-arn arn:aws:states:us-east-1:123456789012:execution:HelloWorldFunction:01

{
"executionArn": "arn:aws:states:us-east-1:123456789012:execution:HelloWorldFunction:01",
"stateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorldFunction",
"name": "01",
"status": "SUCCEEDED",
"startDate": 1549903084.221,
"stopDate": 1549903087.51,
"input": "{}",
"output": "{\"statusCode\":200,\"body\":\"{\\\"message\\\": \\\"hello world\\\"}\"}"
}

ちなみにDocker Desktop(Windows/Mac、18.03以降)であれば

http://host.docker.internal を接続先(ローカルエンドポイント)として指定することで

bridgeネットワークであってもコンテナ内からSAM CLIのLocal Lambdaに接続できるようです。

host.docker.internalは内部IPを解決するための特殊なDNS名ですが、

Docker for Linuxでは今のところサポートされていません。

参考:

Container cannot be connected to host network #669

https://github.com/awslabs/aws-sam-cli/issues/669

Cannot connect to sam local from another docker container #196

https://github.com/awslabs/aws-sam-cli/issues/196


DynamoDB Localとの連携

続いてDynamoDB Localを起動します。

# docker run --name dynamodb -p 8000:8000 -d amazon/dynamodb-local:1.11.477

ec860af91a8ab918949e54ff599db479853f610e8b91547d3bb260fbc6f2f052
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec860af91a8a amazon/dynamodb-local:1.11.477 "java -jar DynamoDBL…" 3 seconds ago Up 2 seconds 0.0.0.0:8000->8000/tcp dynamodb
83c9f085bbb3 amazon/aws-stepfunctions-local:1.0.0 "java -jar StepFunct…" 44 minutes ago Up 44 minutes 0.0.0.0:8083->8083/tcp stepfunctions

ここではlocaltable というテーブルを作成しておきます。

# aws dynamodb create-table \

> --endpoint-url http://localhost:8000 --table-name localtable \
> --attribute-definitions AttributeName=id,AttributeType=S \
> --key-schema AttributeName=id,KeyType=HASH \
> --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5
{
"TableDescription": {
"AttributeDefinitions": [
{
"AttributeName": "id",
"AttributeType": "S"
}
],
"TableName": "localtable",
"KeySchema": [
{
"AttributeName": "id",
"KeyType": "HASH"
}
],
"TableStatus": "ACTIVE",
"CreationDateTime": 1549702430.218,
"ProvisionedThroughput": {
"LastIncreaseDateTime": 0.0,
"LastDecreaseDateTime": 0.0,
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5
},
"TableSizeBytes": 0,
"ItemCount": 0,
"TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/localtable",
"BillingModeSummary": {
"BillingMode": "PROVISIONED",
"LastUpdateToPayPerRequestDateTime": 0.0
}
}
}

# aws dynamodb --endpoint-url http://localhost:8000 list-tables
{
"TableNames": [
"localtable"
]
}

inputで指定した内容をDynamoDBに格納するだけのステートマシンを作ってみたいと思います。

以下のようなシンプルな内容です。

image.png

ASL定義は以下のようなものです。


PutDynamoDB.json

{

"StartAt": "PutDynamoDB",
"States": {
"PutDynamoDB": {
"Type": "Task",
"Resource": "arn:aws:states:::dynamodb:putItem",
"Parameters": {
"TableName": "localtable",
"Item": {
"id": {"S.$": "$.id"},
"message": {"S.$": "$.message"}
}
},
"End": true
}
}
}

ステートマシンを作成します。

# aws stepfunctions create-state-machine \

> --endpoint http://localhost:8083 --definition file://PutDynamoDB.json \
> --name "PutDynamoDB" --role-arn "arn:aws:iam::012345678901:role/DummyRole"

{
"stateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:PutDynamoDB",
"creationDate": 1549720535.77
}

input で以下を指定します。


input.json

{

"id": "1",
"message": "Hello World"
}

実行しますが、またしても失敗してしまいました。

# aws stepfunctions --endpoint http://localhost:8083 start-execution \

> --state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:PutDynamoDB \
> --name 01 --input file://input.json

{
"executionArn": "arn:aws:states:us-east-1:123456789012:execution:PutDynamoDB:01",
"startDate": 1549720800.303
}

# aws stepfunctions describe-execution \
> --endpoint http://localhost:8083 \
> --execution-arn arn:aws:states:us-east-1:123456789012:execution:PutDynamoDB:01

{
"executionArn": "arn:aws:states:us-east-1:123456789012:execution:PutDynamoDB:01",
"stateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:PutDynamoDB",
"name": "01",
"status": "FAILED",
"startDate": 1549720800.303,
"stopDate": 1549720801.464,
"input": "{\n \"id\": \"1\",\n \"message\": \"HelloWorld\"\n}\n"
}

ログを確認すると、DynamoDB Localの方ではなく、実アカウントの方を見てしまっています。

# docker logs stepfunctions

2019-02-09 14:00:01.463: arn:aws:states:us-east-1:123456789012:execution:PutDynamoDB:01 :
{"Type":"ExecutionFailed","PreviousEventId":0,"ExecutionFailedEventDetails":
{"Error":"States.Runtime","Cause":"User: arn:aws:sts::<account_id>:assumed-role/<role_name>/
i-0fxxxxxxxxxxxx is not authorized to perform: dynamodb:PutItem on resource:
arn:aws:dynamodb:us-east-1:<account_id>:table/localtable (Service: AmazonDynamoDBv2; Status
Code: 400; Error Code: AccessDeniedException; Request ID:
OSIPIKBRVHMQ5R9A8OCKUD7TIFVV4KQNSO5AEMVJF66Q9ASUAAJG)"}}

DynamoDB Localとの連携方法については引き続き調べたいと思いますが

一部でも参考になれば幸いです。