Slide 1

Slide 1 text

© 2024 Cookpad Inc. https://speakerdeck.com/sorah/fryegg Step Functions で決裁を回そう Shota Nagasaki & Sorah Fukumori, Cookpad Inc.

Slide 2

Slide 2 text

Sorah Fukumori https://sorah.jp/ Head of Corporate Engineering & Security / 本業はインフラ領域 SWE いろいろあって Corporate Engineering 領域 (IT とセキュリティ) も担当 今回の話では主に設計・実装レビュワー 好きなAWSのサービス: AWS IAM と STS Nia (Shota Nagasaki) Software Engineer Umeebe CTO → Cookpad に買収されやさしい釣り事業部で部長 → 事業クローズで Corporate Engineering に異動 今回の話では実装に加え、関係各所へのヒアリング→設計への反映を担当 自己紹介

Slide 3

Slide 3 text

今日の話 Step Functions で決裁を回すとは…

Slide 4

Slide 4 text

某サービスの上で動いていたワークフローとバックオフィス領域の問い合わせ管理を 移行することになった (経緯は割愛)。 当時の決裁規定や満たしていた需要がそこそこ複雑で適切な移行先が見当たらなかった ● インシデント管理 (チケット管理) ○ エンジニアリングチームは GitHub で問い合わせを受けたりしているが、 バックオフィスはそうもいかない ■ GitHub のライセンスはあっても不慣れ、英語UI etc ■ そもそも秘匿したい案件も多数ある ● ワークフロー (リクエストアイテム) ○ 稟議的なやつ。n円の購入をします、この契約書にサインしたいです、etc ■ 購入金額や申請者の所属・役職による分岐 ■ 設計当時はまあまあ複雑なフローが組まれていた なお、業務をシステムに合わせてシンプルなフローにできるならそれに越したことはない チケット管理システムの移行プロジェクト

Slide 5

Slide 5 text

移行プロジェクトが始まった頃、チームで密かに期待していた AWS サービスがあった ● 実は 2016 年くらいからあるステートマシンサービス ○ データを渡して、ステートマシンで処理して、データを出力するサービス ■ JSON データの処理に特化した Yahoo! Pipes みたいなもの ■ 部分的には AWS マネージド、サーバーレスな Airflow とも考えられる (はず) ● 2022〜2023 年くらいに急に加速しはじめた ○ Lambda 関数の呼び出しを含め、AWS 全サービスの API コールが Step Functions から実行できるようになったり ○ なんなら外部の HTTP API も Lambda 無しで呼び出せるようになったり Step Functions (SFn)

Slide 6

Slide 6 text

移行先を選定する中、内製の選択に至るまでの諸々は割愛するが…。 ● Step Functions でワークフロー作れるのでは…? と思い付いてしまった ○ できそうという事が分かり、やることになってしまった ● 実際できそう ○ 条件分岐、簡単なデータの操作、AWS APIを含めた豊富な外部連携がある ○ 可視化 (Workflow Studio でのビジュアルエディタ) もある ○ Task token の概念があり、外部にリクエストした処理が解決されるのを、 非同期に待ち合わせることが出来る ■ SQS や HTTP API 等の先で非同期に処理を行った結果を戻すための仕組み ■ これを利用すれば「人間の承認待ち」とかもできそう → Step Functions 含め AWS フル活用すればギリギリできちゃうのでは? 内製を決定した

Slide 7

Slide 7 text

Fryegg やってみたらできました

Slide 8

Slide 8 text

ワークフロー・チケット管理システム Fryegg を作りました ● チケットに複数のワークフロー実行を付けられる ● 複数のバックオフィス部署を1つのチケットに呼び出して案件を最初から最後まで 1チケットで対応できる ● 組織の透明性を確保するため、チケットの公開・非公開も柔軟に対応 2023/5 開発開始、2023/9 リリース & 主要なワークフローは移行完了 (!?) ● 主要メンバーは我々 2 人。スケジュールは都合により厳しかったが Step Functions 等々 AWS サービスを活用することで、なんとか…。 Fryegg: コンセプト

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

Fryegg の後ろにいる State Machine の例 (組織変更の申請関連) ● この例ではユーザー入力によって承 認フローが変わったりしている ○ 可視化は AWS Management Console の Workflow Studio に任せられている! こんな感じでバックオフィスの業務が Step Functions に乗りました、という話 をして行きます

Slide 11

Slide 11 text

