# クラスター機能

> **警告**
>
> クラスター機能は実験的機能のため、正式版では仕様が変更される可能性があります

## 概要

これは Sora でクラスターを構成し、冗長化、負荷分散を行うための仕組みです。

クラスター内では、ひとつのチャネル ID への接続は、担当するひとつの Sora ノードにすべて割り当てられます。
クライアントがクラスターを構成しているいずれかの Sora に接続した際には、
そのチャネル ID で、接続すべき Sora の `Signaling URL` を提示する仕組みです。

## 目的

可用性を高めるため Sora 複数台でクラスターを組み、冗長化やロードバランシングを実現します。

## 仕組み

Sora のクラスター機能の仕組みは、ひとつのチャネル ID への接続は、
担当するひとつの Sora ノードにすべて割り当てるものです。
これを実現するために、Sora のクラスターに参加しているそれぞれの Sora ノードは、
どのノードがどのチャネルを担当するべきかを把握しています。
そして、自分が担当でなかったら担当者にリダイレクトすることにより、
クライアントは担当ノードを意識することなく、任意のノードにアクセスするだけで担当ノードに接続できます。

## 特徴

> **注釈**
>
> これらの仕組みは `sora.conf` にてクラスター機能を有効にした場合に使用できます

### クライアントへの複数シグナリング設定

Sora SDK にシグナリング URL を複数指定することで、いずれかの Sora ノードが正常に動作していれば Sora に繋がります。

### Sora ノード選択 / ロードバランス機能

クラスターを構築している Sora ノードの中で `現在の同時接続数` を
`ライセンスの最大同時接続数` で割った値が一番小さい Sora ノードに新規チャネル ID の担当を割り当てます。
値が全て同じ場合は接続しに行ったノードが担当します。

すでにそのチャネル ID がクラスター内部で利用されている場合は、
そのチャネル ID を担当している Sora ノードへ割り当てられます。

### シグナリングのリダイレクト機能

> **重要**
>
> Sora の SDK を利用している場合は、基本的にここに書かれているリダイレクトの
> 細かい仕様を把握する必要はありません。

`"type": "connect"` を送った際、認証に入る前に `{"type": "redirect", "location": "wss://sora1.example.com/signaling"}` が戻ってくる場合があります。

この場合は送られてきたシグナリング URL に切り替えて再度 `type: connect` を行ってください。
その際は `{type: connect, redirect: true}` のように `redirect: true` を追加情報として入れてください。

### HTTP API のリダイレクト機能

チャネル ID を指定する HTTP API を利用する場合、リダイレクトが発生することがあります。
指定されたチャネル ID を他ノードが担当している場合に、ステータスコード 307 (Temporary Redirect)の
HTTP 応答により、クライアントにそのノードへのリダイレクトを要求します。

ステータス 307 の処理は HTTP のクライアントとするツールやライブラリにより異なります。
必要に応じて、リダイレクト応答の Location ヘッダーへアクセスを継続するようにしてください。

たとえば、`curl` コマンドの場合は、 `-L` オプションを指定することでリダイレクト先への再要求を行います。


### クラスター自動参加機能

複数の Sora ノードでクラスターを自動構築する機能です。

Sora はクラスターが有効な際、起動時に `sora.conf` の `contact_node_name_list` に存在する
ノード名に対してクラスターの参加を自動で試みます。

もし、そのノードが一度でもクラスターに参加したことがある場合は、そのクラスターに参加していた時の
ノード一覧 **も** 利用して、クラスターへの参加を自動で試みます。

### クラスター手動参加機能

> **重要**
>
> `sora.conf` の `contact_node_name_list` を利用した自動参加の利用を推奨しています。

