AWS Lambdaを使わずStep Functionsだけでシステムを構築するメリット・デメリット
この記事は、ニフティグループ Advent Calendar 2024 17日目の記事です。
こんにちは。システム開発部 スペシャリストのkiwiです。
弊社では数年前から、AWSのマネージドサービスを活用したサーバーレス構成でシステムを構築しています。APIのバックエンドなどの軽量な処理にはAWS Lambdaを採用することが多いのですが、Lambdaの数が増えるにつれ、ランタイム更新などの運用作業も増加し、開発部の課題となっています。
そこで今回、通常ならLambdaを使う新しいシステムについて、Step Functionsでの構築にチャレンジしました。この記事では、構築した処理の内容と開発フロー、そしてLambdaによる開発と比較した所感をご紹介します。
目次
構築内容
今回実装した処理は「指定されたIDのユーザーにメールを送信する」という処理です。「ニフティ温泉」にて電子チケットを購入した方へお送りするメールの送信などに利用しています。
メール送信に必要な情報は複数のDynamoDBに分散して保存されているため、これらを取得した後、SESのメールテンプレートを使って送信します。
ワークフロー実装
今回の処理は以下の3つのステップに分解できます。
- DynamoDB1からユーザー情報を取得する
- DynamoDB1の内容をもとに、DynamoDB2からユーザー情報を取得する
- 1と2の情報を使い、SESでメールを送信する
これらに対応するステップをStep Functionsで作成していきます。
DynamoDBからのデータ取得はいずれもパーティションキーで取得できるため、Step Functionsの最適化統合(GetItem)を使って実装します。SESでのメール送信はStep Functionsの最適化統合がないため、サービスSDK統合を利用しています。
また、複数人に対してメール送信するワークフローも作成しました。並列処理を行う全体ワークフローを作成し、その中から先ほどの「1人へメール送信するワークフロー」を呼び出しています。
実行中のステートマシンから新しいステートマシンを起動する(ドキュメント)
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/connect-stepfunctions.html
Step Functionsのテスト
Step Functionsのテストには主に3つの方法があります。
- Step Functions Localを使い、ローカル環境で実行(JSONPath形式のみ)
- TestState APIを用いて、特定のステートのみを実行
- AWS環境にデプロイし、入力を変えながら実行
Step Functions Localを使ったテスト
このワークフローを実装した時点では、まずローカル環境を利用した自動テストを作成しました。Step Functions Localを使うと、ワークフローの作成や実行のコマンドを受け付けるサーバーをローカルで起動できます。
ただし、Step Functions Localは現在AWSからのサポートが終了しており、JSONata形式などの新機能に対応していません。サードパーティ製のエミュレーション用アプリケーションの利用を検討中です。
TestState APIを使ったテスト
TestState APIは、1ステート分の定義とステートへの入力、その時点の変数などを入力し、その定義をそのまま実行できるテスト用のAPIです。コンソールのワークフロー編集画面でステートを選択した際に表示される「テスト状態(Test state)」というボタンからも呼び出せます。
テストコードからTestState APIを利用する場合、テスト実行環境にTestStateを呼び出すための権限(IAMロール等)を付与しておく必要があります。
また、テスト対象の定義を実際に実行するため、ワークフロー実行に使うRoleもダミーのものは使用できません。AWSサービスや外部のAPIを呼び出すタスクステートの場合、その内容が実際に実行されることにも注意が必要です。
さらに、TestState APIは1秒に1回という実行数制限(ハードクォータ)が設定されており、テストケースが複数ある場合などで並列に実行するとすぐに429(Too many requests)エラーが返却されてしまいます。自動テストとの相性はあまり良くないと言えるでしょう。
Step Functionsのデプロイ
AWSコンソール上からGUIで作成できるStep Functionsですが、構成管理のためコードで管理や開発を行うことも多いと思います。
Step FunctionsのワークフローはAmazon States Language(ASL)と呼ばれるJSONで定義します。YAML形式でインポート・エクスポートすることも可能で、AWS Serverless Application Model(SAM)を利用すると、YAML形式のままCLIからデプロイすることもできます。
また、CDKを使ってワークフローを定義・デプロイすることも可能です。
Lambdaでの実装と比較したメリット・デメリット
☀️定常的な保守運用が不要
コーディングで実装した場合に必要となるリソースやライブラリのアップデート、Lambdaの場合はランタイムアップデートへの追従など、定常的な運用がほぼ不要となる点は最大のメリットだと思います。
標準ワークフローの場合は実行時間に関する課金も発生しないため、長時間稼働するバッチ処理の起動や通知周りのハンドリングなどについては、できるだけLambda関数を作らず、極力Step Functionsのみでサーバーレス構成を構築するのが良いと思っています。
☀️Expressワークフローを使った短時間処理
長時間処理でよく利用されるStep Functionsですが、実行時間が短い処理にも利用できます。特に、データ変換やPUTなどべき等な処理の場合、Expressワークフローを利用することで、パフォーマンスやコスト効率を高めた運用が可能です。
Expressワークフローは、実行時間や外部タスクの呼び出し形式、履歴の保持などに制約がある代わりに、実行数・実行時間と使用メモリによって料金が決まる(Lambdaに近い料金体型)形式のステートマシンです。IoTデータの収集やストリーミングデータ処理など、大量のイベント処理ワークロードに最適とされています。
標準ワークフローとの比較は以下のドキュメントにまとめられています。
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/choosing-workflow-type.html
標準ワークフローを大量に呼び出すと実行コストが高くつくことが多いのですが、5分以内で完了する処理の場合にはExpressワークフローの利用を積極的に検討すると良いでしょう。
☀️ノーコードで実装できる
コーディングを行わず、GUI上から実装できるのもメリットのひとつです。
最終的にコード管理する場合も、GUIで組んだワークフローをJSON/YAML形式で出力できるため、特にASLに慣れないうちは、GUIから作成すると良いでしょう。
☔️テスト(特に自動テスト)との相性が悪い
ステート間の入出力が大きなポイントとなる割に、その部分の自動テストに耐えうる仕組みが(少なくとも公式からは)提供されていない点は考慮が必要です。
単機能ワークフローの組み合わせでメインワークフローを構築するなどの方法を取ることで、テストにかかる工数を下げることになります。
☔️ASLの学習コスト
ワークフローの定義に使用するASLは、構成自体はシンプルなものの多少の学習コストがかかります。特にJSONPath形式で実装された従来のワークフローは、コードから挙動が把握しづらく、理解が難しい可能性が高いです。
従来のワークフローは早めにJSONata形式に置き換えることをおすすめしますが、前述のテスト環境の課題と相まって、リファクタリングに時間がかかるのが欠点です。なお、ステップ単位で部分的にJSONata形式を取り入れることも可能なので、段階的な置き換えも検討すると良いでしょう。
☔️コストがかかる
LambdaとExpressワークフローを比較すると、特に並列処理の実行においてExpressワークフローの方が割高になります。リトライなどが絡む並列処理の実装コスト、前述の運用コストの低減などと天秤にかけて判断する必要があるでしょう。
標準ワークフローは実行時間による課金がない代わりに、状態遷移ごとに費用が発生し、Lambdaと単純に比較するとコストが高くなります。このコストが問題となる場合は、標準ワークフローの特徴であるExactly-onceの実行セマンティクスや、実行履歴の保持要件などの必要性を検討し、アーキテクチャを検討すると良いでしょう。
まとめ
今回はStep Functionsについて、サーバーレス処理の実行環境としてどの程度活用できるか、実際の利用シーンに沿ってメリット・デメリットを並べました。
実はこの記事を準備している最中にJSONata機能・変数機能のサポートが発表されており、これをきっかけに実装体験が格段に向上しました。「一連の処理をステート単位に切り分け、入出力や変数を用いてつなげていく」という一番重要な部分に時間を割けるようになった感覚があります。
複雑なデータの変換などは引き続きLambdaを利用しますが、Step Functionsの採用による運用上のメリットもとても大きいことを実感しています。今後、様々なユースケースでの採用を検討したいと考えています。
掲載内容は、記事執筆時点の情報をもとにしています。