Slide 1

Slide 1 text

AWS CDK + Step Functions ⼊⾨ 2020/7/2 Developers.IO 2020 CONNECT クラスメソッド株式会社 CX事業本部 佐藤直哉

Slide 2

Slide 2 text

2 このセッションについて • AWS Step Functionsの簡単な概要 • AWS CDKの簡単な概要 • AWS CDK と Step Functionsを使ったステートマシンの 実装⽅法について • ⾔語はTypeScript、実⾏基盤は AWS Lambdaを前提とし ています。

Slide 3

Slide 3 text

3 ⾃⼰紹介 クラスメソッド株式会社 CX事業本部 MAD(Modern Application Development)チーム サーバーサイドエンジニア 佐藤直哉(Sato Naoya)

Slide 4

Slide 4 text

4 アジェンダ l AWS Step Functionsとは l AWS CDKの登場 l ステートマシンにおけるAWS CDKの実装⽅法につ いて

Slide 5

Slide 5 text

5 AWS Step Functionsとは

Slide 6

Slide 6 text

6 AWS Step Functionsとは

Slide 7

Slide 7 text

7 AWS StepFunctionsとは l サーバーレスなワークフロー処理を作成することが できるサービス l ステートマシン l ワークフロー全体の処理のこと l ステートマシン⼀つ⼀つの処理単位のことをStateと呼ぶ l ユースケース l 複数のLambda関数を順次的に処理したい l アプリケーションのジョブ機能として使う l Lambda単体では処理しきれない⼤容量ファイルを並列 に処理する

Slide 8

Slide 8 text

8 ステートマシンの構成要素について

Slide 9

Slide 9 text

9 Stateとは l ステートマシンにおける 処理の単位のこと l 全部で7つの種類があり ます l Task l Wait l Parallel l Pass l Choice l Succeed l Fail

Slide 10

Slide 10 text

10 Task 単⼀の処理を⾏う基本的な State l タスクの実⾏基盤として、AWS Lambda、ECS等を選択するこ とができます l 基本的には、このTaskを組み合 わせてステートマシンを構成し ていくことになります start end 最初の処理 次の処理

Slide 11

Slide 11 text

11 Pass 前stateのoutputをそのままパ ススルーするstate l デバッグ⽤途で使うことが多い l 前stateのoutput JSONがその まま流れてくるので、その値を 確認したりするのに使う l とりあえず、全体のステートマ シンをPassで定義して、あとか らPassをTaskに置き換えていく という⽅法も取れる start end 処理 Pass

Slide 12

Slide 12 text

12 Wait 最初の処理 Start End 10秒待つ 最後の処理 指定された時間を待機する State l StateとStateの間に⼊り、指定 された時間待機する

Slide 13

Slide 13 text

13 Parallel 並列に処理を実⾏するState l 複数のTask(Lambda)を並列 に処理させることができる l Parallel内の全てのStateが実⾏ されたら処理3に遷移する 並列処理1 並列処理2 start end 処理3

Slide 14

Slide 14 text

14 Choice 条件により分岐するState l IF⽂のように条件によって分岐 させるState l 処理1のoutputを受け取り、 ChoiceStateはその値により、 分岐させます 処理1 ChoiceState 処理2 処理3 処理4 start end

Slide 15

Slide 15 text

15 Succeed, Fail 成功か失敗かを定義するState l 前のChoiceと組み合わせて、 Choiceの結果に合わせて、ス テートマシンが成功したか失敗 したかをマークするなどで使う l Succeed、Failに到達した段階 でステートマシンは終了する ChoiceState 処理 Succeed 処理 Fail start end

Slide 16

Slide 16 text

16 Map 動的並列処理を⾏うState l inputに配列を受け取り、配列 の数だけ処理を繰り返すState start 処理1 処理2

Slide 17

Slide 17 text

17 ASL(Amazon State Language) ASLと呼ばれるJSONベー スの⾔語を使ってステー トマシンを定義

Slide 18

Slide 18 text

