Slide 1

Slide 1 text

Lecture course on Microservices 第2部:マイクロサービスの設計方法 中井悦司 / Etsuji Nakai 2023/01/04 ver2.5

Slide 2

Slide 2 text

Contents ● 5. サービス設計の基本的な考え方 ● 6. トランザクションの実装方法 ● 7. データの Query 機能 ● 8. 信頼性を高める工夫 2

Slide 3

Slide 3 text

5. サービス設計の基本的な考え方

Slide 4

Slide 4 text

変化に追従するための基本原則 ● 変更要求に対する凝縮度を上げることが「モジュール化」の基本 ○ 変更する理由が同じものは集める → 単一のモジュールだけを考えればよい ○ 変更する理由が異なるものは分離する → 想定外の影響を心配しなくてよい ● 開発者の認知能力を超えた連鎖的変更を防止。「容易に理解できる」範囲の変更 は生産性が高い 4 単一責任の原則 ↓ クラスを変更する理由は1つ以上存在してはならない 一言で言うと 「関係あるものはまとめる」 「関係ないものは分ける」

Slide 5

Slide 5 text

ビジネス要件からのマッピング ● ビジネス課題から必要となるビジネス要件を見つけ出し、それを実現するための「サービ ス」を設計することが原則 ○ ビジネス課題、ユースケース、想定されるソリューションを理解する ○ ビジネス視点での「登場人物(役割)」、および、「提供するべき機能(Business capability)」を整理する ○ 「提供するべき機能(Business capability)」ごとに、それを実現するサービスを設 計する ○ 現在、および、将来に想定されるビジネス要件が満たされることを確認する 5

Slide 6

Slide 6 text

マイクロサービスとビジネス要件の対応例 6 Order service Stock transaction service Fee service Market service 売却リクエストを受け付けて 外部システムに送信する 手数料を 引き落とす 売却対象の株式 を確保する 売却リクエスト の情報を記録 売却リクエストを外部 システムに送信

Slide 7

Slide 7 text

必要な機能をサービスに分割する方法 ● 実際には次のような方針もあわせて検討する ○ ビジネス要件:大まかに関連するビジネス機能の領域ごとにサービスを分割する ○ ユースケース:ユースケース(ワークフロー)の中で実行される「アクション」をサー ビスとして実装する ○ 変更可能性:将来変更される部分について、変更の単位ごとにサービスとしてまとめる ○ 技術要件:実装が特定の技術に依存する場合、ビジネス要件とは関係なく技術要件のみ が変化する可能性がある場合など ○ セキュリティ要件:PCI DSS 準拠が求められるサービスを分割するなど 7

Slide 8

Slide 8 text

新しい機能を追加する例で考える ● 「投資戦略」機能を追加したい ○ ユーザーは、事前に定義された「投資戦 略」を選択して投資ができる ○ 投資戦略とは、複数の投資対象資産(ア セット)を集めて、投資割合をアサイン したもの ● 想定されるユースケースは右図の通り ○ 「管理者に提供する機能」と「エンド ユーザーに提供する機能」の2つのビジ ネス要件(Business capability)がある 8 「投資戦略」を定義 「資産」を選択 「投資戦略」を選択 投資を実行 「投資戦略」に従って 「資産」を購入 管理者 エンド ユーザー

Slide 9

Slide 9 text

「投資戦略」管理機能の設計 ● この「Business capability」を1つの Strategy サービスとして実装する 9 「投資戦略」を定義 「資産」を選択 管理者 管理者に提供する機能: 管理者は「投資戦略」の 定義を作成・変更できる ABC ・・・・ Code Search 20% DEF ・・・・ 10% Current Total:30% Code Description    ratio User:Asset manager 1 Cancel Save ・・・・・ Strategy name 管理画面の モックイメージ

Slide 10

Slide 10 text

「投資戦略」管理機能の設計 ● Strategy サービスの API は、「投資戦 略」の作成と取得機能を提供 ● 管理画面イメージからユースケースを想 像すると、この他に「ユーザー管理サー ビス」と、「アセット管理サービス」が 必要とわかる 10 Strategy service Create strategy Get strategy API エンドポイント イベント Strategy created Get user User service Asset service Get asset Search asset

Slide 11

Slide 11 text

