⚙️

AWS StepFunctionsで始めるワークフローエンジン

2023/09/27に公開

はじめに

この記事ではAWS StepFunctionsをワークフローエンジンとしてECSタスクを実行する方法を紹介したいと思います。
AWS StepFunctionsではECSタスク以外にもAWSのリソースを実行するワークフローを作成することができ、例えばEC2のcronで実行時間を指定することで順序だてていたような処理をStepFunctionsのワークフローで実行できるようになります。

ワークフローのように使用しているcron
0 9 * * * task1.sh
0 11 * * * task2.sh

上記のcronでは、task1が1時間程度で終わるので、確実に終わっているであろう2時間後に後続処理のtask2を実行しているため、後続処理の開始に余計な待ち時間が発生していて総処理時間が長くなることや、task1がエラーで終了した際でもtask2が実行される等の問題があります。

StepFunctionsのワークフロー


一方StepFunctionsではtask1が終わってからすぐにtask2を実行できたり、エラーが起きた場合に後続処理を実行しないこと等を設定することができ効率的で安全なワークフローの運用を行うことができます。

ECSタスクの作成

ECRリポジトリの作成

まずは、ECSタスクで使用するECRのリポジトリの作成を行います。
AWSのコンソールでECRのRepositoriesから「step-functions-sample」という名称でリポジトリを作成します。

実行コードの作成

StepFunctionsがタスクを実行できたかわかりやすいように、slackにメッセージを送る処理をタスク化します。

ディレクトリ構成

├── Pipfile
├── Pipfile.lock
├── aws
│   └── ecr
│       ├── Dockerfile
│       ├── build.sh
│       └── start.sh
└── src
    └── notify_slack.py

src/notify_slack.py

import slackweb

slack = slackweb.Slack(url="通知を行いたいslackのWebhookのURL")
slack.notify(text="タスクの実行")

Pipfile

[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
slackweb = "*"

[dev-packages]

[requires]
python_version = "3.9"

[scripts]
notify_slack = "python src/notify_slack.py"

aws/ecr/build.sh ※docker imageを作成しECRにpushする

rm -rf ./tmp
mkdir ./tmp
cp -r ../../src ./tmp/
cp ../../Pipfile ./tmp/
cp ../../Pipfile.lock ./tmp/
docker build -t {image名} .
aws ecr get-login-password --region {リージョン} | docker login --username AWS --password-stdin {アカウントID}.dkr.ecr.{リージョン}.amazonaws.com
docker tag {image名}:latest {アカウントID}.dkr.ecr.{リージョン}.amazonaws.com/step-functions-sample:latest
docker push {アカウントID}.dkr.ecr.{リージョン}.amazonaws.com/step-functions-sample:latest

aws/ecr/Dockerfile

FROM python:3.9-slim-buster
WORKDIR /app
ENV TZ=Asia/Tokyo
COPY ./tmp /app
COPY ./start.sh /app/start.sh
RUN apt-get update && \
    apt-get -y install gcc libmariadb-dev wget
RUN python -m pip install --upgrade pip
RUN pip install pipenv
RUN pipenv install
RUN mkdir /app/tmp
CMD ["/app/start.sh"]

aws/ecr/start.sh

#!/bin/bash
pipenv run notify_slack

ECRへのdocker imageのpush

上記のコードが作成できたらaws/ecr/build.shを実行しECRにdocker imageをpushします。

タスク定義の作成

次にECRのリポジトリを起動するタスク定義を作成します。
AWSのコンソールでECSのタスク定義を開き、下記設定でタスク定義を作成を行います。
タスク内で他のAWSのリソースを使用しないため、タスクロールはなしとしています。

  • タスク定義ファミリー
    • step-functions-sample
  • 起動タイプ
    • AWS Fargate
  • タスクロール
    • なし
  • タスク実行ロール
    • 新しいロールの作成
  • コンテナ
    • 名前
      • step-functions-sample
    • イメージURI
      • 先ほど作成したECRリポジトリのURI

クラスターの作成

作成したタスク定義を実行するためのクラスターをAWSのコンソールでECSのクラスターを開き「step-functions-sample」という名称でFargate用のクラスターを作成します。

StepFunctionsの設定

ステートマシンの作成

ECSのTask一つを実行するだけのフローを作っていきます。
左側のメニューからAmazon ECSの「Run Task」を探し、startの次となるように配置します。

先ほど作成したECSのTaskを実行するように「Run Task」のアクションの設定を変更します。

  • APIパラメータ
{
  "LaunchType": "FARGATE",
  "Cluster": {クラスターのARN},
  "TaskDefinition": {タスク定義のARN},
  "NetworkConfiguration": {
    "AwsvpcConfiguration": {
      "AssignPublicIp": "ENABLED",
      "SecurityGroups": [
        {セキュリティグループのARN}
      ],
      "Subnets": [
        {サブネット1のARN},
        {サブネット2のARN}
      ]
    }
  }
}

デフォルトのAPIパラメータのプロパティにはECS Taskを動かすネットワーク情報の部分がないためNetworkConfigurationのプロパティを追加する必要があります。
パラメータの入力ができたら作成ボタンでステートマシンを作成を完了させます。

ステートマシンの実行

実行メニューにある「実行を開始」ボタンを押下し、実行します。
ステートマシンに実行時の引数をJSONで渡すことができますが、今回は内部で引数を使用しないため、デフォルトのものを使用します。

slackの通知が行われるかと思いきや、ECSタスクの実行でエラーとなりました。

原因は、ステートマシンのroleにECSタスクの実行roleのPassRoleがないということだったのでIAMでPassRoleの付与を行います。

ステートマシンのroleにPassRoleの付与

ステートマシンのroleを開き、許可を追加からインラインポリシーの作成を行います。

  • インラインポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": {ECSの実行roleのARN}
    }
  ]
}

ステートマシンの再実行

ECSタスクが実行され、ステートマシンの実行が正常に完了しました。

slack通知も成功しました!

まとめ

今回は、一つのECSタスクを実行するだけの単純なワークフローをStepFunctionsで作成しました。
この時点ではStepFunctionsの強みは十分に活かせていませんが、ここから前後の処理を追加したいというニーズがあった場合フローの中に新たなタスクを追加するだけでワークフローを作成することが可能になります。
また、StepFunctions内でタスクのエラーハンドリング(再実行や、フロー自体の終了)を行うことができるため、安全なワークフローを手軽に実装することができます。

CareNet Engineers

Discussion