18 ASLの⾟み l JSON形式の⾔語のため、あ る程度処理が複雑化してくる と、処理を追うのが⼤変にな る l プログラミング⾔語ではない ため、ある程度の複雑さがあ ると可読性が悪くなってしま う

Slide 19

Slide 19 text

19 そこでAWS CDKが登場します

Slide 20

Slide 20 text

20 AWS CDKとは

Slide 21

Slide 21 text

21 AWS CDKとは l TypeScriptやPythonといったプログラミング⾔語を 使ってAWSリソースを定義する事ができるツール l Constructというライブラリを使うことで、 CloudFormationよりも少ない⾏数で記述が可能

Slide 22

Slide 22 text

22 Constructとは https://docs.aws.amazon.com/cdk/api/latest/docs/aws-construct-library.html

Slide 23

Slide 23 text

23 AWS CDKのメリット l 使い慣れたプログラミング⾔語でAWSリソースを定 義することができる l IDEの恩恵を受けることができる l CloudFormationや独⾃⾔語(今回の場合だとASL) がConstructによって抽象化されているため、通常よ りも少ない記述量でリソースを定義することができ ます

Slide 24

Slide 24 text

24 AWS CDKにおけるステートマシンの実装⽅ 法について

Slide 25

Slide 25 text

25 AWS CDKプロジェクトの作成

Slide 26

Slide 26 text

26 AWS CDKのインストール npm install –g aws-cdk

Slide 27

Slide 27 text

27 CDKプロジェクトの作成 mkdir stepfunctions-sample cd stepfunction-sample cdk init app –language=typescript

Slide 28

Slide 28 text

28 ディレクトリ構成

Slide 29

Slide 29 text

29 AWS CDK Constructのインストール npm install –d @aws-cdk/aws-lambda npm install –d @aws-cdk/aws-stepfunctions npm install –d @aws-cdk/aws-stepfunction-tasks

Slide 30

Slide 30 text

30 Lambda(タスク)のサンプルコード handlers/task.ts export async function handler(event: any): Promise { console.log(JSON.stringify(event)); return { title: 'Hello’, message: 'World’, }; } type TaskResponse = { title: string; message: string; }

Slide 31

Slide 31 text

31 スタックのコード lib/stepfunction-sample-stack.ts import * as cdk from '@aws-cdk/core'; import * as lambda from '@aws-cdk/aws-lambda'; import * as sfn from '@aws-cdk/aws-stepfunctions'; import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; export class StepfunctionsSampleStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // ここにAWSリソースを定義していく } }

Slide 32

Slide 32 text

32 スタックのコード lib/stepfunction-sample-stack.ts import * as cdk from '@aws-cdk/core'; import * as lambda from '@aws-cdk/aws-lambda'; import * as sfn from '@aws-cdk/aws-stepfunctions'; import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; export class StepfunctionsSampleStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); } } 先程インストールした、Constructライブラ リをimportします。

Slide 33

Slide 33 text

33 ステートマシンの基本的な実装⽅法

Slide 34

Slide 34 text

34 Taskの順次処理(Next) l 基本的なNextでタスク同⼠を順 次処理させる⽅法です。 l AWS CDKでは .next() を使う ことで簡単にタスクをつなげる ことができます。 start end 最初の処理 次の処理

Slide 35

Slide 35 text

35 Taskの順次処理(Next) lib/stepfunctions-sample-stack.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler’, }); const firstState = new tasks.LambdaInvoke(this, '最初の処理', { lambdaFunction: taskFn, outputPath: '$.Payload’, }); const secondState = new tasks.LambdaInvoke(this, '次の処理', { inputPath: '$.message’, lambdaFunction: taskFn, }); const definition = firstState.next(secondState); new sfn.StateMachine(this, 'stateMachine', { definition: definition, });

Slide 36

Slide 36 text

36 Taskの順次処理(Next) lib/stepfunctions-sample.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler’, }); const firstState = new tasks.LambdaInvoke(this, '最初の処理', { lambdaFunction: taskFn, outputPath: '$.Payload’, }); const secondState = new tasks.LambdaInvoke(this, '次の処理', { inputPath: '$.message’, lambdaFunction: taskFn, }); const definition = firstState.next(secondState); new sfn.StateMachine(this, 'stateMachine', { definition: definition, }); タスクの処理を実⾏する Lambda関数を作成します