ユースケースに基づくサービス設計 ● ビジネス要件が複数のドメインにまたがる場合、 特に、複数のドメインが複雑に連携する際は、 ユースケースからサービスを設計するとよい ○ ユースケース内の個々の「アクション」が独 立したサービスの候補となる ● たとえば、ユーザーが「投資戦略サービス」を利 用して投資を実行する際は、支払い処理との連携 が必要 11 「投資戦略」を選択 投資を実行 「投資戦略」に従って 「資産」を購入 エンド ユーザー 支払い処理との 連携が必要

Slide 12

Slide 12 text

支払い処理との連携 ● 支払い処理を行う Payment service を別に用意した場合、 右図のようなユースケースが考 えられる ● Strategy order service は、投 資のリクエストを受け付けた 後、支払い処理の完了を待って から、実際の発注を行う 12 Strategy investment Create payment Confirm Payment details Payment completed Get strategies Client Strategy order service Payment service Strategy service Strategies Create orders Save request Process payment 複数のアクション が混在している?

Slide 13

Slide 13 text

投資リクエストサービスの分離 ● 投資のリクエスト処理は、より 一般的な処理として別のサービ スにすることもできる 13 Payment completed Get strategies Strategy order service Payment service Strategy service Strategies Create orders Order service Order details Place orders Order completed

Slide 14

Slide 14 text

投資リクエストサービスの分離 ● 実際のところ、投資のリクエストは既存のユースケースに存在するので、 Strategy Order から分離して再利用するべき ● Order service は、「API の呼び出し元が誰であるか」には依存しない実装が必要 → サービスの自立性 14 Order service Create order Strategy order service 投資リクエストの情報 (ライフサイクル)を管理

Slide 15

Slide 15 text

(参考)サービスの自律性とクリーンアーキテクチャー ● マイクロサービスは、個別に データベースを持つ事で密結合 を防止して、サービスの自律性 を保つことができる ● これは、「各サービスは個別の ビジネスエンティティのオー ナーであり、エンティティの状 態変化に責任を持つ」というク リーンアーキテクチャーの考え 方にマッピングできる 15 Payment completed Get strategies Strategy order service Payment service Strategy service Strategies Create orders Order service Order details Place orders Order completed

Slide 16

Slide 16 text

(参考)Orchestration と Choreography の対比 ● クリーンアーキテクチャーの観点では、ビジネスプロセスとは、「一定のルールにした がってエンティティの状態変化を引き起こすプロセス」であり、個々の状態変化のロジッ クはサービス内部に隠蔽されている ● 他のサービスを含めたプロセス全体を理解した「ワークフローマネージャー」が存在する のが Orchestration パターンで、プロセスの全体を意識しないサービス群がイベントフ ローで自立連携するのが Choreography パターンと理解できる 16 Service A Service B Service C Service D API リクエスト リクエスト Service A Service B Service C Service D API リクエスト イベント Orchestration パターン Choreography パターン

Slide 17

Slide 17 text

変更可能性に基づくサービス設計 ● 継続的な変更が見込まれる部分をサービス化して、外部に対して変更を隠蔽する 17 ● たとえば、Market service は、外部システムの仕様変更・ 拡張により、連携方式が変更・ 拡張する可能性が高い

Slide 18

Slide 18 text

変更可能性に基づくサービス設計 ● 外部システムごとに異なるビジネスロジックやテクノロジーが必要な場合、バックエンド のサービスを分離することもできる 18 Market service Stock exchange A 売却リクエストを 外部システムに送信 Stock exchange B Stock exchange C REST API SOAP CSV を FTP 転送 Market service Stock exchange A Stock exchange B Stock exchange C SOAP service FTP service

Slide 19

Slide 19 text

技術要件に基づくサービス設計 ● 場合によっては、ビジネス要件ではなく、技術要件からサービスを切り出した方がよい場 合もある ● 例:支払いサービスは、支払い処理完了時にメール通知を行う。ただし・・・ ○ 支払いサービスは、メールアドレスなどのユーザー情報は持たない ○ 支払いサービス意外にもメール通知を必要とするサービスがあるかもしれない ○ メール以外の通知(SMS、プッシュ通知など)を希望するユーザーがいるかもしれない 19 Payment service Payment Completed イベント発行 email service Push notification service SMS service Subscribe

Slide 20

Slide 20 text

技術要件に基づくサービス設計 ● 一般には、次のような場合は、技術要件からサービスを切り出した方がよい ○ 実装が特定技術に依存しており、ビジネス要件に基づいたサービスから分離したい ○ 複数のサービスから利用される可能性がある ○ ビジネス要件とは無関係に、技術要件のみが変化する可能性がある 20 ● 技術的な再利用性だけを考え て、ビジネス要件的に1つで あるべきサービスを技術的に 分離するべきではない Order UI Service Order Logic Service Order Data Service Order database 機能要件 機能変更 機能変更 機能変更 失敗例