● Backend: Rails + Aurora MySQL ● Frontend: Vite + React Router ● ワークフローは Step Functions 全のっかり ● ワークフロー以外の外部連携は EventBridge ○ あらゆるイベントを EventBridge で拾えるようにしている ● 本体であるところの Rails は ECS で、Step Functions からの連携は SQS 経由 ○ Rails + Aurora + ECS は社内標準 詳細→ [Cookpad ECS Hako][検索] ● Lambda は主に通知を含めた外部連携で活用 ○ ECS 側の実行環境と Lambda 側の実行環境で多重にビルドしないといけない のを避けたかったので、一旦サーバーフルになっている 実装

Slide 12

Slide 12 text

● Step Functions 採用したらめっちゃ良かった ○ やすい ○ ちゃんとうごく ○ まねーじど 先に結論

Slide 13

Slide 13 text

実装はシングルテナントを前提にしているが、設計する時のマインドは当社 (Cookpad) を Fryegg の 1 ユーザー組織と見做している ● 外部SaaSを利用する場合、触れないところ、カスタマイズしている所が明らかだ し、出来ることに限界があるので問題はない ● ただ、内製しはじめるとその境界がとても曖昧になってしまうので、丁寧に責任 を分離しておきたい 重要な部分 (Fryegg 本体) にユーザー組織 (Cookpad) 特有の事情のビジネスロジックを 入れたくない 設計にあたり

Slide 14

Slide 14 text

● 従って、Fryegg 本体には Cookpad の事情を絶対に入れないように実装している ○ データモデル等はシンプルになるように。 Cookpad が採用している ERP 等関連システムを前提にしてはいるが、なるべ く業務特有の事情が入らないように気をつける ● 事情が入りそうであれば設計がおかしいので見直す ○ 基本的には Fryegg の [外部連携] の仕組みから先に全て持たせる ■ 具体的には、Step Functions と EventBridge を責任分界点と見做している ○ ワークフローを司る Step Functions からがユーザー組織 Cookpad の設定ファ イルとなり、基本的にはここがユーザー組織独自の外部連携の起点 ○ ワークフローに依らない部分は EventBridge などを通して引っ掛けてもらう AWS サービスという共通言語が分界点になるのもお得! 設計にあたり (2)

Slide 15

Slide 15 text

たとえば、組織構造のツリーなどは持たない (複雑かつ組織に依りがち) ● Organization > Group > User 以上の構造は取らない ○ 権限管理の都合、Group 同士をまとめたレイヤーは必要で、Organization と いうレイヤーをしぶしぶ作成した。基本は Group に全ておしつける ● Group は [xxx本部 yyy部 zzzグループ] のように組織上の最小単位で作成 ○ 階層構造ではないため、xxx本部や yyy部を表わすグループは (直所属を示す Group を除き) 作らない ● ワークフロー中で承認者を見つける仕組みとして Group それぞれに Role と Role に対応する User 割り当てがある Group has many RoleAssignments, RoleAssignments belongs to Role and has User 設計にあたり - 考慮の実例

Slide 16

Slide 16 text

設計にあたり - 考慮の実例 A部1グループ ● CEO: X ● CFO: Y ● 法務担当者: M, N, O, … ● 法務部長: L ● グループリーダー: A ● 部長: B B部2グループ ● CEO: X ● CFO: Y ● 法務担当者: M, N, O, … ● 法務部長: L ● グループリーダー: C ● 部長: C 最下部の A,B,C の部分だけ違うが、共通である CEO=X, CFO=Y, …も データ上は各 Group の数だけ RoleAssignment レコードが全て存在 する。Group 1つだけ見れば承認者が分かる Group, Role, RoleAssignment のイメージ:

Slide 17

Slide 17 text

● 各 Role に対応する User を探すのはデータ連携システムから先に押し付けている ○ Fryegg は承認者を検索する時に Cookpad の事情を踏まえて組織構造を辿る必要がない ( 承認者は Role で指定する) ● 実際には人事マスタから内製のデータ連携システムで Fryegg にインポート ○ 人事マスタを持つ Workday 側で 各 Group (WorkdayのSVO) を行として、対応する Role のユーザーをカラムとするレポートがあるため、これを元に取り込み (レポートは materialized view のようなもの) ○ じつは内製のデータ連携システムも Step Functions で Lambda を振り回しているけ ど今回は割愛 地味に面倒な部分はいさぎよく持たない、持たずに実現可能な方法でサボって実装 人事マスタ側の担当者の作業で完結し、根本的にロジックが変わらない限りは Step Functions の State Machine なども修正する必要がなく便利 設計にあたり - 考慮の実例

Slide 18

Slide 18 text

Step Functions と相性が良すぎる ワークフロー

Slide 19

Slide 19 text

ここでは Fryegg のチケットから起動する「一連の手続き」 ● 社内にいろいろある ○ 「契約書の内容を法務部がレビューしてから、内容について決裁者が承認する」 ○ 「購入金額に応じて選択される承認者が内容をレビュー・承認する」 ○ 「労務への書類作成の依頼」 ○ 「端末など貸与品の紛失報告」 ワークフロー