Slide 37

Slide 37 text

37 Taskの順次処理(Next) lib/stepfunctions-sample.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler’, }); const firstState = new tasks.LambdaInvoke(this, '最初の処理', { lambdaFunction: taskFn, outputPath: '$.Payload’, }); const secondState = new tasks.LambdaInvoke(this, '次の処理', { inputPath: '$.message’, lambdaFunction: taskFn, }); const definition = firstState.next(secondState); new sfn.StateMachine(this, 'stateMachine', { definition: definition, }); Taskを定義します。

Slide 38

Slide 38 text

38 Taskの順次処理(Next) lib/stepfunctions-sample.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler’, }); const firstState = new tasks.LambdaInvoke(this, '最初の処理', { lambdaFunction: taskFn, outputPath: '$.Payload’, }); const secondState = new tasks.LambdaInvoke(this, '次の処理', { inputPath: '$.message’, lambdaFunction: taskFn, }); const definition = firstState.next(secondState); new sfn.StateMachine(this, 'stateMachine', { definition: definition, }); Taskを定義します。 aws-stepfunctions-tasksライブラリのクラスを 使うことで、Lambdaを実⾏基盤としたタス クを定義することができます。

Slide 39

Slide 39 text

39 Taskの順次処理(Next) lib/stepfunctions-sample.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler’, }); const firstState = new tasks.LambdaInvoke(this, '最初の処理', { lambdaFunction: taskFn, outputPath: '$.Payload’, }); const secondState = new tasks.LambdaInvoke(this, '次の処理', { inputPath: '$.message’, lambdaFunction: taskFn, }); const definition = firstState.next(secondState); new sfn.StateMachine(this, 'stateMachine', { definition: definition, }); Taskを定義します。 lambdaFunctionsに上で定義したtaskFnインス タンスを渡します。

Slide 40

Slide 40 text

40 Taskの順次処理(Next) lib/stepfunctions-sample.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler’, }); const firstState = new tasks.LambdaInvoke(this, '最初の処理', { lambdaFunction: taskFn, outputPath: '$.Payload’, }); const secondState = new tasks.LambdaInvoke(this, '次の処理', { inputPath: '$.message’, lambdaFunction: taskFn, }); const definition = firstState.next(secondState); new sfn.StateMachine(this, 'stateMachine', { definition: definition, }); Taskを定義します。 outputPath には Taskの output を指定します。 $.Payload を指定することで, Lambdaの戻り 値を次のTaskに渡すことができます

Slide 41

Slide 41 text

41 Taskの順次処理(Next) lib/stepfunctions-sample.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler’, }); const firstState = new tasks.LambdaInvoke(this, '最初の処理', { lambdaFunction: taskFn, outputPath: '$.Payload’, }); const secondState = new tasks.LambdaInvoke(this, '次の処理', { inputPath: '$’, lambdaFunction: taskFn, }); const definition = firstState.next(secondState); new sfn.StateMachine(this, 'stateMachine', { definition: definition, }); Taskを定義します。 inputPath にはoutputPathから何を Taskの input とするかを指定します。 $とすることで、上のTaskのJSONがすべて Inputに渡ることになります。

Slide 42

Slide 42 text

42 Taskの順次処理(Next) lib/stepfunctions-sample.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler’, }); const firstState = new tasks.LambdaInvoke(this, '最初の処理', { lambdaFunction: taskFn, outputPath: '$.Payload’, }); const secondState = new tasks.LambdaInvoke(this, '次の処理', { inputPath: '$.message’, lambdaFunction: taskFn, }); const definition = firstState.next(secondState); new sfn.StateMachine(this, 'stateMachine', { definition: definition, }); Taskを .next() でつなげます。

Slide 43

Slide 43 text