Slide 21

Slide 21 text

セキュリティ要件に基づくサービスの分離 ● クレジットカード情報など機密性の高 い情報を扱うゾーンを分離して、該当 ゾーン内のサービスは監査ログの取得 など、固有のセキュリティ要件を満た す実装を要求する 21 Payment service Payment database ゲートウェイ クレジットカード情報は このゾーン内のみに存在 Fee service Fee rules database クレジットカード情報を 扱う処理は別サービスに移譲

Slide 22

Slide 22 text

(参考)モジュール分割の考え方 ● モジュール分割は、次の3つの要求の「せめぎ合い」にさらされている (1) 変更するモジュールを減らしたい(関係あるものはまとめる)   → ビジネス要件からのマッピング ○ まとめすぎると (2) に矛盾する (2) 無関係な変更を減らしたい(関係ないものは分ける)   → 変更可能性からのマッピング ○ あらゆる可能性を予見することは不可能 (3) 再利用性を高めたい → 共通の技術要件を切り出してまとめる ○ 切り出しすぎると (1) に矛盾する 22 ここのバランスを重視 するのがマイクロ サービスの考え方 ここにフォーカス したのが SOA

Slide 23

Slide 23 text

新規アプリケーションの初期デザイン ● ビジネス課題の理解とビジネス要件の決定には、 ビジネス知識に基づいた深い分析が必要 ● サービスを分割すべきか判断がつかない場合は、 はじめは1つのサービスにまとめておく ○ 将来の分割に備えて、コードのモジュールやデー タ構造はビジネス要件に応じてできるだけ事前に 分割しておく ● ビジネス要件の変化に伴う機能追加の際は、「単 一責任の原則」を破らないよう、必要に応じて サービスの分割を検討する 23 Micro service API 既存の API に フィールドを追加 API 既存のサービスに API を追加 Micro service API 新しいマイクロ サービスを追加 機能追加のパターン

Slide 24

Slide 24 text

サービスの分割(機能の切り出し)の課題 ● API 移行:他のチームの協力が必要(機能拡張な ど変更のインセンティブを与える) ● データ移行:データの複製に必要なサービス停止 時間が取れない場合、データレプリケーションな ど特別なテクニックが必要 24 Micro Service A database Micro Service B database データの移行 Micro service アクセス先 /API の移行 マイクロサービス A の機能をマイクロ サービス B に移行する際の影響

Slide 25

Slide 25 text

移行を考えた API 設計 ● API 設計では、内部構造を隠蔽して「外 部に見せる必要のあるデータ」のみを公 開する ○ 必要最低限の機能を公開すること ● 「外部」にもいくつかのレベルがある事 に注意が必要 ○ エンドユーザーに見せる情報 ○ 管理者に見せる情報 ○ 他のサブシステムに見せる情報 ○ 同じサブシステム内のサービスに見 せる情報 25 Get asset Search asset Asset service Risk assessment Classification 外部 API 内部 API 同じサブシステム内の サービスにのみ公開 他のサブシステム にも公開

Slide 26

Slide 26 text

6. トランザクションの実装方法

Slide 27

Slide 27 text

マイクロサービスの具体例(オンライン投資サービス) 27 ● ユーザーが、株の売却リクエストを 出した場合の処理の流れ サービス手数料を 計算・引き落とし Order service A 社の株を 100 株売却 Stock transaction service 売却リクエストの 情報を記録 Order database Transaction database Fee service Fee rules database Market service Stock exchange 売却リクエストを 外部システムに送信 売却対象の株を ロック

Slide 28

Slide 28 text

● 例えば、右図のケース(売却対 象の株をロックした後に手数料 の引き落としに失敗)では、ど のように対処すればよい? ● 複数のサービスが関連していく れので、「トランザクションを ロールバックして、すべて無 かった事にする」というわけに はいかない・・・ 28 処理の流れと失敗ケース Create order Confirm reservation Charge fee Client Order service Stock transaction service Fee service Save order Reserve stock × Reserve stock

Slide 29

Slide 29 text

29 同期処理による Orchestration パターン ● Order service はプロセス に含まれるすべてのサービ スを理解する必要がある ● プロセスに含まれる処理が 変わるごとに、Order service に変更が必要 Create order Confirm reservation Charge fee Client Order service Stock transaction service Fee service Reserve stock Market service Confirm charge Place order Confirm place order Confirm order