Slide 20

Slide 20 text

ワークフローを実際にシステム上で動作させ管理するための要件がいろいろある ● 条件分岐 ● エラー処理 ● 外部APIとの連携 (自動化推進) ● 人間への通知、人間の対応・承認を待ち受ける ● 「ワークフローの現在の状態」をUIにフィードバックする ワークフロー

Slide 21

Slide 21 text

再掲: Step Functions はワークフローと相性がいい 要件をほぼクリアしている ● 条件分岐 ● エラー処理、リトライ → 標準的にサポートされている。特に失敗を回復する Redrive が便利 ○ Redrive は途中からそこまでの state を維持して execution を再開させる事が 可能。これにより、たとえば全ての承認を得た後エラーを対応する際、その 手前までに済んだ承認を取り直す必要などがない ■ Fryegg 開発中にこういう機能作らないといけないよね… と言ってたら公式が実装してく れた 嬉しい ワークフロー

Slide 22

Slide 22 text

そのほかにも… ● 結果を元に自動化をするための HTTP API や AWS API コールのサポート ● Task Token → Step Function 外での処理の待機ができる → 人間の承認やタスクの完了を State Machine が待ち受けられる ワークフロー

Slide 23

Slide 23 text

ワークフロー定義は Step Functions の State Machine. なので基本 SFn 標準の ASL で記述 ● Jsonnet で記述したものをデプロイ時に State Machine として作成・更新されるような 仕組みを備えている ● ワークフローへの変更を分かりやすくコードレビューできる ○ これまでやそのへんのワークフローSaaSとは大違いで嬉しい ■ GitOps っぽいことが出来るSaaSもあるが扱いやすいかというと…… ■ あと Git 非対応でノーコードみたいなやつはもっと保守性が…… ● ASL には存在しない入力値のスキーマも与えられるようになっていて、Web UI 上のフ ォーム自動生成などに活用されている ワークフローの Step Functions State Machine の記述

Slide 24

Slide 24 text

ワークフローの定義からフォームをある程度自動生成 実行するとフォームの内容が SFn State Machine への Input として使われる

Slide 25

Slide 25 text

● Step Functions から実行途中で Fryegg の機能を利用するシーンは度々ある (人間からの承認を待つ等) ● そのため、Fryegg 側へのリクエストは糖衣構文のようなもので記述可能になっていて 、実際に State Machine へ設定される際に ASL にトランスパイルする仕組みを入れてい る ○ AWS SAM で言うところの generated resource のような雰囲気 ○ たとえば承認リクエストを送信したければ Type: Fryegg::Approval な State を記述 すると、デプロイ時に Fryegg 本体に向けた sqs:SendMessage の State に変換され る ASL への糖衣構文、トランスパイル

Slide 26

Slide 26 text

↓トランスパイル↓

Slide 27

Slide 27 text

Approval は人間の承認を待つ ● ワークフロー実行者の Group で対応する Role を持つユーザーへ承認要求を送信す ることができる ○ マネージャや経営メンバーの了解や決裁を得る際に利用できる ● ここで生じた task token は Fryegg の Web UI 上で、approval request が人間によっ て resolve されるまで終了しない ○ SFn の仕様として結構長いこと待ってくれるので助かる ● Fryegg 側の UI で承認者が承認・拒否することで task を完了させる → Step Functions 側では Fryegg の SQS にメッセージを送るようにして waitForTaskToken するだけなので、難なく実装できた Fryegg 側のワークフロー支援機能: Approval

Slide 28

Slide 28 text

Task は「やること」を作成して待つ ● 承認を必要としない、バックオフィス担当者による作業が必要な作業はチケットに Task として作成できるようになっている ○ 自動化できず、人間が作業してそれを待つ必要がある場合に使う (物理的な郵送や捺印とか) ○ チケットと同じ可視範囲 ● これも同様に task token はタスクの完了ボタンが押されるまで resolve しない ○ もちろん、タスクの完了を待たないこともできる Fryegg 側のワークフロー支援機能: Task

Slide 29

Slide 29 text

実際のユースケース

Slide 30

Slide 30 text

本セッションでは外部連携を伴うワークフローを1例、個人情報の取得申請を紹介しま す ● 当社のルール上、ユーザーの個人情報を扱う Web フォームを作成する際は、 法務およびセキュリティチームの承認が必要 (アンケート等) ● 要件を満たす所定の方法で作成された Google フォームと公開用 URL を使うのが 標準となっている ○ Fryegg 以前からフォーム作成サービスが Lambda + API Gateway なシステム で自動化されていた 先に書いたように外部連携を「する」責務を Fryegg 本体から剥がしているので、ワー クフロー起因の外部連携は Step Functions から行う 外部連携の例: 個人情報の取得申請

