個人的なメモ

Tomohiro Suzuki @hiro128_777 のブログです。Microsoft MVP for Developer Technologies 2017- 本ブログと所属組織の公式見解は関係ございません。

Orleans の概要と基本構成のデプロイまで(目次)

Orleans は何を解決するために生まれ、どのようなフレームワークなのか

記事はこちらです。
hiro128.hatenablog.jp
 

Orleans の 構成と重要なプリミティブ(構成要素)

記事はこちらです。
hiro128.hatenablog.jp
 

Orleans の構成要素:フロントエンド

記事はこちらです。
hiro128.hatenablog.jp
 

Orleans の構成要素:グレイン

記事はこちらです。
hiro128.hatenablog.jp
 

Orleans の構成要素:サイロ

記事はこちらです。
hiro128.hatenablog.jp
 

Orleans の基本的な構成を App Service にデプロイする(1)構成の確認

記事はこちらです。
hiro128.hatenablog.jp
 

Orleans の基本的な構成を App Service にデプロイする(2)VNet の設定

記事はこちらです。
hiro128.hatenablog.jp
 

Orleans の基本的な構成を App Service にデプロイする(3)App Service の プライベートポート

記事はこちらです。
hiro128.hatenablog.jp
 

Orleans の基本的な構成を App Service にデプロイする(4)動作確認

記事はこちらです。
hiro128.hatenablog.jp
 
 

Orleans の 構成と重要なプリミティブ(構成要素)

Orleans の構成

Orleans の構成は以下のようになっています。

Orleans のクラスターは1つの巨大なコンピューターのように取り扱われます。
スケールアウトやアップグレードによるインスタンス追加や、障害時のインスタンスの突然停止に対応するため、
サイロは、Container Apps、App Service、マネージド k8s などの SaaS にデプロイする前提となっています。
 
Orleans の基本的な動作は、以下の通りです。

  • REST API が定義されたエンドポイントであるフロントエンドで、HTTP リクエストを受信します
  • リクエストは適切なグレインにルーティングされグレインのメソッドが呼び出され、データがメモリ内にない場合はDBから取得し、レスポンスを返します

 
 

Orleans の重要なプリミティブ(構成要素)

Orleans の全体像を把握するために、重要な構成要素はフロントエンド、グレイン、サイロの3つです。


 

プリミティブ 概要
Frontend Orleans の世界と外部とのゲートウェイ
HTTP Request/Response と Grain Call/Response の変換を行います。
Grain フロントエンドから呼び出す際のID(identity)、業務ロジック実装(behavior)、インメモリの状態(state)で構成されるエンティティ
クラスターでホストされているグレインは、1 つのプロセス内にあるかのように相互に通信できます。
Silo グレインをホストするインスタンス
クラスターとして実行され、サイロ同士は互いに連携して作業を分散し、失敗を検出して復旧できます。

 
 
次の記事はこちらです。
hiro128.hatenablog.jp
 
 

Orleans の基本的な構成を App Service にデプロイする(2)VNet の設定

構成図

構成では、フロントエンドのみパブリックアクセス可能、2つのサイロとストレージはプライベートアクセスのみ可能としますが、プライベートアクセスはリージョン VNet 統合とプライベートエンドポイントで実現します。
そのための VNet の設定は以下のようになります。
 

アドレス空間


 

接続デバイス(プライベートエンドポイント)


 

サブネット


 
 
次の記事はこちらです。
hiro128.hatenablog.jp
 
 

Orleans は何を解決するために生まれ、どのようなフレームワークなのか

古典的なWebアプリの問題点

  • 読み取り要求のたびにデータベースにアクセスするため、 データベースの負荷が大きい
  • アプリのインスタンスの状態をお互いが把握できず、データベースへの書き込み競合が発生する
  • 各々のアプリのインスタンスが状態を個別に持ちリソースの効率が悪い。

 

 
 

対策としてキャッシュ、キューなどを追加するも新たな問題が

  • 読み取り要求のデータベース負荷対策としてキャッシュを追加。代わりに、キャッシュの一貫性の問題が発生
  • データベースへの書き込み競合対策としてキューを追加。代わりに、非同期書き込みの待ちが発生


 