43 Taskの順次処理(Next) lib/stepfunctions-sample.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler’, }); const firstState = new tasks.LambdaInvoke(this, '最初の処理', { lambdaFunction: taskFn, outputPath: '$.Payload’, }); const secondState = new tasks.LambdaInvoke(this, '次の処理', { inputPath: '$.message’, lambdaFunction: taskFn, }); const definition = firstState.next(secondState); new sfn.StateMachine(this, 'stateMachine', { definition: definition, }); ステートマシンを作成します。

Slide 44

Slide 44 text

44 デプロイ npm run build npm run cdk deploy

Slide 45

Slide 45 text

45 デプロイされたステートマシン図

Slide 46

Slide 46 text

46 他のStateについても実装してみます

Slide 47

Slide 47 text

47 Wait(再掲) 最初の処理 Start End 10秒待つ 最後の処理 指定された時間を待機する State この例だと、最初の処理が終わっ てから、10秒後に最後の処理が実 ⾏されることになります。

Slide 48

Slide 48 text

48 Wait(AWS CDKで実装) const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler', }); const firstState = new tasks.LambdaInvoke(this, '最初の処理', { lambdaFunction: taskFn, outputPath: '$.Payload', }); const wait10 = new sfn.Wait(this, '10秒待つ', { time: sfn.WaitTime.duration(cdk.Duration.seconds(10)), }) const secondState = new tasks.LambdaInvoke(this, '次の処理', { inputPath: '$.message’, lambdaFunction: taskFn, }); const definition = firstState.next(wait10).next(secondState); new sfn.StateMachine(this, 'stateMachine', { definition: definition, }); lib/stepfunctions-sample-stack.ts Wait Stateを定義して、WatiTimeクラスの durationを使って秒数を指定します

Slide 49

Slide 49 text

49 Wait(AWS CDKで実装) const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler', }); const firstState = new tasks.LambdaInvoke(this, '最初の処理', { lambdaFunction: taskFn, outputPath: '$.Payload', }); const wait10 = new sfn.Wait(this, '10秒待つ', { time: sfn.WaitTime.duration(cdk.Duration.seconds(10)), }) const secondState = new tasks.LambdaInvoke(this, '次の処理', { inputPath: '$.message’, lambdaFunction: taskFn, }); const definition = firstState.next(wait10).next(secondState); new sfn.StateMachine(this, 'stateMachine', { definition: definition, }); lib/stepfunctions-sample-stack.ts 先程と同じく.nextでつなげます。

Slide 50

Slide 50 text

50 デプロイ

Slide 51

Slide 51 text

51 Parallel(再掲) 並列に処理を実⾏するState 並列処理1 並列処理2 start end 処理3

Slide 52

Slide 52 text

52 Parallel(AWS CDKで実装) const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler', }); const paralellState1 = new tasks.LambdaInvoke(this, '並列処理1', { lambdaFunction: taskFn, }); const paralellState2 = new tasks.LambdaInvoke(this, '並列処理2', { lambdaFunction: taskFn, }); const wait10 = new sfn.Wait(this, '10秒待つ', { time: sfn.WaitTime.duration(cdk.Duration.minutes(10)), }) const parallel = new sfn.Parallel(this, 'Parallel'); parallel.branch(paralellState1, paralellState2).next(wait10); new sfn.StateMachine(this, 'stateMachine', { definition: parallel, }); lib/stepfunctions-sample-stack.ts

Slide 53

Slide 53 text

53 Parallel(AWS CDKで実装) const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler', }); const paralellState1 = new tasks.LambdaInvoke(this, '並列処理1', { lambdaFunction: taskFn, }); const paralellState2 = new tasks.LambdaInvoke(this, '並列処理2', { lambdaFunction: taskFn, }); const wait10 = new sfn.Wait(this, '10秒待つ', { time: sfn.WaitTime.duration(cdk.Duration.minutes(10)), }) const parallel = new sfn.Parallel(this, 'Parallel'); parallel.branch(paralellState1, paralellState2).next(wait10); new sfn.StateMachine(this, 'stateMachine', { definition: parallel, }); lib/stepfunctions-sample-stack.ts branch()メソッドで、並列処理するstate を指定します。

Slide 54

Slide 54 text

54 デプロイ

