CloudFormationでStep Functions入門
AWS DVAの出題範囲であるリファクタリングに関する内容です。
Step Functionsはサーバーレスオーケストレーションサービスです。
今回はStep Functions入門ということで、CloudFormationで簡単なステートマシンを作成します。
構築する構成
Step Functionsの1つのステートマシンと、その内部に2つのタスクを作成します。
タスク内で、Lambda関数を実行することとします。
関数は以下の働きを行います。
- Lambda関数1:現在日時情報を取得します。
- Lambda関数2:日時情報からUNIX時間を計算して返します。
関数のランタイム環境はPython3.8とします。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-dva/tree/main/04/002
テンプレートファイルのポイント解説
Step Functions
ステートマシン
Resources:
StateMachine:
Type: AWS::StepFunctions::StateMachine
Properties:
Definition:
Comment: !Sub "${Prefix}-StateMachine"
StartAt: FirstState
States:
FirstState:
Type: Task
Resource: !Ref Function1Arn
Next: LastState
LastState:
Type: Task
Resource: !Ref Function2Arn
End: true
LoggingConfiguration:
Destinations:
- CloudWatchLogsLogGroup:
LogGroupArn: !GetAtt LogGroup.Arn
IncludeExecutionData: true
Level: ALL
RoleArn: !GetAtt StateMachineRole.Arn
StateMachineName: !Ref Prefix
StateMachineType: STANDARD
Code language: YAML (yaml)
Definitionプロパティでステートマシンの構造を定義します。
構造はAmazon ステートメント言語と呼ばれる記法で表現します。
Amazon ステートメント言語はJSONまたはYAML形式で記述可能でして、今回は後者を採用します。
ステートマシン構造は状態(ステート)によって構成されています。
状態には実行するアクションを定義します。
今回の構成ですと、Lambda関数を実行するという1つのアクションを、1つの状態で定義するということです。
また2つのLambda関数を実行しますので、2つの状態を作成するということでもあります。
ステートマシン構造において、2つのフィールドが重要であり、必須でもあります。
1つ目はStartAtフィールドです。
複数の状態を定義した場合、その状態からアクションを開始するかを定めるフィールドです。
今回は現在日時を取得するLambda関数1を実行する状態から開始するように指定します。
2つ目はStatesフィールドです。
ステートマシン内で実行する状態を定義します。
状態を構成するフィールドを確認します。
Typeフィールドは状態の種別を指定します。今回のようにLambda関数実行のような何らかのアクションを行う場合は「Task」を指定します。
Resourceフィールドは実行するリソースのARNを指定します。今回ですと、実行するLambda関数をそれぞれ指定します。
Nextフィールドは次に実行する状態を指定するものです。今回ですと、関数1の後に関数2を実行しますので、関数1の状態に本フィールドを定義し、値に関数2用の状態を指定します。
Endフィールドは最後に実行する状態を示すものです。今回ですと、関数2用の状態を最後に実行しますので、こちらの状態に本フィールドを定義し、値に「true」を指定します。
LoggingConfigurationプロパティで、ステートマシン実行時のログに関する設定を行うことができます。
ログはCloudWatch Logsに保存することができます。
後述のロググループのARNを指定します。
IncludeExecutionDataプロパティに「true」を、Levelプロパティに「ALL」を指定しますと、全ログを収集することができるようになります。
StateMachineTypeプロパティでステートマシンのタイプを指定します。
StandardとExpressの2種類があります。
詳細は以下の公式ページをご確認いただきたいですが、今回はStandardを選択します。
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/concepts-standard-vs-express.html
IAMロール
Resources:
StateMachineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- states.amazonaws.com
Policies:
- PolicyName: !Sub "${Prefix}-InvokeTaskFunctions"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !Ref Function1Arn
- !Ref Function2Arn
- PolicyName: !Sub "${Prefix}-DeliverToCloudWatchLogPolicy"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogDelivery
- logs:GetLogDelivery
- logs:UpdateLogDelivery
- logs:DeleteLogDelivery
- logs:ListLogDeliveries
- logs:PutLogEvents
- logs:PutResourcePolicy
- logs:DescribeResourcePolicies
- logs:DescribeLogGroups
Resource: "*"
Code language: YAML (yaml)
ステートマシンを実行するための権限を与えます。
大別すると2種類あります。
1つ目はLambda関数を実行するために必要な権限です。
2つ目はCloudWatch Logsにログを配信するための権限です。
後者につきましては、以下の公式ページを参照して作成しました。
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/cw-logs.html#cloudwatch-iam-policy
ロググループ
Resources:
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "${Prefix}-StateMachineLogGroup"
Code language: YAML (yaml)
特別な設定は不要です。
シンプルにロググループを作成します。
Lambda関数
インラインに記載したコードからLambda関数を作成します。
詳細につきましては以下のページをご確認ください。
本ページは、実行するコードを中心に取り上げます。
関数1
Resources:
Function1:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import datetime
def lambda_handler(event, context):
now = datetime.datetime.now()
return {
'year': now.year,
'month': now.month,
'day': now.day,
'hour': now.hour,
'minute': now.minute,
'second': now.second,
'microsecond': now.microsecond
}
FunctionName: !Sub "${Prefix}-function-01"
Handler: !Ref Handler
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole.Arn
Code language: YAML (yaml)
現在日時を取得するための関数です。
現在日時を取得後、日時オブジェクトから7つの要素を抽出して返すという内容です。
関数2
Resources:
Function2:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import datetime
import pprint
import time
def lambda_handler(event, context):
pprint.pprint(event)
year = event['year']
month = event['month']
day = event['day']
hour = event['hour']
minute = event['minute']
second = event['second']
microsecond = event['microsecond']
dt = datetime.datetime(year, month, day, hour, minute, second, microsecond)
epoch_time = int(time.mktime(dt.timetuple()))
print(epoch_time)
return epoch_time
FunctionName: !Sub "${Prefix}-function-02"
Handler: !Ref Handler
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole.Arn
Code language: YAML (yaml)
日時情報からUNIX時間を取得するための関数です。
Pythonですと、eventオブジェクトから関数実行時の引数にアクセスすることができます。
今回は関数1が返す7つの値を取得するように設定します。
引数の取得後は、datetimeオブジェクトを作成後、これからUNIX時間を計算して返すという処理を行います。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- Step Functionsのステートマシン:dva-04-002
- Lambda関数1:dva-04-002-function-01
- Lambda関数2:dva-04-002-function-02
- CloudWatch Logsロググループ:dva-04-002-StateMachineLogGroup
AWS Management Consoleからステートマシンの作成状況を確認します。
正常に作成されています。
次にLambda関数も確認します。
こちらも正常です。
動作確認
準備が整いましたので、ステートマシンの実行を行います。
AWS Management Consoleから実行する場合は、「Start execution」を押下します。
実行時のオプションを設定するページが表示されます。
Inputは空で「Start execution」で押下します。
ステートマシンが開始されます。
Execution Statusが「Running」となっています。
ステートマシンが実行中ということです。
もう少し待機します。
Execution Statusが「Succeeded」となっています。
ステートマシンが完了しました。
実行ログが確認できます。
Stepの列を見ると、ステートマシン開始から終了までに、2つの状態に遷移していることがわかります。
それぞれLambda関数1および2の状態です。
つまり2つの関数が正常に実行されたということです。
ステートマシン実行時の詳細なログを確認します。
まずステートマシン開始および関数1実行時のログから確認します。
ExecutionStartのログを見ると、inputが空であることがわかります。
これはステートマシン実行時のオプションで、このパラメータを空にしたためです。
LambdaFunctionScheduledのログを見ると、resourceで実行するLambda関数1が指定されていることがわかります。
またステートマシンのinputが空だったため、関数実行時の引数としてのinputも空になりました。
TaskStateExitedのログを見ると、outputに関数1で返された結果が設定されています。
現在日時を構成する7つのデータが確認できます。
次に関数2および実行およびステートマシン終了時のログを確認します。
TaskStateEnterdのログを見ると、inputに関数1で返された結果が確認できます。
LambdaFunctionScheduledのログを見ると、resourceで実行するLambda関数2が指定されていることがわかります。
またステートマシンのinputが設定されていますので、これが関数実行時の引数に設定されています。
TaskStateExitedのログを見ると、outputに関数2で返された結果が設定されています。
関数1から渡された日時、つまり現在日時のUNIX時間です。
ExecutionSuccededのログを見ると、こちらのoutputにも関数2の結果が設定されていることがわかります。
最後にCloudWatch Logsロググループに配信されたログも確認します。
ログストリームが作成されています。
先ほど確認したログがCloudWatch Logsロググループにも配信されていることがわかります。
まとめ
Step Functions入門ということで、CloudFormationを使ってシンプルなStep Functions構成を構築しました。