Slide 30

Slide 30 text

30 イベントを用いた非同期処理(Choreography パターン) ● Order service は、オーダー受付イベントを発行して、その後の処理には関与しない ● 関連するサービスは、イベントをトリガーにして自分が担当する処理を実行する Order service Stock transaction service Fee service Market service Order created Stock reserved Fee charged Order placed 売却 リクエスト イベント発行

Slide 31

Slide 31 text

31 Saga パターン ● イベントによる非同期処理でトランザクションを実装 ● 失敗ケースのロールバックを含めて、一連のプロセスとして実装する ● 例えば、コーヒーショップでの注文処理を「失敗ケース」含めてプロセス化すると・・・ コーヒーを 注文する 代金を支払う コーヒーを 用意する コーヒーを 手渡す

Slide 32

Slide 32 text

32 Compensating アクションの導入 ● 失敗時のロールバックに相当する処理を「Compensating アクション」と呼ぶ ● 下図の例では、支払い処理を「取り消す」のではなく、それと等価な「返金処理」を実行 する(トランザクションにおけるロールバックでは、処理を完全に「なかった事」にする が、それとは異なる点に注意) コーヒーを 注文する 代金を支払う コーヒーを 用意する コーヒーを 手渡す カードが使えない 種類を間違えた 返金する 売り切れ

Slide 33

Slide 33 text

33 Compensating アクションの導入 ● Saga パターンでは、プロセスに含まれるそれぞれの処理に対して、それを取り消す Compensating アクションを用意しておく サービス アクション Compensating アクション Order A1: Create order C1: Cancel order A2: Update status (placed) C2: Update status (cancelled) Stock transaction A3: Reserve stock C3: Release stock Fee A4: Charge fee C4: Refund fee Market A5: Place order C5: Cancel order Order service Stock transaction service Fee service Market service Order created A1 Stock reserved Fee charged Order placed A3 A4 A5 A2

Slide 34

Slide 34 text

34 Compensating アクションの導入 ● 各サービスは、「失敗イベント」をトリガーにし て、必要に応じて Compensating アクションを実行 ○ 例えば、トランザクションごとにユニークな ID を発行して、各サービスは ID ごとに実行した アクションをデータベースに記録しておく ● 各サービスは自律的に動作するため、あらゆる処理 パターンにおいて、意図通りのロールバックが行わ れることを事前検証するのは大変 ● トレーシングの仕組みを導入して、エラー発生時の 動作を再現・確認できるようにすることが必要  (すべてのアクションを ID と共にログに記録して おく) Order service Market service Order failed C1, C2 Stock transaction service Fee service C3 C4

Slide 35

Slide 35 text

ワークフローマネージャーによるオーケストレーション 35 ● 自律分散プロセスではなく、ワークフロー マネージャーを導入して、プロセスの流れ をトラッキングする方法もある。 サービス手数料を 計算・引き落とし Order service A 社の株を 100 株売却 Stock transaction service 売却リクエストの 情報を記録 Order database Transaction database Fee service Fee rules database Market service Stock exchange 売却リクエストを 外部システムに送信 売却対象の株を ロック ワークフロー マネージャー 事前に定義した ワークフローを実行

Slide 36

Slide 36 text

36 Orchestrated saga ● 例外ケースを含めて、すべてのプロセス をワークフローマネージャーのジョブと して実装する ● ワークフローマネージャーのログから、 実際のプロセスの流れが確認できるの で、エラー発生時の確認が容易になる Stock transaction service Fee service Market service A1: Create order A3: Reserve stock A4: Charge fee A5: Place order C3: Release stock C4: Refund fee C2: Update status (cancelled) Failed Saga log ワークフロー マネージャー

Slide 37

Slide 37 text

37 Orchestrated saga ● ロールバック用の「Compensating トランザ クション」を別のジョブとして定義する方法 もある ● 正常系のジョブで例外が発生したら、 Compensating トランザクションを実行す る。この際、ロールバック対象のトランザク ションを特定する ID を指定して実行する。 ● Compensating トランザクションは、各サー ビスの対象ジョブに対する処理状況をチェッ クして、必要に応じて Compensating アク ションを実行する A1: Create order A3: Reserve stock A4: Charge fee A5: Place order Compensating トラ ンザクションを実行 Failed ワークフロー マネージャー C3: Release stock C4: Refund fee C2: Update status (cancelled) Compensating トランザクション

Slide 38

Slide 38 text