Slide 55

Slide 55 text

55 Choice(再掲) 条件により分岐するState 処理1 ChoiceState 処理2 処理3 処理4 start end

Slide 56

Slide 56 text

56 Choice(AWS CDKで実装) lib/stepfunctions-sample-stack.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler', }); const task = new tasks.LambdaInvoke(this, '処理', { lambdaFunction: taskFn, outputPath: '$.Payload', }); const choice = new sfn.Choice(this, 'ChoiceState'); choice.when(sfn.Condition.stringEquals('$.message', 'Hello'), new sfn.Pass(this, '処理1')); choice.when(sfn.Condition.stringEquals('$.message', 'World'), new sfn.Pass(this, '処理2')); choice.otherwise(new sfn.Pass(this, '処理3')); const definition = task.next(choice); new sfn.StateMachine(this, 'stateMachine', { definition });

Slide 57

Slide 57 text

57 Choice(AWS CDKで実装) lib/stepfunctions-sample-stack.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler', }); const task = new tasks.LambdaInvoke(this, '処理', { lambdaFunction: taskFn, outputPath: '$.Payload', }); const choice = new sfn.Choice(this, 'ChoiceState'); choice.when(sfn.Condition.stringEquals('$.message', 'Hello'), new sfn.Pass(this, '処理1')); choice.when(sfn.Condition.stringEquals('$.message', 'World'), new sfn.Pass(this, '処理2')); choice.otherwise(new sfn.Pass(this, '処理3')); const definition = task.next(choice); new sfn.StateMachine(this, 'stateMachine', { definition }); when()メソッドで、分岐条件を定義します

Slide 58

Slide 58 text

58 Choice(AWS CDKで実装) lib/stepfunctions-sample-stack.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler', }); const task = new tasks.LambdaInvoke(this, '処理', { lambdaFunction: taskFn, outputPath: '$.Payload', }); const choice = new sfn.Choice(this, 'ChoiceState'); choice.when(sfn.Condition.stringEquals('$.message', 'Hello'), new sfn.Pass(this, '処理1')); choice.when(sfn.Condition.stringEquals('$.message', 'World'), new sfn.Pass(this, '処理2')); choice.otherwise(new sfn.Pass(this, '処理3')); const definition = task.next(choice); new sfn.StateMachine(this, 'stateMachine', { definition }); Conditionクラスを使って条件を記述します この場合は、前のタスクから渡されてきた $.PayloadのmessageがHelloという⽂字列なら 次の引数のStateに遷移するという意味にな ります。

Slide 59

Slide 59 text

59 Choice(AWS CDKで実装) lib/stepfunctions-sample-stack.ts const taskFn = new lambda.Function(this, 'TaskFn', { runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset('handlers’), handler: 'task.handler', }); const task = new tasks.LambdaInvoke(this, '処理', { lambdaFunction: taskFn, outputPath: '$.Payload', }); const choice = new sfn.Choice(this, 'ChoiceState'); choice.when(sfn.Condition.stringEquals('$.message', 'Hello'), new sfn.Pass(this, '処理1')); choice.when(sfn.Condition.stringEquals('$.message', 'World'), new sfn.Pass(this, '処理2')); choice.otherwise(new sfn.Pass(this, '処理3')); const definition = task.next(choice); new sfn.StateMachine(this, 'stateMachine', { definition }); otherwise()で条件にあてはまらなかった場合 のstateの遷移先を指定します。

Slide 60

Slide 60 text

60 デプロイ

Slide 61

Slide 61 text

61 まとめ l AWS CDKを使うことで、JSONベースなASLを書か なくとも、TypeScriptやPythonのようなプログラミ ング⾔語でStep Functionsのワークフローを書くこ とができる l ASLで書くよりも直感的に実装できる

Slide 62

Slide 62 text

62 参考⽂献 l https://docs.aws.amazon.com/cdk/api/latest/ docs/aws-stepfunctions-readme.html l https://docs.aws.amazon.com/cdk/api/latest/ docs/aws-stepfunctions-tasks-readme.html

Slide 63

Slide 63 text

No content