問題は軽減しているが、インフラを追加して厄介ごとをオフロードしているだけなので根本的な解決ではない…
スケーラビリティも確保できていない
 
 

根本解決のためのアプローチ(Orleans のコンセプト)

  • アプリインスタンスがお互いに会話し互いに連携することで分散アプリの問題を解決する
  • データベースを唯一の「真実のソース」とし、キャッシュやキューなどの余計なインフラ自体をなくす


 
これによって

  • 高スループット
  • 低レイテンシー
  • 高スケーラビリティ

が確保できることをねらって開発されました。
 
 

結局 Orleans とは

Orleans は、アプリケーションのフロントエンド と永続化ストレージ(DBなど)の間のステートフルでスマートな中間層を提供するフレームワークです。

 

Orleans のメリット

  • DB への書き込み競合の考慮が不要
  • キャッシュ不要
  • Appインスタンス間の一貫性考慮が不要
  • Appインスタンスを追加すれば自動でクラスターが構成される

 
分散アプリケーションを開発するときに発生するいろいろな面倒なことについて、
いい感じで面倒を見てくれるので、メリットだけを享受することができます。
Teams など超大規模サービスでの実績もある安定したフレームワークです

 
 
次の記事はこちらです。
hiro128.hatenablog.jp
 
 

Orleans の基本的な構成を App Service にデプロイする(1)構成の確認

目次

 
前の記事はこちらです。
hiro128.hatenablog.jp
 

方針

 
Orleans の基本的な構成のデプロイを App Service にデプロイしてみましょう。
 
基本的な方針を以下の通りとします

  • フロントエンドのみパブリックアクセス可能
  • 2つのサイロとストレージはプライベートアクセスのみ可能とする
  • プライベートアクセスはリージョン VNet 統合とプライベートエンドポイントで実現する

 
実際の構成は以下のようになります。
それぞれのリソースが Orleans のどの構成要素に相当するか分かりやすくするため概念図との対比にしました。
 
 

構成

 

 

役割 リソース パブリック
アクセスの受信
プライベート
アクセスの受信
プライベート
エンドポイント
リージョン
VNet 統合
WebSocket
フロントエンド App Service X X ○(ポート数:2)
サイロ01 App Service X ○(ポート数:2)
サイロ02 App Service X ○(ポート数:2)
永続化ストレージ Table Storage X ーーー ーーー

 
 
次の記事はこちらです。
hiro128.hatenablog.jp
 
 

Orleans の 永続データオブジェクト

Orleans では、グレインの永続化とサイロのメンバーシップ管理用のテーブルとして、永続データオブジェクトを利用します。
 
Microsoft によってメンテされているグレインの永続化ストレージパッケージは以下です。

  • ADO.NET
  • Azure Blob Storage、Azure Table Storage、Azure CosmosDB などの Azure Storage
  • Amazon DynamoDB

 
learn.microsoft.com
 
Microsoft によってメンテされているサイロのメンバーシップ管理用テーブルのパッケージは以下です。

  • ADO.NET
  • Azure Blob Storage、Azure Table Storage、Azure CosmosDB などの Azure Storage
  • Amazon DynamoDB
  • Apache ZooKeeper
  • Consul IO

 
learn.microsoft.com
 
 

Azure Table Storage を使用した場合の実例

例として、グレインの永続化ストレージとして Azure Table Storage を使用した場合について見てみます。
「orleanspoc」という名前のストレージアカウントを作成し、下記のように接続文字列を指定してサイロを構成すると初回に自動的にテーブルが作成されます。
 
 

グレインの状態の永続化用テーブル OrleansGrainState の指定
.AddAzureTableGrainStorage(
    "PocStore",
    options => options.ConfigureTableServiceClient(connectionString)
)

 

サイロメンバーシップの管理用のテーブル OrleansSiloInstances の指定
.UseAzureStorageClustering(options => options.ConfigureTableServiceClient(connectionString))

 

 
 