Slide 31

Slide 31 text

● Fryegg への移行を行ってみた。外部連携がある以外はシンプルなワークフロー ○ 関連チームの承認 → 先述の内部 API を利用して Google フォームを作成 → 申請者に必要な情報を通知 ● Step Functions の HTTP API 連携で済んだ。当初の設計コンセプトの狙い通り、 Fryegg 本体に触らず、 Step Functions の State Machine から先だけ設定することで 移行できた 外部連携: 個人情報の取得申請 (2)

Slide 32

Slide 32 text

● Step Functions の HTTP API State で済まない処理も、Lambda function を用意すれ ば容易に連携できて便利 ● 連携に必要な小規模な Lambda function も作成する場合はコードベースを同居せず 分離しておくのが容易にできて嬉しい ○ 本体はサーバーフルだが、連携先や連携に必要な glue code は Lambda に置い てサーバーレスにすることで運用をサボれる ■ コードベースは分離したいが、それぞれでDockerイメージなりデプロイなりコン テナなりを維持するのはまあまあ大変 ■ 簡単なスクリプトだけならDocker使わずにLambda、そうじゃなければDocker使っ てLambda、もしくはSAMなど ■ Lambda 直であれば API Gateway や Function URI を通さず lambda:InvokeFunction の権限を IAM で渡すだけなので簡単にセキュアにもできる 外部連携を Step Functions や EventBridge を通して良かったところ

Slide 33

Slide 33 text

● ワークフロー以外 (Step Functions 外) も外部連携したい事はそれなりにある ● チケットに関連するイベントは全部 EventBridge に流している ○ EventBridge のルールで Lambda を起動するなど、簡単に連携を実装できて便 利 ● Fryegg の Slack 通知やメール通知は EventBridge で拾って Step Functions + Lambda で必要な相手に配送している EventBridge + Lambda

Slide 34

Slide 34 text

課題

Slide 35

Slide 35 text

● ユーザーインターフェースは今後の課題 ○ 機能要件を満たすためにそっちを急いだ関係もあり、UI は未だに突貫工事的 ● チケット管理が出来るシステムへの暗黙の要求はまあまあ高い、という問題 ○ キーボードショートカットとか画像の貼り付けとか 今後の課題(1)

Slide 36

Slide 36 text

● Step Functions の限界 ○ dry-run がしづらい ■ State 単体でのテストが出来る仕組みは導入されたが、全体を通して dry- run みたいなことは出来ない (例えば、誰に承認が飛びますみたいなのが分からない) ■ → 既存のASLをそう振る舞うように機械的に書き換えれば可能ではある が、複数の state にモックを入れて起動してみるみたいなのが出来るとう れしい ○ 進捗が分かりづらい ■ Step Functions 上の Workflow Studio (ビジュアルスタジオ) を見れば 進捗は分かるが、Fryegg のユーザーは AWS コンソールを見れない (残りの承認ステップなどがどれくらい残っているかが分からない) ■ → ここは手動で state に進捗具合を書き込んでいくしかないのかなぁ 今後の課題(2)

Slide 37

Slide 37 text

● 社内の対応ワークフローの拡充 ○ 従業員/バックオフィス機能をつなげるワークフロー以外にも、バックオフィ スチーム内の自動化にも活用可能 ○ 入社・退職処理とか ● 社内でまあまあ複雑な発注処理を終始一貫で処理できるように ○ サプライヤー申請 (反社チェック) → 購入決裁の取得 → 発注書・契約書へのサ イン/送付 やりたいこと

Slide 38

Slide 38 text

● AWS IoT ○ コメントなどがあった場合にリアルタイムにUI反映させるのにに使えるはず ● Amazon Verified Permissions ○ 認可まわりのコードがだいぶ複雑になっている上、ポリシーを本体外に持つ ことで最初に述べた設計マインドに沿えそうでトライしたい ● UIの改善 ○ キーボードショートカットや画像貼り付け、そもそものデザイン・レイアウ ト改善など諸々… やってみたいこと

Slide 39

Slide 39 text

まとめ

Slide 40

Slide 40 text

● 社内のバックオフィス向けチケット管理システム・ワークフロー実行システムを Step Functions, EventBridge を活用した内製ツールに移した ○ Step Functions, EventBridge を責任分界点としてコードが綺麗に分離できる ○ 肝心のワークフロー自体は内製せず、Step Functions という信頼できる基盤に 頼れる ○ 外部連携も柔軟に構築可能 まとめ