38 Compensating アクションが失敗した時は? ● Compensating アクションを含む個々のアクションは、単一のサービス内で実行されるもの なので、バックエンドデータベースのトランザクション機能を利用できる点に注意 ○ システムエラーに対しては、必要に応じてロールバック&リトライを実行すればよい ● それでも Compensating アクションが実行できない状況については、対応するビジネスプ ロセスを設計する ○ 例:ユーザーへの Refund ができない場合 → 手作業での返金プロセスを回すために、ユーザーと関連部門にその旨の通知を出す ○ 通知にも失敗したら??? → オペレーターにアラートを上げる、など

Slide 39

Slide 39 text

39 Saga の同時実行に伴う問題 ● 通常のトランザクションと異なり、 Saga では処理中の状態(データ)が 外部に見える(同期・非同期にかか わらず発生する問題) ● 特に、複数の Saga が同一のデータ を変更する場合の対処が必要 ● 図は、売却リクエストの Saga 実行 中に、リクエストのキャンセルが発 行されてキャンセル処理の Saga が 同時に実行される例 Order service A1: Create order A3: Reserve stock A4: Charge fee A5: Place order Stock transaction service Fee service Market service C3: Release stock C4: Refund fee C2: Update status (cancelled) 売却 リクエスト リクエスト キャンセル ?

Slide 40

Slide 40 text

40 Saga の同時実行への対応方法 ● コーヒーショップの例のように、現実の業務フローを想定して対応方法を考えるとよい ● Short-circuiting:Order サービスは、「処理中」ステータスのオーダーについては、キャ ンセルリクエストを受け付けない ● Interruption:Order サービスは、「処理中」ステータスのオーダーを「キャンセル受付済 み」ステータスに変更して、キャンセルリクエストに Ack を返す ○ その後、オーダー処理の Saga が完了して、オーダーのステータスを「完了済」に変更 するタイミングで、キャンセル受付済であれば、あらためてキャンセル処理の Saga を 実行する。(オーダー処理に関与するサービスは、オーダーのステータスをチェックし て「キャンセル受付済み」であれば、処理を中止してもよい)

Slide 41

Slide 41 text

41 トランザクションの実装パターン(まとめ) ● ACID 特性を持ったトランザクションを単一のサービス内で閉じて実行する ○ 最もシンプルで実装が容易 ○ サービスの粒度が大きくなり「単一責任の原則」を満たしにくくなる ● ワークフローマネージャー(ジョブ管理ツール)を用いたオーケストレーション ○ ロールバックの手順をジョブフローの一部として実装する必要がある ○ トランザクションの途中経過が外部に見える(Atomicity が担保されない) ● サービスコリオグラフィーによる非同期連携 ○ 「マイクロサービスの思想」に最も合った実装方法 ○ 実行パターンが複雑になり、問題発生時の対応が難しい ACID 特性の重要性 高 低 マイクロサービスのメリット 低 高

Slide 42

Slide 42 text

42 トランザクションの実装パターン(論理的な分類) オーケストレーター を使用するか サービス間の通信方法 (同期/非同期) クライアントから 見た同期性 コメント Phone Tag Saga 使用しない 同期通信 同期処理 Order Service がオーケス トレーターになる最初の例 Epic Saga 使用する 同期通信 同期処理 上の例に外部のワークフ ローマネージャーを追加 Anthology Saga 使用しない 非同期通信 非同期処理 イベントベースの 完全な非同期処理 Time Travel Saga 使用しない 同期通信 非同期処理 クライアントを待たせない ように実装を変更 Fairy Tale Saga 使用する 同期通信 非同期処理 クライアントを待たせない ように実装を変更

Slide 43

Slide 43 text

43 トランザクションの実装パターン(論理的な分類) オーケストレーター を使用するか サービス間の通信方法 (同期/非同期) クライアントから 見た同期性 コメント Fantasy Fiction Saga 使用する 非同期通信 同期処理 非同期処理に対応した オーケストレーターを 用いる特殊な実装 Parallel Saga 使用する 非同期通信 非同期処理 非同期処理に対応した オーケストレーターを 用いる特殊な実装 Horror Story 使用しない 非同期通信 同期処理 内部は非同期処理なのにク ライアントには同期的に見 せるという無理のある実装 ※ 詳細は参考書籍「Software Architecture: The Hard Parts」を参照

Slide 44

Slide 44 text