複数の Sora でクラスターを手動で組む機能です。クラスターに参加する際に [JoinCluster](EXPERIMENTAL_API.html#17f3f5) API を叩きます。

すでにクラスターが存在している場合には、そこに参加しているノードのいずれかを引数の `contact_node_name` で指定してくだ
さい。クラスターに参加していれば、どのノードでもかまいません。新規にクラスターを作成する場合には、起動中の任意のノードを
`contact_node_name` に指定して大丈夫です。

### クラスター自動復旧

クラスターで利用しているネットワークに障害が発生した際、個々のノードはクラスターを構成する他のノードへの接続を試み、復旧
処理を行います。

### ネットワーク分断時の接続受付の停止

ネットワーク分断時には Sora のノード間で通信ができず、クラスター内の情報の整合性が取れなくなる可能性があります。
不整合を回避するために、Sora は自分が過半数未満のグループに属した場合に、既存の接続を切断した上で、
新規接続の受付を停止します。

### 特定ノードへの新規チャネル ID の割り当て停止

[モード機能](MODE.html) の [イニシャルモード](MODE.html#74fada) 、
[新規コネクションブロックモード](MODE.html#0b67d3) または [新規セッションブロックモード](MODE.html#041e1c) になっている場合は、
     そのノードに対して新規チャネル ID の割り当てを行いません。

### 録画情報の共有

録画の情報はクラスター内部で共有されます。
たとえば [StartRecording](API.html#c5b527) API をあるノードに対して実行した後にそのノードが停止しても
録画情報は他のノードに残り続けます。

## ポート番号の開放

以下のポート番号を開放して下さい

- epmd（Erlang Port Mapper Daemon）で利用する TCP/IP ポート番号- 4369
- ノード間通信に利用する TCP/IP ポート番号- 49010 - 49020
  - `sora.conf` の `cluster_listen_(min|max)_port` にて指定可能です

## 注意

### ライセンス

クラスターの 1 Sora ノードにつき 1 ライセンスが必要になります。
3 台の Sora ノードでクラスターを組む場合は 3 ライセンス必要になります。

> **注釈**
>
> [最大ノード数ライセンス](LICENSE.html#aee259) を利用することで、同一ライセンスを複数 Sora ノードで利用することが可能になります。

### クラスターのノード数

クラスター機能を有効にしている場合は 1 ノードだけでは利用できず、必ず 2 ノード以上である必要があります。

> **注釈**
>
> 1 ノードの場合は接続を受け付けません

> **重要**
>
> 2022 年 12 月リリースの Sora からは 2 ノードでのクラスター構築には非対応となり、3 ノード以上が必須となる予定です。

### クラスターからのノードの消去について

クラスターからノードを完全に消去させる場合は `bin/sora stop` で停止させた後に、
クラスターを構成してる実行中のノードのいずれかに対し、[PurgeClusterNode](EXPERIMENTAL_API.html#13b35a) API を実行します。

### クラスターノードリストファイル

> **危険**
>
> このファイルは Sora が内部で利用するためのファイルなので触らないでください。

Sora でクラスターを利用する場合、クラスター参加後のノード情報を永続化する
`data/cluster-nodes.bin` というファイルを生成します。

アップデート時にこのファイルをコピーする必要はありません。ファイルが無い場合、クラスターに参加した際に生成されます。

### 障害時の録画情報の消去

**将来的にこの問題は解決する予定はありますが、時期は未定です**

クラスターに所属するすべてのノードが過半数未満に所属した場合に、録画情報は消去されます。
この場合には `recording.report` イベントウェブフックは発火せず、
`report-<recording_id>.json` レポートファイルも生成されません。

## クラスター機能有効時のモード

クラスター機能を有効にした場合、 Sora は :ref:`イニシャルモード`という
クラスター機能だけで利用される特殊なモードで起動します。

- [イニシャルモード](MODE.html#74fada) はすべての接続を受け入れません
- [イニシャルモード](MODE.html#74fada) はクラスターに参加後、過半数以上のグループになったタイミングで[ノーマルモード](MODE.html#920e82) へ自動で切り替わります
- [イニシャルモード](MODE.html#74fada) はモード変更 API では指定できません

## ネットワーク障害やノード障害

Sora のクラスター機能はネットワーク障害やノード障害が発生した際に、
自動で新規接続の受付の停止とそこからの復旧を試みます。

### 発生しうる問題と新規接続の受付停止

ネットワーク障害等が発生した場合には、
Sora のノード間で通信ができず、クラスター内の情報の整合性が取れなくなる可能性があります。

たとえば、5 ノードのクラスターが 3 ノードからなるグループ A と
残りの 2 ノードからなるグループ B の 2 グループに分断されるということが起こりえます。
それぞれのグループ内では通信ができるものの、他グループへの通信ができないという状態です。
この状態ですべてのノードが接続を受け付けると、同じチャネル ID を指定していても、
あるクライアントはグループ A に繋がり、別のクライアントは グルーブ B につながる可能性があり、
その場合はクライアント間で音声と映像が届かなくなります。

このような状況を防止するため、Sora は自分が過半数未満のグループに属した場合に、新規接続の受付を停止します。
特に、自分が孤立した場合もこれに含まれます。

その後、クラスター自動復旧により、通信できるノードがで過半数以上となった場合には、自動で新規接続の受付を再開します。

#### 新規接続の受付がまったくできなくなる場合

障害の状況によっては新規接続の受付がまったくできなくなる場合もあります。
例えばネットワークの分断と状態によって、5 ノードのクラスターが 2 ノード、2 ノード、1 ノードという
3 つ以上のグループに分断された状況を考えます。
この場合にはどのノードも過半数未満となるため、新規接続の受付はまったくできなくなります。

ネットワーク障害ではなくても、5 ノードクラスターで、3 ノードが停止してしまった場合には、残った 2 ノードは
過半数未満となるため、新規接続の受付はできません。

#### タイブレークによる特別な「過半数」

> **重要**
>
> 2022 年 12 月リリースの Sora からはタイブレーク処理は廃止される予定です。
> それに伴い 2 ノードでのクラスター構築には非対応となり 3 ノード以上が必須となります。

クラスターを構成するノードが偶数の場合、ネットワーク分断時に自分と通信可能なノード数がちょうど半数になることがあります。
たとえば 4 ノードクラスターで 2 ノードのグループがふたつできる状態です。

単純にノード数を数えた場合には両グループともに過半数には届かず、新規接続の受付が完全に停止してしまいます。
これを避けるために、Sora は「過半数」の判断に特別なタイブレークを適用します。

タイブレークのロジックは、辞書順に見てノード名が一番小さなノードが参加しているクラスターを「過半数」とするものです。

上記の 4 ノードクラスターで、 `sora1@192.0.2.1` と `sora4@192.0.2.4` がひとつの通信可能なグループ、
`sora2@192.0.2.2` と `sora3@192.0.2.3` がもうひとつのグループと分断された例を考えます。
辞書順の一番小さなノードは `sora1@192.0.2.1` であるため、このノードと `sora4@192.0.2.4` は
新規接続の受付を継続し、他の 2 ノードは停止します。

このドキュメントでは「過半数」という用語を、単純なノード数を数えた過半数だけではなく、
このタイブレークでの勝者グループを含めた意味で用います。

### 通信できるノードが過半数未満となった場合の挙動

ノード数が過半数未満のクラスターに参加しているノードは、接続中のすべての接続を切断し、その後は接続を受け入れません。

また、その時のモードによってモードが変わることがあります。

- [ノーマルモード](MODE.html#920e82) の場合には [イニシャルモード](MODE.html#74fada) に状態を移行します
- [ノーマルモード](MODE.html#920e82) 以外のモードであった場合はモードは変わりません

その後はクラスター自動復旧を試みます。

### 通信できるノードが過半数以上となった場合の挙動

通信できるノードが過半数未満から過半数以上に変わったタイミングで、そのときのモードによってモードが変わることがあります。

- [イニシャルモード](MODE.html#74fada) の場合には [ノーマルモード](MODE.html#920e82) に状態を移行します
- [イニシャルモード](MODE.html#74fada) 以外のモードであった場合はモードは変わりません

遷移後のモードに従って、新規接続を受け付けます。

### 全ノードが過半数未満に所属した場合の挙動

ネットワーク障害等が発生したことで、いずれのノードも通信できるノードが過半数未満になった場合、
新規接続はまったく受け付けられない状態になります。

この場合でも、永続化しておいたノード一覧を利用し自動でクラスターの復旧を試みます。
復旧して過半数以上のグループができた場合は新規接続が可能になります。

## クラスター環境での録画

### 録画情報の共有

録画の情報はクラスター内部で共有されます。
たとえば [StartRecording](API.html#c5b527) API をあるノードに対して実行した後にそのノードが停止しても
録画情報は他のノードに残り続けます。
また [StopRecording](API.html#fd0de5) API はどのノードに対しても実行することができます。

たとえば、あるチャネルで [StartRecording](API.html#c5b527) API が実行された後に、
ノード A に接続がきて録画が開始したとします。
このノード A がノード障害等で終了した後に、同じチャネルで別の接続がノード B に来た場合には
ノード B で録画が進行します。

### 録画ファイルの出力ノード

`archive-<connection_id>.json`, `archive-<connection_id>.webm` の録画ファイルは、接続を担当するノードで
出力されます。

次のような場合には同一の `recording_id` に属する録画ファイルが、
複数ノードにまたがって出力されることがありますのでご注意ください。

1. 録画開始
2. ノード A に接続が来てしばらくして終了、録画ファイルがノード A に作られる
3. ノード A が停止
4. ノード B に接続が来てしばらくして終了、録画ファイルがノード B に作られる

### `report-<recording_id>.json` ファイルの出力ノード

あるチャネルに対して、 [StopRecording](API.html#fd0de5) API を実行した場合には録画が終了し、
`report-<recording_id>.json` ファイルが出力されます。

このファイルの出力ノードは次のとおりです。

- そのチャネルに接続したクライアントがひとつもなかった場合はどのノードで出力されるかは未定です- ただし、クラスター内のいずれかのノードで出力されます
- そのチャネルに接続中のクライアントがいる場合は、チャネルの担当ノードで出力されます
- そのチャネルに接続したクライアントが過去にいた場合は 2 パターンがありえます- クライアントが接続したノードが生存し続けている場合はそのノードで出力されます
  - ノードが落ちている、または停止した後に再起動した場合にはどのノードで出力されるかは未定です- ただし、クラスター内のいずれかのノードで出力されます

### `report-<recording_id>.json` ファイルが出力されない場合

全ノードが過半数未満に所属した場合には `report-<recording_id>.json` ファイルが出力されません。
また、録画情報自体も消去されます。

5 ノードのクラスターがあるとして、いくつか例を示します。

- ネットワーク障害が発生し、2 + 2 + 1 に分断された場合
- ノード障害(運用での停止も含む)が発生し、3 ノードが停止した場合
- ノード障害で 1 ノードが停止した状態でネットワーク障害が発生し、 2 + 2 に分断された場合

## 設定

### cluster

クラスター機能を利用する場合、 `sora.conf` にて [cluster](SORA_CONF.html#b2cd99) を `true` を設定する必要があります。

デフォルトでは `cluster` は有効になっていません。

> **重要**
>
> cluster = true にして Sora を起動した場合、
> Sora はすべての接続を受け付けない [イニシャルモード](MODE.html#74fada) モードで起動します。
>
> 自動または手動でクラスターに参加した後、過半数以上のグループになった時点で
> [ノーマルモード](MODE.html#920e82) へ自動で切り替わります。

```ini
cluster = true
```

### node_name

クラスター機能を利用する際のノード名を指定して下さい。

ここで指定するノード名は `sora.conf` の `contact_node_name_list` 、 [JoinCluster](EXPERIMENTAL_API.html#17f3f5) API、
[PurgeClusterNode](EXPERIMENTAL_API.html#13b35a) API で指定する際に利用するノード名です。
ノード名の @ の前には、正規表現 `[0-9A-Za-z_\\-]+` にマッチする文字列を指定してください。
また @ の後ろには、サーバーのドメイン名（FQDN）や、IP アドレスを指定してください。

> **重要**
>
> クラスターどうしの通信は暗号化されていないため、プライベートネットワークを利用するか
> 暗号化されたネットワークを利用してください。

> **重要**
>
> @ の後ろにホスト名（"." を含まない文字列）を指定すると、ノード間で通信が行えなくなってしまいます。

> **重要**
>
> クラスター参加後にノード名を変更する場合には、古い方の名前は [PurgeClusterNode](EXPERIMENTAL_API.html#13b35a) API を用いて削除してください。

```ini
# ドメイン名を指定する例
node_name = node01@node01.example.com
```

```ini
# IP アドレスを指定する例
node_name = node02@192.0.2.1
```


### contact_node_name_list

クラスター機能が有効な場合で、 `sora.conf` の `contact_node_name_list` が指定されていた場合、
起動時に自分以外のノード名に対して自動でクラスターの参加を試みます。

> **注釈**
>
> 自分自身のノード名が `concact_node_name_list` に含まれていても無視します。

```ini
node_name_list = sora1@192.0.2.1, sora2@192.0.2.2, sora3@192.0.2.3
```


### cluster_auto_reconnect

クラスター機能が有効な場合で、 `sora.conf` の `cluster_auto_reconnect` が `true` の場合、
ネットワーク障害やノード障害の発生時に自動で再接続を試みます。

この設定はデフォルトで有効です。

```ini
cluster_auto_reconnect = true
```

### external_signaling_url

`sora.conf` にてシグナリングの URL を指定して下さい。
この URL はクラスター機能を利用した際に `"type": "redirect"` の `"location"` の値として払い出されます。

```ini
external_signaling_url = wss://node1.example.com/signaling
```

上記の設定をした場合は `{"type": "redirect", "location": "wss://node1.example.com/signaling"}` として払い出されます。

### external_api_url

`sora.conf` にて API の URL を指定して下さい。
この URL はクラスター機能を利用した際に API を適切なノードに HTTP 307 でリダイレクトする際、
HTTP ヘッダー `"location"` の値として払い出されます。

```ini
external_api_url = https://node1.example.com/
```

### cluster_listen_(min|max)_port

`sora.conf` にてクラスター情報の同期に利用する TCP/IP の受信ポートの範囲を指定してください。

デフォルトでは 49010 - 49020 が指定されています。

```ini
cluster_listen_min_port = 49010
cluster_listen_max_port = 49020
```


## API

### クラスター参加 API

> **ヒント**
>
> `sora.conf` の `contact_node_name_list` を利用したクラスターへの自動参加の利用を推奨しています。

参加するノードに対して、すでにクラスターに参加しているノードを指定してクラスターへ参加をします。

詳細は [JoinCluster](EXPERIMENTAL_API.html#17f3f5) API をご確認ください。

```console
http POST 127.0.0.1:3000/ x-sora-target:Sora_20211215.JoinCluster \
    contact_node_name=sora1@192.0.2.5
HTTP/1.1 200 OK
content-length: 646
content-type: application/json
date: Tue, 16 Nov 2021 08:42:25 GMT
server: Cowboy

{
    "node_name_list": [
        "node-02@192.0.2.7",
        "node-03@192.0.2.8",
        "node-01@192.0.2.5"
    ]
}
```

### クラスターノード消去 API

ある Sora ノードが障害で再参加が難しい、またはスケールインのためにノード破棄する場合は
[PurgeClusterNode](EXPERIMENTAL_API.html#13b35a) API を利用して、そのノード情報を消去してください。

これはクラスターのどのノードでもかまわないので 1 回だけ実行すれば、消去した結果はクラスター内部で共有されます。

```console
$ http POST 127.0.0.1:3000/ x-sora-target:Sora_20220629.PurgeClusterNode \
    target_node_name=sora1@192.0.2.5
```

詳細は [PurgeClusterNode](EXPERIMENTAL_API.html#13b35a) API をご確認ください。

### クラスターノード一覧 API

詳細は [ListClusterNodes](EXPERIMENTAL_API.html#a70901) API をご確認ください。

```console
$ http POST 127.0.0.1:3000/ x-sora-target:Sora_20211215.ListClusterNodes
HTTP/1.1 200 OK
content-length: 1051
content-type: application/json
date: Tue, 16 Nov 2021 08:36:25 GMT
server: Cowboy

[
    {
        "external_api_url": "http://192.0.2.8:3000/",
        "epoch": 1,
        "license_max_connections": 600,
        "license_max_nodes": 10,
        "license_serial_code": "ABCDEF-SRA-E003-203801-500",
        "license_type": "Experimental",
        "member_since": "2021-11-16T08:29:36.972932Z",
        "node_name": "node-03@192.0.2.8",
        "connected": true,
        "mode": "normal",
        "external_signaling_url": "wss://node-03.example.com/signaling",
        "version": "2022.1.0"
    },
    {
        "external_api_url": "http://192.0.2.7:3000/",
        "epoch": 1,
        "license_max_connections": 600,
        "license_max_nodes": 10,
        "license_serial_code": "ABCDEF-SRA-E002-203801-400",
        "license_type": "Experimental",
        "member_since": "2021-11-16T08:22:46.898730Z",
        "node_name": "node-02@192.0.2.7",
        "connected": true,
        "mode": "normal",
        "external_signaling_url": "wss://node-02.example.com/signaling",
        "version": "2022.1.0"
    },
    {
        "external_api_url": "http://192.0.2.5:3000/",
        "epoch": 1,
        "license_max_connections": 600,
        "license_max_nodes": 10,
        "license_serial_code": "ABCDEF-SRA-E001-203801-400",
        "license_type": "Experimental",
        "member_since": "2021-11-16T08:23:02.488711Z",
        "node_name": "node-01@192.0.2.5",
        "connected": true,
        "mode": "normal",
        "external_signaling_url": "wss://node-01.example.com/signaling",
        "version": "2022.1.0"
    }
]
```

### クラスターチャネル割り当て一覧 API

詳細は [ListClusterChannels](EXPERIMENTAL_API.html#0a4459) API をご確認ください。

```console
$ http POST 127.0.0.1:3000/ x-sora-target:Sora_20211215.ListClusterChannels
HTTP/1.1 200 OK
content-length: 646
content-type: application/json
date: Tue, 16 Nov 2021 08:42:25 GMT
server: Cowboy

[
    {
        "channel_id": "sora",
        "node_in_charge": "node-01@192.0.2.5",
        "node_in_charge_epoch": 1,
        "node_in_charge_epoch_stale": false,
        "node_in_charge_connected": true
    },
    {
        "channel_id": "lemon",
        "node_in_charge": "node-01@192.0.2.5",
        "node_in_charge_epoch": 1,
        "node_in_charge_epoch_stale": false,
        "node_in_charge_connected": true
    },
    {
        "channel_id": "hisui",
        "node_in_charge": "node-03@192.0.2.8",
        "node_in_charge_epoch": 2,
        "node_in_charge_epoch_stale": false,
        "node_in_charge_connected": true
    },
    {
        "channel_id": "zakuro",
        "node_in_charge": "node-02@192.0.2.7",
        "node_in_charge_epoch": 2,
        "node_in_charge_epoch_stale": false,
        "node_in_charge_connected": true
    }
]
```












## クラスター構築

> **重要**
>
> node_name はクラスター内のすべてのノードでユニークである必要があります

### ライセンス

Sora ノード数分のライセンスが必要になります。
Sora 3 台でクラスターを組む場合は通常のライセンスが 3 つ、もしくは 3 ノード以上に対応した
最大ノード数ライセンスが 1 つ必要です。

### 3 台での構築

クラスターを構築する際、クラスターを同期するネットワークは可能な限りプライベートネットワークを利用してください。

1. 必要なすべてのサーバーのポート開放- TCP/IP 4369- ポート番号の変更はできません、必ずこのポートを開放してください
   - TCP/IP 49010-49020- `sora.conf` にて変更可能ですがデフォルトをお勧めします
2. 1 ノード目の `sora.conf` の設定を cluster 対応にする- `node_name = sora1@192.0.2.101`- `@` 以下の IP アドレスまたはドメイン名は、可能な限りプライベートネットワークを指定してください
   - `cluster = true`
   - `external_signaling_url = wss://sora-node1.example.com/signlaing`
   - `external_api_url = https://sora-node1.example.com/api`
   - `contact_node_name_list = sora1@192.0.2.101, sora2@192.168.0.102, sora3@192.168.0.103`
3. Sora を起動する
4. `http POST 192.0.2.101:3000/ x-sora-target:Sora_20211215.ListClusterNodes` を実行する
5. 1 台のノードリストが取得できれば、ここまでの手順は OK
6. 2 ノード目の `sora.conf` の設定を cluster 対応にする- `node_name = sora2@192.168.0.102`- `@` 以下の IP アドレスまたはドメイン名は、可能な限りプライベートネットワークを指定してください
   - `cluster = true`
   - `external_signaling_url = wss://sora-node2.example.com/signlaing`
   - `external_api_url = https://sora-node2.example.com/api`
   - `contact_node_name_list = sora1@192.0.2.101, sora2@192.168.0.102, sora3@192.168.0.103`
7. Sora を起動する
8. `http POST 192.0.2.101:3000/ x-sora-target:Sora_20211215.ListClusterNodes` を実行する
9. 2 台のノードリストが取得できれば、、ここまでの手順は OK
10. 3 ノード目の `sora.conf` の設定を cluster 対応にする- `node_name = sora3@192.168.0.103`- `@` 以下の IP アドレスまたはドメイン名は、可能な限りプライベートネットワークを指定してください
    - `cluster = true`
    - `external_signaling_url = wss://sora-node3.example.com/signlaing`
    - `external_api_url = https://sora-node3.example.com/api`
    - `contact_node_name_list = sora1@192.0.2.101, sora2@192.168.0.102, sora3@192.168.0.103`
11. Sora を起動する
12. `http POST 192.0.2.101:3000/ x-sora-target:Sora_20211215.ListClusterNodes` を実行する
13. 3 台のノードリストが取得できれば、クラスターの構築は完了

## クラスター運用

### ノード数

Sora は 2 ノードからクラスターを構築できますが、 2 ノードの場合は可用性が 1 ノードの場合と同じになるため、
3 ノード以上でのクラスター構築をお勧めしています。

> **重要**
>
> 2022 年 12 月リリースの Sora からは 2 ノードでのクラスター構築には非対応となり、3 ノード以上が必須となる予定です。

### クラスターからの一時的な離脱

モード切り替え API を利用し、 [新規コネクションブロックモード](MODE.html#0b67d3) または [新規セッションブロックモード](MODE.html#041e1c) に
切り替えます。
その後、すべての接続がいなくなったタイミングで Sora ノードを終了してください。

### 一時的に離脱したノードを含めるクラスターノード一覧

[ListClusterNodes](EXPERIMENTAL_API.html#a70901) API では現在クラスターに参加しているノード一覧が返りますが、
`include_all_known_nodes` を `true` に指定し API を実行するとで、そのクラスターが一時的に離脱したノードを含めて知っているノード一覧を返します。

### クラスターからノード情報の消去

> **重要**
>
> この作業は必ず行うようにしてください。

特定のクラスターノードが障害が起きた後、復旧が難しい場合は [PurgeClusterNode](EXPERIMENTAL_API.html#13b35a) API を利用して、
クラスター参加しているノードから、障害が起きたノード情報を消去してください。

また、スケールインのためにノードを破棄する場合も同様に [PurgeClusterNode](EXPERIMENTAL_API.html#13b35a) API を利用して破棄したノードの情報を消去してください。

この作業はクラスターの新鮮さを保つために必要な作業となります。
この作業を行わないと存在していないノードが残り続けることで過半数かどうかの判断を誤る場合があります。

### クラスターのアップデート

> **注釈**
>
> ローリングアップデートを推奨しています

1. アップデート対象の Sora ノードを [ChangeMode](MODE.html#3af05f) API で [新規コネクションブロックモード](MODE.html#0b67d3)または [新規セッションブロックモード](MODE.html#041e1c) に切り替えます- 必要があれば [ListChannels](EXPERIMENTAL_API.html#b27c42) API と [DisconnectChannel](API.html#a87366) API を利用して既存接続を切断します
2. すべての接続がなくなったら Sora を停止させます
3. 新しいバージョンの Sora へ入れ替えます
4. 新しいバージョンの Sora を起動します
5. [ListClusterNodes](EXPERIMENTAL_API.html#a70901) API で新しいバージョンの Sora がクラスターに参加できているかを確認します

これを 1 ノードごとに繰り返してください。

## シーケンス図

### type: redirect

seqdiag {
    default_fontsize = 14;
    edge_length = 200;

    クライアント ->> Sora1 [label = '"type": "connect"'];
    クライアント <<- Sora1 [label = '"type": "redirect"', rightnote='sora2 のシグナリング URL を返す'];
    クライアント ->> Sora2 [label = '"type": "connect"'];
        Sora2 -> アプリケーションサーバー [label = '認証ウェブフック'];
        Sora2 <-- アプリケーションサーバー [label = '200 OK\n{"allowed": true}'];
    クライアント <<- Sora2 [label = '"type": "offer"'];
    クライアント ->> Sora2 [label = '"type": "answer"', noactivate];
    クライアント ->> Sora2 [label = '"type": "candidate"', noactivate];

    === WebRTC 確立 ===

        Sora2 -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.created"'];
        Sora2 <-- アプリケーションサーバー [label = '200 OK'];
}
