Docker で Step Functions Local の実行環境を構築する
最近、車を購入したので、色々なところに遠出をしています。エンジニアの内山です。
今回は、Step Functions をローカルPC内で実行する環境の構築について、ご紹介します。
概要
Step Functions は、複数の Lambda 関数を組み合わせて機能を実装できるようにするサービスです。
ループや分岐などのロジックを組むことができます。
ロジックが複雑になってくると、ローカルPCで動作確認したいと思うようになってきます。
ローカルPCで動作させるために Step Functions Local というツールが公式で用意されています。
今回は、Docker と Step Functions Local を用いて、動作チェック環境をローカルPC(Mac)上に構築する方法をご紹介します。
さっさと試したい方は以下のリポジトリをご参照ください。
https://github.com/memememomo/step-functions-local-test
構成
上記のような環境を構築します。設定するコンテナとしては以下の二種類があります。
- SAM Local用のコンテナ
- Step Functions用のコンテナ
SAM Local は、ローカルPC上で Lambda 関数を実行するツールです。Lambda 関数の実行時には、実行用のコンテナを作成します。注意点として、Docker Compose で作成したネットワーク(net)を設定する必要がある ということです。このことについては後述します。
また、図には含めていませんが、Goプログラムをビルドする用のコンテナも作成しています。
各 Dockerfile の設定
各コンテナ用の Dockerfile を設定していきます。
SAM Local 用の Dockerfile
SAM Local 用のコンテナでは、Python がインストールされているイメージを使用します。
Dockerfile では、SAM Local 用のコマンド(aws-sam-cli)をインストールするように設定しています。
# Dockerfile_sam
FROM python:3.5-alpine
ENV PYTHONUSERBASE=/usr/local
RUN apk add --no-cache py-pip git bash gcc libc-dev &&
pip install --upgrade pip &&
pip install --user awscli==1.16.76 aws-sam-cli==0.9.0
WORKDIR /var/opt
COPY . /var/opt/
EXPOSE 3001
Step Functions Local 用の Dockerfile
Step Functions Local 用のコンテナでは、公式で用意されているイメージを使用します。
# Dockerfile_stepfunctions
FROM amazon/aws-stepfunctions-local
EXPOSE 8083
Goプログラムビルド用の Dockerfile
Goプログラムビルド用のコンテナでは、Goがインストールされているイメージを使用します。
Dockerfile では、必要なツールをインストールするように設定しています。
# Dockerfile_go
FROM golang:1.11.5-alpine
RUN apk add --no-cache git bash make curl gcc libc-dev openssl &&
go get -u github.com/golang/dep/cmd/dep
WORKDIR /go/src/step-functions-local-test
COPY . /go/src/step-functions-local-test
RUN dep ensure
RUN cd ./handlers/helloworld/ && CGO_ENABLED=0 GOOS=linux go build -v -installsuffix cgo -o main .
各種スクリプト
Docker コンテナ内で実行するスクリプトを作成していきます。
Goプログラムビルドスクリプト
go-build.sh
という名前で、Goプログラムビルドスクリプトを作成します。
内容としては、依存モジュールのインストールとコンパイルコマンドの実行を行っています。
#!/usr/bin/env bash
dep ensure
cd ./handlers/helloworld/ && CGO_ENABLED=0 GOOS=linux go build -v -installsuffix cgo -o main .
SAM Local 起動スクリプト
start-lambda.sh
という名前で、SAM Local を起動するスクリプトを作成します。
先述したとおり、Lambda関数実行用コンテナにDockerのネットワークを設定する ようにします。これが行われていないと、SAM LocalのLambdaから他のコンテナ(DynamoDB Localなど)にアクセスすることができなくなります。
--docker-network
オプションで、Dockerのネットワークを設定しています。
#!/bin/bash
sam local start-lambda
--docker-volume-basedir "${VOLUME}"
--docker-network step-functions-local-test_net
--host 0.0.0.0
--template template.yml
Lambda関数用のプログラム
handlers/helloworld/main.go
という名前のファイルで、Lambda関数用のGoプログラムを作成します。
今回は、"Hello World!"という文字列を返すだけのLambda関数を実装しています。
package main
import "github.com/aws/aws-lambda-go/lambda"
func handler() (string, error) {
return "Hello World!", nil
}
func main() {
lambda.Start(handler)
}
Gopkg.toml
という名前のファイルで、依存モジュールを記述します。
[prune]
go-tests = true
unused-packages = true
[[constraint]]
name = "github.com/aws/aws-lambda-go"
version = "1.2.0"
SAM テンプレート(template.yml)の設定
SAM Local で読み込ませるテンプレート(template.yml)を作成します。
"HelloWorld"という名前のLambda関数を作成し、先程作成したプログラムを実行するように設定しています。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Runtime: go1.x
Timeout: 900
Resources:
HelloWorld:
Properties:
CodeUri: ./handlers/helloworld
FunctionName: HelloWorld
Handler: main
Type: AWS::Serverless::Function
Step Functions Local の設定
Step Functions Local に読み込ませるための設定ファイルを作成します。
aws-stepfunctions-local-credentials.txt
というファイル名にします。
今回は Lambda のエンドポイントを指定しています(http://sam-local:3001/)。
AWS_DEFAULT_REGION=ap-northeast-1
AWS_ACCESS_KEY_ID=dummy
AWS_SECRET_ACCESS_KEY=dummy
WAIT_TIME_SCALE=10
LAMBDA_ENDPOINT=http://sam-local:3001
BATCH_ENDPOINT=
DYNAMODB_ENDPOINT=
ECS_ENDPOINT=
GLUE_ENDPOINT=
SAGE_MAKER_ENDPOINT=
SQS_ENDPOINT=
SNS_ENDPOINT=
# https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/sfn-local-config-options.html#docker-credentials
Docker Composeの設定
Docker Composeで複数のDockerコンテナを連携させる設定を記述します。
docker-compose.yml
というファイル名にします。
version: '3'
services:
sam-local:
build:
context: ./
dockerfile: ./Dockerfile_sam
command: ./start-lambda.sh
volumes:
- .:/var/opt/
- /var/run/docker.sock:/var/run/docker.sock
environment:
- VOLUME=$PWD
env_file:
- .env
networks:
- net
step-functions-local:
build:
context: ./
dockerfile: ./Dockerfile_stepfunctions
ports:
- '8083:8083'
env_file:
- aws-stepfunctions-local-credentials.txt
depends_on:
- sam-local
networks:
- net
# Goビルド用
go-build:
build:
context: ./
dockerfile: ./Dockerfile_go
command: ./go-build.sh
volumes:
- .:/go/src/step-functions-local-test/:cached
networks:
net:
driver: bridge
以上で、設定が一通りできました。
コンテナの起動
以下のコマンドでコンテナを起動します。
# ビルド
$ COMPOSE_PROJECT_NAME=step-functions-local-test docker-compose build
# Lambdaハンドラーのビルド
$ COMPOSE_PROJECT_NAME=step-functions-local-test docker-compose run go-build ./go-build.sh
# サーバ立ち上げ
$ COMPOSE_PROJECT_NAME=step-functions-local-test docker-compose up
docker ps
で確認すると、コンテナが起動していることを確認できると思います。
Step Functions の実行
起動している Step Functions コンテナ内に StateMachine を作成して、実行します。
コンテナを立ち上げたコンソールとは別のコンソールで、以下のコマンドを実行します。
# StateMachineを作成
$ aws stepfunctions --endpoint http://localhost:8083 create-state-machine --definition "{
"Comment": "A Hello World example of the Amazon States Language using an AWS Lambda Local function",
"StartAt": "HelloWorld",
"States": {
"HelloWorld": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:HelloWorld",
"End": true
}
}
}
}}" --name "HelloWorld" --role-arn "arn:aws:iam::012345678901:role/DummyRole"
# StateMachineを実行
$ aws stepfunctions --endpoint http://localhost:8083 start-execution --state-machine arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorld
コンテナを立ち上げたコンソールで、以下のような出力が表示されたら成功です。
step-functions-local_1 | 2019-07-31 00:49:58.444: [200] StartExecution <= {"sdkResponseMetadata":null,"sdkHttpMetadata":null,"executionArn":"arn:aws:states:ap-northeast-1:123456789012:execution:HelloWorld:9f49a299-36f7-4b22-95b1-fb7ae816b113","startDate":1564534198424}
step-functions-local_1 | 2019-07-31 00:49:58.483: arn:aws:states:ap-northeast-1:123456789012:execution:HelloWorld:9f49a299-36f7-4b22-95b1-fb7ae816b113 : {"Type":"ExecutionStarted","PreviousEventId":0,"ExecutionStartedEventDetails":{"Input":"{}","RoleArn":"arn:aws:iam::012345678901:role/DummyRole"}}
step-functions-local_1 | 2019-07-31 00:49:58.487: arn:aws:states:ap-northeast-1:123456789012:execution:HelloWorld:9f49a299-36f7-4b22-95b1-fb7ae816b113 : {"Type":"TaskStateEntered","PreviousEventId":0,"StateEnteredEventDetails":{"Name":"HelloWorld","Input":"{}"}}
step-functions-local_1 | 2019-07-31 00:49:58.507: arn:aws:states:ap-northeast-1:123456789012:execution:HelloWorld:9f49a299-36f7-4b22-95b1-fb7ae816b113 : {"Type":"LambdaFunctionScheduled","PreviousEventId":2,"LambdaFunctionScheduledEventDetails":{"Resource":"arn:aws:lambda:us-east-1:123456789012:function:HelloWorld","Input":"{}"}}
step-functions-local_1 | 2019-07-31 00:49:58.508: arn:aws:states:ap-northeast-1:123456789012:execution:HelloWorld:9f49a299-36f7-4b22-95b1-fb7ae816b113 : {"Type":"LambdaFunctionStarted","PreviousEventId":3}
sam-local_1 | 2019-07-31 00:49:58 Invoking main (go1.x)
sam-local_1 |
sam-local_1 | Fetching lambci/lambda:go1.x Docker container image......
sam-local_1 | 2019-07-31 00:50:01 Mounting /Users/uchiko/project/step-functions-local-test/handlers/helloworld as /var/task:ro inside runtime container
sam-local_1 | START RequestId: bf7b22bb-7cc7-1703-a07b-f94434457538 Version: $LATEST
sam-local_1 | END RequestId: bf7b22bb-7cc7-1703-a07b-f94434457538
sam-local_1 | REPORT RequestId: bf7b22bb-7cc7-1703-a07b-f94434457538 Duration: 1.21 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 5 MB
sam-local_1 | 2019-07-31 00:50:03 172.19.0.3 - - [31/Jul/2019 00:50:03] "POST /2015-03-31/functions/HelloWorld/invocations HTTP/1.1" 200 -
step-functions-local_1 | 2019-07-31 00:50:03.689: arn:aws:states:ap-northeast-1:123456789012:execution:HelloWorld:9f49a299-36f7-4b22-95b1-fb7ae816b113 : {"Type":"LambdaFunctionSucceeded","PreviousEventId":4,"LambdaFunctionSucceededEventDetails":{"Output":""Hello World!""}}
step-functions-local_1 | 2019-07-31 00:50:03.691: arn:aws:states:ap-northeast-1:123456789012:execution:HelloWorld:9f49a299-36f7-4b22-95b1-fb7ae816b113 : {"Type":"TaskStateExited","PreviousEventId":5,"StateExitedEventDetails":{"Name":"HelloWorld","Output":""Hello World!""}}
step-functions-local_1 | 2019-07-31 00:50:03.695: arn:aws:states:ap-northeast-1:123456789012:execution:HelloWorld:9f49a299-36f7-4b22-95b1-fb7ae816b113 : {"Type":"ExecutionSucceeded","PreviousEventId":0,"ExecutionSucceededEventDetails":{"Output":""Hello World!""}}
まとめ
ローカルPC上で Step Functions の動作チェックを行える環境の構築方法をご紹介しました。
Step Functions のロジックは複雑になりがちなので、ぜひ取り入れてみてください。
なお、MMMのDockerコンテナへの取り組みを以下で紹介しています。ぜひ合わせてご覧ください。
・Dockerコンテナ基盤(AWS Fargate/Amazon ECS)