44 (参考)イベントソーシング ● 各サービスは、自身が管理するエンティティ(データ)を変更する事が本質的な役割 ● すべての変更をイベントとして発行して、イベントログに保存しておく ○ イベントログからデータベースが再構成できる(エンティティの作成から始まるすべて のイベントログをリプレイする) ● このイベントは、サービス間の非同期連携には 使用しない(データ実装の詳細を外部に公開し ないため) Micor service イベント保存 Database Event Store エンティティ更新 Event

Slide 45

Slide 45 text

45 (参考)イベントソーシング ● 考え方をもう一歩進めて、イベントログそのものをデータベースとして扱う方法もある ● Query ごとに、対象となるエンティティのすべてのログをリプレイする ○ 「エンティティのこれまでの変化」がビジネスデータとして必要な際に有効な方法 ○ リプレイのオーバーヘッドが問題になる場合は、定期的にチェックポイントをキャッ シュしておく Micro service Event イベント保存 Event Store エンティティ更新 エンティティ マネージャー イベントリプレイ エンティティ取得

Slide 46

Slide 46 text

7. データの Query 機能

Slide 47

Slide 47 text

47 データの Query 機能 ● 各サービスは、必要に応じて、自身が管理する Entity に対する検索機能を実装する ○ /customers?page=1&size=20 ページング機能 ○ /orders?customerIds=4,5,10,20 ID 指定での検索 ● 複数のサービスに跨ったデータを Join する場合は、そのための機能を個別に実装する ○ 図は API Gateway で実装する例で、この場合は、クライアント開発チームが実装する BFF パターンが推奨される Customer service Order service get all customers (1) for each customer: get order (2) (1) (2) get orders for all customers

Slide 48

Slide 48 text

48 検索用データの重複保存 ● 各サービスは、検索に必要なデータを重複保存することで、複数サービスに跨った Join を 避けることができる ○ 複製元のサービスがデータ更新イベントを発行して、複製先にデータを非同期に送る ○ 複製先のサービスが必要なタイミングで自発的に複製元のデータを取りに行く ● 重複データの一時的な不整合があることを前提としたアプリケーション設計が必要 Order service Order database Fee service Order id Stock id Units Fee details 782346 abc 25 ・・・・ get fee details create order get order details Fee service のデータを重複保存

Slide 49

Slide 49 text

49 検索専用データベースの追加(CQRS パターン) ● 例えば、イベントソーシングパターンを用いる場合、すべてのデータ変更 がイベントとして発行されるので、これを集めることで、事前に Join し た検索専用のデータベースを用意することが可能 queries event handlers commands Event Entity database Query database read create update delete 検索専用 データベース Service Customer service Order service Fee service Event Event Event Service queries event handlers Query database query テーブルのビューを読み出し 専用で公開する方法もある

Slide 50

Slide 50 text

50 データウェアハウスへのデータ挿入 ● イベントソーシングを用いると、データ分析用のデータウェアハウスへのデータ挿入が非同 期に実行できる Micro service イベント イベント イベント ・・・ Micro service Data warehouse Micro service Data transformation

Slide 51

Slide 51 text

51 (参考)データメッシュ ● 複数サービスのイベントをまとめて処理するシステムを作ると、各サービスの独立性が失わ れる(イベントの仕様変更が自由にできなくなる) ● サービスごとに CQRS パターンで外部公開用の Query 専用データベースを作成しておき、 データ分析チームは、これらのデータを集約してデータウェアハウスを構築する

Slide 52

Slide 52 text

52 Eventual consistency への対応 ● 例えば、クライアントが売却リクエスト送信直後にリクエストの状態を確認すると、リクエ スト情報がまだ存在しない可能性がある Order service Order information service Order created Order information 関連情報の収集 売却リクエスト リクエストの 情報を取得 リクエスト情報が まだ存在しない

Slide 53

Slide 53 text

53 Eventual consistency への対応 ● クライアント側の実装で対応する方法 ○ Polling / Push notification:処理結果が確定するのを待ってから UI を更新する ○ Optimistic update:処理が成功した前提で UI を更新して、後で結果を再チェックす る(例:Twitter の Like ボタン) Client Command Query Polling / Push notification Update Client Command Query Update Confirm or rollback

Slide 54

Slide 54 text

(参考)GraphQL について 54 ● GraphQL : Query Language for API ● クライアントが必要とするデータごとに API を用意する(もしく は、すべてのデータをまとめて返す API を用意する)のではな く、クライアント側が必要なデータを指定してリクエストを出す ● API ゲートウェイは、問合せ可能なデータ形式のスキーマを公開 ● リクエストを受けた API ゲートウェイは、複数のバックエンドか らデータを収集して、まとめた結果をクライアントに返す ● 主に外部クライアントにデータを公開する際に使用 ○ ネットワーク通信量を減らす為に、クライアントは必要最低 限のデータだけを要求する GraphQL ゲートウェイ Query 実行 複数サービスから データを収集

