ようへいの日々精進XP

よかろうもん

AWS Step Functions の Activity Worker を "郷" で実装してみた系

どうも、郷です。

モチベーション

  • むかーし、むかーし、1 台の EC2 cron で動かしている複数のバッチ処理があったとさ
  • 村人(むらびと)たちはバッチ処理を動かす時だけ EC2 を起動したいと考えたとさ
  • 村人たちの思いを汲んだ禿兵衛は EC2 は Lambda で起動してから、バッチを実行しようと考えたとさ
  • ところが、禿兵衛は EC2 が正常に起動したことを正確に判断したり、バッチ処理の重複起動やハンドリングに頭を悩ましておりましたさ

そして、ちゃんと前後の処理をハンドリングしつつ、処理の流れをコード化(可視化)出来ればなあと黄昏れておりましたら…山の向こうから「AWS Step Functions というツールがあるばってん、こんツールはくさ、Activity を使えばくさ、EC2 やオンプレのタスクも制御出来るちゃんねー」という声を聞くのであった…

ということで、EC2 で動かしているバッチ処理を Activity で制御することを想定して、Golang で Worker を実装して Acitivity の挙動についてチュートリアルしてみたメモでござる。

AWS Step Functions とは

aws.amazon.com

Step Functions の詳しい説明については、以下の資料がとても参考になりました。

www.slideshare.net

有難うございます!!

郷で Activity Worker を実装

Activity

docs.aws.amazon.com

Activities are an AWS Step Functions concept that refers to a task to be performed by a worker that can be hosted on EC2, ECS, mobile devices—basically anywhere.

超ざっくりだけど…

  • Activity とは EC2 や ECS 等、オンプレミスのサーバーでも動く Worker によって実行されるタスクのこと
  • AWS SDKAWS CLI で Worker を実装出来る
  • Worker は Activity の ARN を指定して起動すると Activity に対してポーリングを行う

Activity は AWS CLI では以下のように作成します。

$ aws \
  --profile oreno-profile --region ap-northeast-1 \
    stepfunctions create-activity \
      --name=OrenoActivity

以下のように出力されます。

{
    "creationDate": 1500076193.843,
    "activityArn": "arn:aws:states:ap-northeast-1:012345678912:activity:OrenoActivity"
}

State Machine には以下のように指定します。

{
  "Comment": "Golang Demo",
  "StartAt": "Godesu1",
  "States": {
    "Godesu1": {
      "Type": "Task",
      "Resource": "arn:aws:states:ap-northeast-1:012345678912:activity:OrenoActivity",
      "Next": "wait_using_seconds"
    }
  }
}

参考

こちらこちらを目一杯参考にさせて頂きました。有難うございます!!

コード

gist.github.com

こうやって使うことを想定

./taskRunner -arn=${Activity ARN} -command=${実行したいコマンド}

Demo

Activity Worker を動かす環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G1421

$ go version
go version go1.8.3 darwin/amd64

$ go build taskRunner.go

State Machine: ビジュアルワークフロー

State Machine とは State をつなげたフローのことで、以下のようなフローで動かしてみます。

f:id:inokara:20170715075537p:plain

State Name State Type State で実行したいコマンド
Godesu1 Task demo01.sh
wait_using_seconds Wait 10 秒待ってから State: Godesu2 を実行
Godesu2 Task demo02.sh

State Machine: コード

{
  "Comment": "Golang Demo",
  "StartAt": "Godesu1",
  "States": {
    "Godesu1": {
      "Type": "Task",
      "Resource": "arn:aws:states:ap-northeast-1:123456789012:activity:OrenoFirstActivity",
      "Next": "wait_using_seconds"
    },
    "wait_using_seconds": {
      "Type": "Wait",
      "Seconds": 10,
      "Next": "Godesu2"
    },
    "Godesu2": {
      "Type": "Task",
      "Resource": "arn:aws:states:ap-northeast-1:123456789012:activity:OrenoSecondActivity",
      "End": true
    }
  }
}

State1 : Godesu1 で実行するコマンド

$ cat demo01.sh
#!/usr/bin/env bash

for i in $(seq 1 2 10);
do
  echo $i
done

Activity Worker を以下のように実行して待機させておきます。

./taskRunner -arn=arn:aws:states:ap-northeast-1:123456789012:activity:OrenoFirstActivity -command="./demo01.sh"

以下のように出力されます。

$ ./taskRunner -arn=arn:aws:states:ap-northeast-1:123456789012:activity:OrenoFirstActivity -command="./demo01.sh"
2017/07/15 08:00:49 taskRunner Started.

State2 : Godesu2 で実行するコマンド

$ cat demo02.sh
#!/usr/bin/env bash

for i in $(seq 2 2 10);
do
  echo $i
done

Activity Worker を以下のように実行して待機させておきます。

./taskRunner -arn=arn:aws:states:ap-northeast-1:123456789012:activity:OrenoSecondActivity -command="./demo02.sh"

以下のように出力されます。

$ ./taskRunner -arn=arn:aws:states:ap-northeast-1:123456789012:activity:OrenoSecondActivity -command="./demo02.sh"
2017/07/15 08:00:55 taskRunner Started.

State Machine の実行

以下のように AWS CLI で実行します。

$ aws \
  --profile oreno-profile --region ap-northeast-1 \
    stepfunctions start-execution \
      --state-machine-arn=arn:aws:states:ap-northeast-1:012345678912:stateMachine:DemoStateMachine-Go-3

以下のようにレスポンスが返ってきます。

{
    "startDate": 1500075053.349,
    "executionArn": "arn:aws:states:ap-northeast-1:012345678912:execution:DemoStateMachine-Go-3:859949c4-b3a4-4b7c-84ed-60f555e859e6"
}

そして、それぞれの Activity Worker では以下のように出力されています。

f:id:inokara:20170715083348p:plain

それぞれ別の Activity を利用しているので、順番も制御出来ている(Godesu1 → Godesu2 の順番)ことが判ります。

f:id:inokara:20170715092126p:plain

マネジメントコンソールでも上図のように State Machine の処理が正常に終了していることが判ります。

このフロー図を見ているだけでウキウキしますね。

ということで

Step Functions = Lambda という印象がありましたが、Activity を使うことで従来のバッチ処理等について変更を最小限に処理フロー(State Machine)に組み込めるのはとても嬉しいです。しかし、こちらでも言及されている通り、Activity は Worker を実装する必要があったり、Worker の運用管理が必要になる点は注意が必要です。

今回、禿兵衛の目の前にあるシチュエーションの場合、Activity Worker は EC2 が起動したタイミングで必ず起動することを保証する必要があったりしますが、それ自体も State の一つとして Lambda ファンクションに組み込んでしまうのもありかなと思ったりしています。

Step Functions とても面白いサービスで好きになっちゃいました。