グレインの状態の永続化用テーブル OrleansGrainState

レコードを確認すると以下のようになっています。
App Service にサイロをデプロイした時のレコードを確認して各項目について調べてみました。

列名 値のソース、形式 説明
PartitionKey [ClusterOption.ServiceID]_[グレイン名のプレフィックス]_[連番] 永続化したグレインを一意に特定するキー
RowKey PersistentStateAttribute.StateName グレインは複数の状態を持てる。グレインが保持している状態を特定するキー
Timestamp タイムスタンプ タイムスタンプ
Data
Data1
Data2
シリアライズかつバイナリ化されたグレインが保持している状態の実体
1列に収まらなければ、連番なし、1、2 … N のように複数列に格納される
シリアライズかつバイナリ化されているため直接レコードを見ても中身はわからない状態になっている
規定では Orleans.Storage.JsonGrainStorageSerializer が利用される
参考:Orleans でのシリアル化の概要 | Microsoft Learn

 
 

サイロメンバーシップの管理用のテーブル OrleansSiloInstances

レコードを確認すると以下のようになっています。
App Service にサイロをデプロイした時のレコードとソースコードなどを確認して各項目について調べてみました。
 

列名 値のソース、形式 説明
PartitionKey ClusterOption.ClusterId サイロとクライアントがメンバーシップテーブルでお互いを見つけるためのキー
RowKey [ConfigureEndpoints のパラメーター advertisedIP]
-[ConfigureEndpoints のパラメーター siloPort]
-[Generation]
advertisedIP:クラスタリングに使用される IP アドレス(サイロのエンドポイントの IP アドレス)
siloPort:サイロ間通信の際に当該のサイロが使用するポート(サイロのエンドポイントのポート)
Generation:(epoch)サイロインスタンスの世代。2022/1/1 00:00:00 (UTC) を0とした現時刻 (UTC) までの Tick 数
Timestamp タイムスタンプ タイムスタンプ
Address EndpointOptions.AdvertisedIPAddress クラスタリングに使用される IP アドレス
DeploymentId PartitionKey の値 Orleans 2.0以前は DeploymentId と呼ばれていた名残り
Generation 2022/1/1 00:00:00 (UTC) を0とした現時刻 (UTC) までの Tick 数 サイロインスタンスの世代。learn のドキュメントでは「epoch」と呼称されている
参考:Orleans でのクラスターの管理 | Microsoft Learn
HostName サイロの DNS ホスト名 Dns.GetHostName() と同じ結果となる。サイロの起動時に設定される
IAmAliveTime DateTimeOffset 当該のサイロが生きていることを最後に報告した日時
Orleans ランタイムによる死活診断やトラブルシューティング時に利用する
InstanceName 古いカラムのため未調査 後方互換性のために残されている古いカラム。
Port EndpointOptions.SiloPort サイロ間通信(silo-to-silo)の際に当該のサイロが使用するポート(サイロのエンドポイントのポート)規定値は 11111
ProxyPort EndpointOptions.GatewayPort サイロからクライアント(この文脈では REST API <-> Grain Call のフロントエンドのこと)への TCP ポート。サイロの起動時に設定される。規定値は 30000
RoleName 実行中のアセンブリの名前。
SiloName サイロ ホストが付けたサイロ名。サイロの起動時に設定。
StartTime DateTimeOffset サイロインスタンスのが起動した日時
Status サイロの状態 状態の詳細は右記を参照:SiloStatus 列挙型 (Orleans.Runtime) | Microsoft Learn
メンバーシッププロトコルで管理される。
SuspectingSilos [サイロの IPアドレス]-[サイロの Port]@Generation 当該のサイロが Active ではないことを疑っているサイロのリスト。メンバーシッププロトコルで管理される。
SuspectingTimes DateTimeOffset 当該のが Active ではないことに対する疑いが生じた日時
UpdateZone Azure Cloud Services では利用されていたが、App Service での利用状況は不明
MembershipVersion Int64 TableVersion の場合のみ、現在のメンバーシップ構成の最新バージョン。MembershipEntry の 場合は null