Slide 55

Slide 55 text

8. 信頼性を高める工夫

Slide 56

Slide 56 text

Reliability の依存関係 56 ● 同期呼び出しによる依存関係が深くなるほど、Availability は下がっていく Order service Fee service Market service Strategy service Micro service Micro service 稼働率 99.0% 稼働率 99.9% * 99.0% = 98.9% 稼働率 99.9% * 98.9% = 98.8%

Slide 57

Slide 57 text

Reliable なサービスを実現するためのゴール 57 ● 問題の発生源を理解して、あらかじめ対策しておく ○ 適切なヘルスチェックを実装することも開発者の責任 ● 障害の影響範囲を閉じ込めて、Cascading failure を防止する ○ Circuit breaker、Exponential backoff などの仕組みを利用する ● 自動化を用いて復旧の速度をあげる ○ 特にロールバックの自動化は重要

Slide 58

Slide 58 text

問題の発生源 58 ● レイヤーごとに問題を切り分け るためにも、レイヤーごとのメ トリックの取得が重要 外部データベース 呼び出し先の サービス DNS などの 外部サービス 自分自身(ハード ウェアを含む) 通信経路 呼び出し元の サービス Order database Micro Service A Micro Service B 外部 サービス Order service

Slide 59

Slide 59 text

ロードバランシングによる冗長化 59 ● ヘルスチェック用の標準 API を用意して、サービスごとに実装する ● 連続して fail する場合は、アラートを上げて原因を調査する @app.route('/ping', methods=["GET"]) def ping(): return 'OK' 最もシンプルな 実装例 ロード バランサー Micro Service A Micro Service B ヘルスチェック ヘルスチェックに fail した サービスを強制再起動

Slide 60

Slide 60 text

Micro Service B Cascading failures 60 ● ロードバランスされたタスクの1つが停止 すると、残りのタスクの負荷が上がり、タ イムアウトによるリトライが発生すること でさらに負荷が増加して、最終的に完全停 止する ● サービス停止の影響が呼び出し元のサービ スに伝搬していき、全面的なサービス停止 に到る Order service Fee service Market service × × Micro Service A × ロード バランサー Fee service Fee service × Order service ?

Slide 61

Slide 61 text

Cascading failures を防止する方法 61 ● Retry:Exponential back-off の導入 ● Fallbacks, caching, graceful degradation:依存サービスが停止時も稼働を続ける工夫 ● Timeouts and deadlines:Deadline のチューニングとプロパゲーション ● Circuit breakers:呼び出し先のサービスが不安定な際に、クライアントライブラリが自発 的に fail する仕組み ● Communication brokers:Pub/Sub を用いた非同期通信

Slide 62

Slide 62 text

リトライ 62 ● 単純なリトライが抱える問題:クライアントからのリクエストが突発的に増加すると・・・ → サービスの負荷が高まって処理が遅延し始める → リクエストの処理中にタイムアウトが発生して、クライアント側から強制的に切断 される(それまでの処理は無駄になる) → クライアントが単純にリトライしてくると、同じことが繰り返される ● タイムアウトが発生した場合は、呼び出し先サービスの負荷が下がるのを待ってからリトラ イする方がよい:Exponential back-off の導入 @retry(wait=wait_exponential(multiplier=1, max=5) + wait_random(0, 1), stop=stop_after_delay(5)) def all_prices(self): return self._make_request("prices") 5回目までは待ち時間を 2倍ずつ増やしていく 待ち時間に 乱数を加える 一定時間で リトライを諦める

Slide 63

Slide 63 text

フォールバック 63 ● Graceful Degradation:呼び出し先のサービスが利用できない場合、代替となる情報を返 す(例:パーソナライズド・レコメンデーション) ● Caching:以前にサービスを呼び出した際の結果をローカルにキャッシュしておき、サー ビスが利用できない場合は、キャッシュされた情報を返す ● Functional Redundancy:同等の情報を取得できる複数のサービスを用意する Stock service Market service × Market service B 代替サービスを利用 キャッシュデータ を利用 代替データを返却

Slide 64

Slide 64 text

