-
Notifications
You must be signed in to change notification settings - Fork 0
MS_CloudApplicationArchitectureGuide
- 戻る(アーキテクチャ設計)
- アプリケーション・アーキテクチャ
- Azure Well-Architected Framework
- クラウド アプリケーション アーキテクチャ ガイド
- 参照アーキテクチャ
- クラウド設計パターン
- 参考から目次を抜いて体系化した。
- 結構、網羅が出来て来た(オンデマンドで追記予定)。
補足(3 ページの棲み分け): Azure Architecture Center の内容は
本 Wiki では次の 3 ページに分かれている。混同しやすいので整理しておく。
ページ 扱うもの 粒度 Azure Well-Architected Framework 評価の観点(5 本の柱) 判断基準 本ページ 選択肢の体系(スタイル・原則・技術選定・ベスト プラクティス) 設計手順 参照アーキテクチャ 具体的な構成例 実装の出発点 さらに、個々の実装手法は
クラウド設計パターンにまとまっている。
以下のような立て付けになっている。
Azure Batch (HPC) 推しらしい。
- パブリッシュ/サブスクライブ
- イベント ストリーム モデル
- イベントがログに書き込まれる。
- クライアントはいつでも参加でき、イベントを再生
- ストリーム内でクライアントの位置を進めるのは、クライアントの役割
補足(この 2 つは別物): 同じ「イベント」でも、
メッセージング型(Pub/Sub)とイベント ストリーム型は
性質がまったく異なる。ここを混同すると製品選定を誤る。
Pub/Sub(メッセージ) イベント ストリーム(ログ) イベントの寿命 消費されたら消える 保持期間まで残る 位置の管理 ブローカー側 クライアント側(オフセット) 再生 基本的に不可 可能(過去から読み直せる) 後から参加 過去分は受け取れない 過去分から読める Azure の例 Service Bus、Event Grid Event Hubs(Kafka 互換) 「クライアントの位置を進めるのはクライアントの役割」という
本文の一文が、この違いの核心を突いている。
- 真面目にやると沼。
- SOA の REST WebAPI 版(雑)
補足(「真面目にやると沼」は正しい): この身も蓋もない一言は
実務上きわめて重要な指摘である。マイクロサービスは
技術の選択ではなく組織と運用の選択であり、
分割した瞬間に次がすべて必要になる。
単一アプリなら不要だったもの サービス間通信の失敗処理(Polly、再試行、サーキット ブレーカー) 分散トランザクションの回避(Saga、補正トランザクション、結果整合性) 分散トレーシング(1 リクエストが何十サービスを跨ぐ) サービス ディスカバリ、API ゲートウェイ サービスごとの CI/CD とバージョン互換管理 データの重複と同期(DB を共有できない) したがって現在の一般的な指針は
**「モノリスから始めて、必要になった部分だけ切り出す」**である
(モジュラー モノリス)。
「SOA の REST 版」という要約も、
粒度と自律性の度合いが違うだけという意味では的を射ている。
アプリケーションを論理層と物理層に分離
補足: 従来型の分割であり、
アプリケーション・アーキテクチャで扱う
物理 2 層/3 層の議論がそのまま当てはまる。
クラウドへの移行(リフト&シフト)で最も選ばれる形でもある。
- Web ロール、Worker ロール
- 必要に応じて、キュー
補足: 「Web ロール/Worker ロール」は
Azure Cloud Services(クラシック)の用語である
(同サービスは 2024 年 8 月に提供終了)。
ただし構成としての Web-Queue-Worker は現役であり、
現在は次のように実装する。
役割 現在のサービス Web App Service、Container Apps キュー Storage Queue、Service Bus Worker Functions、WebJobs、Container Apps Jobs 重い処理をキュー経由で後段に逃がすという形は、
応答時間の確保と負荷平準化の両方に効く、最も費用対効果の高い構成である。
- Polly + Pollyで足りていないサーバ実装
- フェイル・オーバー(
OTR_HighReliabilityDesignPoints.md) - パターン
- その他
- フォールト挿入でテストする。
- Chaos エンジニアリングを利用する。
- 従来型のフェイル・オーバーに加え。
- システム・データのパーティション分割
- 複数のリージョンにデプロイ
- geo レプリケーション
- Traffic Manager(Azureの高可用性設計)
- トランザクション(ACID 特性)以外の実現方法を検討する。
- その他
- ドメイン イベントと同期
- 読取 / 書込ワークロードの競合を削減
- 別のインスタンス
- 冪等操作の設計
- リーダー選定
- 並列の分散アルゴリズム(ビッグデータ系の分散)
補足(「調整」= coordination がクラウドで最も高くつく): この原則の原文は
"Minimize coordination" である。
インスタンス間で合意を取る処理(ロック、分散トランザクション、
順序保証)は、台数を増やすほど待ちが増えるため、
スケールアウトの効果を打ち消してしまう。【調整あり】 台数を増やす → 調整のオーバヘッドが増える → 頭打ち 【調整なし】 台数を増やす → そのままスループットが伸びるしたがってクラウドでは、ACID を諦める代わりに
- 楽観排他(衝突は稀と仮定して、起きたら弾く)
- 冪等性(同じ操作を何度実行しても同じ結果)
- 結果整合性(今すぐ一致していなくてよい)
- 補正トランザクション(ロールバックの代わりに打ち消す操作)
という手段で置き換える。
特に冪等性は、再試行を前提とする以上
避けて通れない要件になる(Polly)。
垂直方向・水平方向にスケールできるように設計
- 垂直方向
システム、アプリで構成する。- ボトルネックの特定、ワークロードの分解など。
- タスクのオフロード(バックグラウンド・ジョブ化)
- 水平方向
クラウド機能で構成する。- サーバー・ステートレス
- スケールアウト・スケールイン
- 自動スケール
補足(ステートレス化が全ての前提): 水平方向の項にある
「サーバー・ステートレス」が、
実はこの節で最も重要な一行である。
サーバがセッション状態を保持していると、
- スケールアウトしても同じサーバに戻す必要がある(スティッキー セッション)
- スケールインや再起動で状態が消える
ため、水平方向のスケールが機能しない。
ASP.NET では、セッション状態を
**プロセス外(SQL Server / Redis)**に出すか、
そもそもサーバに持たない設計にする
(ASP.NETの状態管理方式)。
スケール・アップに限界があるので、パーティション分割。
- システム
- データベース
- キューまたはメッセージ バス
- App Service Web アプリ
- データベース
- 行方向
- 列方向
簡単に言うとテーブルの分割 - 機能的
請求書テーブルと製品在庫テーブルを分割
運用チームが必要なツールを得られるようにアプリケーションを設計
なるべく、PaaS / FaaS, SaaSを選択。
- データ ストアの選択
- 調整を最小限に抑える
- その他
- 開発チームのスキルセット
- コンテキスト境界を確認(DDD)
SOA っぽい(疎結合)。
- 高凝集と疎結合
- サービスを個別にデプロイ
- 共通機能を専用のサービスにオフロード
- ゲートウェイにはドメイン ナレッジを実装しない
- ドメイン ナレッジをカプセル化
- オープン インターフェイスを公開
- 非同期メッセージングを使用
-
クリーン・アーキテクチャ(.NET開発基盤部会 Wiki)的な
(抽象インフラストラクチャをドメイン ロジックと分離)
- 成長に対応可能な計画とコストの管理
- 目標復旧時間 (RTO)、目標復旧時点 (RPO)、最大許容停止時間 (MTO)
- サービス レベル アグリーメント (SLA) とサービス レベル目標 (SLO)
- 要件
- 機能要件と非機能要件
- ワークロード別に分けて要件を判断
- ビジネス ドメインを中心にアプリケーションをモデル化(DDD)
補足(合成 SLA に注意): 複数のマネージド サービスを
直列に組み合わせると、SLA は掛け算で下がる。App Service(99.95%) × SQL Database(99.99%) × Storage(99.9%) ≒ 99.84% → 年間の許容停止時間は 約 14 時間「各サービスが 99.9% 以上だから全体も 99.9%」は成立しない。
冗長構成(並列)を入れると逆に上がるため、
直列か並列かを意識して合成 SLA を計算する必要がある。
適切なモンを使えと。
- ホスティング環境
- 参照アーキテクチャ
適切なモンを使えと。
- RDBMS(SQL Serverなど)
- NoSQL(.NET開発基盤部会 Wiki)
- , etc.
※ 多言語パーシステンスと言うらしい。
補足(「適切なモンを使え」の判断軸): 突き放した書き方だが、
実際の判断軸はそれほど多くない。
問い 分岐 強い整合性が要るか 要る → RDBMS。要らない → NoSQL も可 クエリの形は決まっているか 決まっている → キー値/ドキュメント。自由 → RDBMS スキーマは変化するか よく変わる → ドキュメント DB 想定データ量とスループット 単一インスタンスの限界を超えるか 関連(JOIN)が中心か 中心 → RDBMS、グラフ なお、「多言語パーシステンス」を字義どおりに実践すると
運用対象の DB が増えて破綻しやすい。
既定は RDBMS、明確な理由があるものだけ別の DBという
保守的な運用が現実的である。
負荷分散については、NLBの項が参考になる。
(NLB を使えとは言っていない)
補足(Azure の負荷分散サービスの選び分け): 4 種類あり、
OSI 参照モデルのどの層で振るかと
グローバルかリージョン内かで整理できる。
サービス 層 範囲 主な用途 Traffic Manager DNS グローバル リージョン間の振り分け・DR Front Door L7(HTTP) グローバル CDN + WAF + グローバル負荷分散 Application Gateway L7(HTTP) リージョン内 URL パス ベース振り分け、WAF、SSL 終端 Load Balancer L4(TCP/UDP) リージョン内 非 HTTP を含む一般的な分散
色々あるらしい。
- Azure Service Bus
- Event Grid
- Event Hubs
補足(3 つの使い分け): 名前が似ているが役割は明確に違う。
サービス 種別 特徴 典型的な用途 Service Bus メッセージ 順序保証、トランザクション、デッド レター、FIFO 業務処理の確実な受け渡し Event Grid イベント通知 軽量、Push 配信、Azure リソースの変化に反応 リソース作成をトリガに処理を起動 Event Hubs イベント ストリーム 大量取り込み、再生可能、Kafka 互換 テレメトリ・ログの収集 判断のこつは、
**「取りこぼしたら業務が成立しないか(メッセージ)」**と
**「大量に流れてくる事実の記録か(イベント)」**の区別である。
- サーバー実装
-
スケーラビリティ、可用性、応答性の維持
- 非同期サポート
- ステートレス
- クライアントの
・追跡と調整
・HTTP Keep-Alive の管理
- Web API
- テスト
- 公開と管理(Azure API Management)
-
スケーラビリティ、可用性、応答性の維持
- クライアント実装
- Polly
- クライアント SDK の開発
-
API Gatewayの利用
- 一般的機能(.NET開発基盤部会 Wiki)(API Gatewayの該当節を参照)
- 監視
- Azure API Management
- ASP.NET Application Insights(Azureの監視と管理)
- その他
- ドキュメント
水平方向のスケーリング
- アプリケーション設計
- クラウド機能の活用
- PaaS 組込の機能
- Azure Monitor(上記の PaaS と組み合わせて利用)
- 関連のあるパターンとガイダンス
- パターン
- 監視と診断(アーキ・ガイド)
補足(自動スケールは万能ではない): 実務で問題になるのは
スケールが間に合わないケースである。
- VM やインスタンスの起動には数分かかる。
数十秒で立ち上がる急激なスパイクには追随できない。- 判定に使うメトリック(CPU 等)は遅行指標であり、
混雑してから増え始める。対策としては、
手段 内容 キューによる平準化 スパイクをキューで吸収し、Worker は一定速度で処理 予測に基づくスケール 業務のピーク時刻が既知ならスケジュール スケールを併用 事前ウォームアップ 最小インスタンス数を高めに設定 スケールインの緩和 クールダウンを長めにし、フラッピングを防ぐ また、後述のチューニングの節にあるとおり、
自動スケールは根本の問題を隠す副作用がある
(遅いクエリを台数で押し切ってしまう)。
- トリガ
- イベント ドリブン トリガー
- スケジュール ドリブン トリガー
- その他
- 結果の返送方法
- ホスティング環境
- パーティション分割(システム)
- 競合(リソース)
- 調整、回復性
タスクの協調動作と補正 - スケーリング
- 関連のあるパターンとガイダンス
- 注意点
- 高可用性の実装、スケーラビリティ・パフォーマンスの向上
- コンカレンシーの管理、最終的な整合性
- データをキャッシュするタイミングを決める
- データを効率的にキャッシュする方法を決める
- 非常に動的なデータをキャッシュする
- キャッシュ内のデータの有効期限を管理する
- クライアント側キャッシュ内のデータを無効にする
- Redis Cache(.NET開発基盤部会 Wiki)
補足(キャッシュの難所は「消し方」): 上の注意点が
有効期限・無効化に集中しているとおり、
キャッシュで難しいのは載せ方ではなく捨て方である。
問題 内容と対策 キャッシュ スタンピード 期限切れの瞬間に全リクエストが DB に殺到する。有効期限をずらす(ジッター)、再生成を 1 本に絞る 古いデータの表示 更新時に明示的に無効化する(write-through / write-behind) キャッシュ障害時の雪崩 キャッシュが落ちると全負荷が DB へ。DB 側が耐えられるかを確認しておく 何でも載せる ヒット率が下がりメモリを浪費する。参照が多く更新が少ないものに絞る
- ルーティングとバージョン管理
- キャッシュ制御
- セキュリティ
- CDN フォールバック
- パーティションの設計
- 水平的パーティション分割(行方向、シャーディング)
- 垂直的パーティション分割(列方向)
- 機能的パーティション分割(機能的)
- 関連のあるパターンとガイダンス
補足(パーティション キーの選択が全てを決める): 水平分割では
キーの選び方を後から変えるのが極めて困難である
(全データの再配置が必要になる)。選定時の観点は次のとおり。
観点 内容 均等に分散するか 偏るとホット パーティションが生まれ、そこだけ飽和する クエリがキーを含むか 含まないと全パーティションへの問い合わせ(fan-out)になる 跨ぐトランザクションが要るか 要るなら分割位置が誤っている可能性が高い 時系列キーの罠 日付をキーにすると最新パーティションに書き込みが集中する SQL Server のパーティションは
単一インスタンス内の分割であり、
ここでいうシャーディング(インスタンスを跨ぐ分割)とは
目的が異なる点にも注意する
(SQL Server の Elastic Scale とプール)。
- Azure SQL Database のパーティション分割
- Azure Table Storage のパーティション分割
- Azure Blob Storage のパーティション分割
- Azure Queue Storage のパーティション分割
- Azure Service Bus のパーティション分割
- Cosmos DB のパーティション分割
- Azure Search のパーティション分割
- Azure Cache for Redis のパーティション分割
- Azure Service Fabric のパーティション分割
- Azure Event Hubs のパーティション分割
- データ ストアを含め、アプリケーション コンポーネントの複数の独立したコピーをデプロイする。
- 個々のコピーは "スタンプ" と呼ばれ、"サービス ユニット" または "スケール ユニット" と呼ばれる場合もある。
- このアプローチにより、
- 顧客データを分離でき、
- ソリューションのスケーラビリティが向上し、
- インスタンスを複数のリージョンにデプロイすることが可能となる。
- 自動化された完全に反復可能なデプロイ プロセスを使用する
補足: マルチテナント SaaS で
テナントごと(または一定数のテナントごと)に一式を丸ごと複製する方式である。
分離とスケールを同時に得られる一方、
スタンプ数だけ運用対象が増えるため、
「自動化された完全に反復可能なデプロイ」が
努力目標ではなく前提条件になる
(クラウドのインフラ自動化)。
- 正常性の監視
- 可用性の監視
- パフォーマンスの監視
- セキュリティの監視
- SLA の監視
- ユーザー操作の監査
- 利用状況の監視
- 問題追跡
- 操作のトレース
- リリースのデバッグ
- 監視と診断のパイプライン
- 監視と診断のデータ ソース
- アプリケーションのインストルメント化
- データの収集と保存
- データの分析と問題の診断
- データの視覚化とアラートの生成
補足(分散システムでは「監視」から「可観測性」へ): 単一サーバであれば
ログを見れば済んだが、リクエストが複数サービスを跨ぐと
どこで遅くなったのかがログからは分からない。
そこで、次の 3 本を揃えるのが現在の標準的な考え方である。
柱 内容 Azure での実装 メトリック 数値の時系列(CPU、応答時間、キュー長) Azure Monitor ログ 事象の記録 Log Analytics トレース 1 リクエストの経路と各区間の所要時間 Application Insights(分散トレース) 特に分散トレースは、
相関 ID を全サービスに引き回すことで
「A → B → C のうち B が遅い」を可視化する。
これが無いと、後述の「カスケード エラー」の切り分けができない。
現在は OpenTelemetry が標準規格となり、
Application Insights もこれに準拠している。
- Azure サービスの再試行機能
https://docs.microsoft.com/ja-jp/azure/architecture/best-practices/retry-service-specific
- 標準の再試行機構が存在するかどうかを調べる。
- 操作を再試行するのが妥当かどうかを判断する。
- 適切な再試行回数と間隔を決める。
- アンチパターンを避ける。
- 再試行の戦略と実装をテストする。
- 再試行ポリシーの構成を管理する。
- 一過性の障害と一過性ではない障害を記録、追跡する。
- 絶えず失敗する操作に対処する。
補足(再試行は「やり過ぎると障害を悪化させる」): 一覧の
「アンチパターンを避ける」の中身が実務では最重要である。
アンチパターン 何が起きるか 多層での再試行 ライブラリ・アプリ・ゲートウェイが各 3 回 → 実際は 27 回。負荷が指数的に増える 固定間隔での再試行 全クライアントが同時に再試行し、回復直後に再び潰す(thundering herd) 再試行すべきでないものを再試行 認証エラー・不正な入力(4xx)は何度やっても失敗する 冪等でない操作の再試行 二重登録・二重課金 上限が無い 障害が終わらない 正しい形は
指数バックオフ+ジッター(乱数)+上限+サーキット ブレーカーである。
サーキット ブレーカーは、失敗が続いたら
一定時間まったく呼びに行かないことで
相手の回復を待ち、自分側のスレッドも守る。
.NET では Polly がこれらを提供する。
- テレメトリでメトリックを収集できるようコードをインストルメント化する。
- 平均値によって、外れ値が解らなくなる可能性があるので、
90/95/99 パーセンタイル値を閾値にも監視する。 - 一度に 1 つのボトルネックに取り組む
(ボトルネックを取り除くと、次の別のボトルネックが明らかになる) - エラーと再試行は、パフォーマンスに大きな影響を与える可能性がある。
- 並列、分散化できる可能性を探す(パーティション分割)。
- 1 つの操作を包括的にエンドツーエンドで把握するのは困難になる。
- 一貫性のあるビューを取得するにはログとメトリックを 1 か所で集計する。
- 自動スケールは、
- 元の問題が解らなくなる可能性がある。
- また、スケールアウト・スケールインの閾値も解らなくなる。
- カスケード エラー
- エラーが連鎖して根本原因とは
異なるコンポーネントでシグナルが現れる - 上流のエラーが原因で下流で障害が発生する。
- エラーが連鎖して根本原因とは
移行メモ(体裁): 原典の「90/95/99 パーセンタイルパーセンタイル値」は
「パーセンタイル」が重複していたため修正した。
また「基の問題」は「元の問題」とした。
補足(「一度に 1 つのボトルネック」の意味): これは手順の指示であると同時に、
性能問題の性質そのものを述べている。
システムの処理速度は最も遅い箇所で決まるため、
そこを直すまで他をいくら速くしても全体は変わらない。
そして直した瞬間、次に遅い箇所が新しい上限として現れる。【改善前】 CPU:30% DB:95% ← ここが上限 NW:20% 【DB改善後】CPU:85% ← 新しい上限 DB:40% NW:25%したがって、
- 同時に複数を直さない(どれが効いたか分からなくなる)
- 1 つ直すたびに測り直す
(JmeterによるWebアプリの負荷テスト)- 改善目標に達したらそこで止める(際限が無い)
https://docs.microsoft.com/ja-jp/azure/architecture/performance/distributed-transaction
https://docs.microsoft.com/ja-jp/azure/architecture/performance/backend-services
https://docs.microsoft.com/ja-jp/azure/architecture/performance/event-streaming
- ビジー状態のデータベース
- ビジー状態のフロント エンド
- 頻度の高い I/O
- 余分なフェッチ
- 不適切なインスタンス化
- モノリシック永続化
- キャッシュなし
- 同期 I/O
補足(各アンチパターンの中身): 名称だけでは分かりにくいため補足する。
いずれもクラウド固有ではなく、従来から性能問題の定番である。
アンチパターン 内容 対処 ビジー状態のデータベース ロジックを DB(ストアド)に寄せ過ぎ、スケールしにくい層に負荷が集中 アプリ層に戻す ビジー状態のフロント エンド Web 層で重い処理を同期実行し、要求を捌けなくなる キュー+ Worker に逃がす 頻度の高い I/O(Chatty I/O) 細かい呼び出しの多発。ラウンド トリップの累積が支配的になる まとめて取得する 余分なフェッチ(Extraneous Fetching) SELECT *や全件取得後にアプリで絞る必要な列・行だけ取る 不適切なインスタンス化 HttpClient等を毎回 new する(ソケット枯渇の原因)使い回す( IHttpClientFactory)モノリシック永続化 全データを 1 つのストアに押し込む 特性ごとに分ける キャッシュなし 同じ結果を毎回作り直す キャッシュを入れる 同期 I/O I/O 待ちでスレッドを占有し、スレッド プールが枯渇 async/awaitこのうち Chatty I/O と 同期 I/O は
.NET アプリで特に頻出する。
前者は ADO.NET・ORM の使い方
(ADO.NET vs ORM)、
後者は初回が遅い!などと併せて理解しておきたい。
- Azure Architecture Center
- Azure アプリケーション アーキテクチャ ガイド
https://docs.microsoft.com/ja-jp/azure/architecture/guide/
- Azure アプリケーション アーキテクチャ ガイド
https://docs.microsoft.com/ja-jp/azure/architecture/guide/architecture-styles/
https://docs.microsoft.com/ja-jp/azure/architecture/guide/design-principles/
https://docs.microsoft.com/ja-jp/azure/architecture/guide/technology-choices/
https://docs.microsoft.com/ja-jp/azure/architecture/best-practices/
https://docs.microsoft.com/ja-jp/azure/architecture/performance/
- Scott Leberknight 氏、多言語パーシステンスについて語る
https://www.infoq.com/jp/news/2009/08/leberknight-polyglot-persistence/
- RESTFul APIs において HATEOAS を使用する利点
https://www.infoq.com/jp/news/2009/04/hateoas-restful-api-advantages/ - Rails の API に HATEOAS を散りばめてみる : REST の拡張、HATEOAS の詳解と実装例 | POSTD
https://postd.cc/sprinkle-some-hateoas-on-your-rails-apis/
補足(HATEOAS は普及しなかった): HATEOAS は REST の
成熟度モデル(Richardson Maturity Model)のレベル 3にあたり、
応答に次に取り得る操作のリンクを含めることで
クライアントが URL を組み立てずに済む、という考え方である。ただし実際にはほとんど普及しなかった。
クライアント側にリンクを解釈する仕組みが必要な割に
得られる疎結合が限定的だったためで、
現在の Web API は**レベル 2(リソース+ HTTP メソッド)**に
OpenAPI による仕様記述を組み合わせる形が主流である
(WebAPI、REST)。
Tags: 移行, アーキテクチャ, クラウド系開発
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。