タイムアウトとデッドライン 64 ● 一般に、同期 API の呼び出しを行ったクライアントは、一定時間以内に応答がないとタイ ムアウトして応答待ち状態を打ち切る。この時、クライアントのタイムアウト後もサー バーは処理を続けるが、この処理は無駄になってしまうので、タイムアウトが短すぎない ように注意が必要 ● さらに、クライアントから「デッドライン」情報をサーバーに受け渡しておき、サーバー は、デッドラインまでに応答できなかった場合はそこで自発的に処理を中断するという実 装も可能 ● サーバーがさらに他のサービスを呼び出す場合も同様にタイムアウトとデッドラインを指 定するが、大元のクライアントよりも長いタイムアウトを設定しても意味がない

Slide 65

Slide 65 text

タイムアウトとデッドライン 65 ● 正常時のレスポンスタイムの分布を見 て、適切なタイムアウトを設定する ● 大元のクライアントのデッドライン情 報は、絶対時刻で保持して、サービス 間で引き継いでいく(gRPC は自動で 絶対時刻への変換を行ってくれる) サービスが Unresponsive な 場合、応答が無駄に遅れる A 15s B 5s 10s C 15秒以内に 応答が必要 10秒以内に 応答が必要 ビジネス的に応答が遅すぎても 意味が無い場合、早めにエラー を返した方がよい場合もある サービスの応答を待ちきれず に fail する場合がある

Slide 66

Slide 66 text

サーキットブレーカー 66 ● 呼び出し側のクライアントライブラリ に実装する機能 ● 一定回数連続して fail したサービス は、それ以降(タイムアウトを待たず に)即座に fail する ● 一定時間待った後に、サービスの状態 をチェックして問題がなければ、通常 状態に戻る アプリケーション ライブラリ Micro Service A × 「Close」状態の時は ライブラリが即座に fail 応答する 連続して fail すると 接続を「Close」する サービスの状態を チェックして復活した ら接続を「Open」する

Slide 67

Slide 67 text

レートリミット 67 ● 呼び出されるサービス側で実装する機能 ● リクエストが一定量を超えた際に、タイムアウトを待たずに即座にエラーを返す ● 一定時間リクエストを控えるように「バックプレッシャー」メッセージを返す方法もある (クライアントがメッセージを理解するように作られている場合) Micro Service A 処理中 処理中 × 高負荷時は即座に fail

Slide 68

Slide 68 text

Pub/Sub を用いた非同期通信 68 ● キュー内部のイベントがロストしない限り、処理は継続していつかは完了する ● デメリットもあるので注意 ○ サービス連携のロジックが複雑になる → トレーサビリティが必要 ○ Pub/Sub が SPOF になる → Pub/Sub キューのモニタリングが必要 Order service Market service イベント イベント イベント ・・・ イベント発行 イベント取得 Fee service

Slide 69

Slide 69 text

フレームワーク / サービスメッシュの利用 69 ● リトライ、タイムアウト、サーキットブレーカー、レートリミットなどの通信処理に関わ る機能は、開発者が個別に実装するのではなく、これらの機能を提供する共通のライブラ リーやフレームワークを利用する ● もしくは、サービスメッシュ(通信 処理をフックして、アプリケーショ ンに対して透過的に上記の機能を追 加する Proxy サービス)を利用し て、すべてのサービスの通信処理を 一元管理する https://istio.io/latest/docs/concepts/what-is-istio/

Slide 70

Slide 70 text

負荷テスト 70 ● 将来のアクセス数の増加を見積もった上で、負荷テストを行う ○ 依存するサービスが連動してスケールすることを確認する ● サービス個別のテストと複数サービスを連携させたテストの両方を行う ○ リリース直前の負荷テストで問題が発覚しても手遅れになる ● プロダクション環境を想定した総合的な負荷テストは定期的に実施する ○ 特定のサービスのスケーラビリティが向上した結果、依存先のサービスの負荷が上 がって不安定になるなども起こり得る点に注意が必要

Slide 71

Slide 71 text

(参考)Chaos Engineering 71 ● 意図的に障害を発生させて、エンドユーザーに見える影響がないことを確認する手法 ○ (エンドユーザーに見える影響がない)「安定状態」を定義する ○ システムの耐障害性を分析して、ある障害を導入しても安定状態が保たれるという仮 説を立てる ○ 実際に障害を導入したグループと導入しないグループのそれぞれからデータを取得し て、「仮説が正しくない」という証拠を探す ○ 証拠が発見された場合は、仮説の前提に誤りがあった、つまり、耐障害性の設計に問 題があったという事なので問題点を修正する

Slide 72

Slide 72 text

Thank You.