# クラスター機能運用

不明点がある場合はサポートまでお問い合わせください。

## クラスター利用時のファイル操作に問題が発生した場合

クラスター機能を利用している際に、ファイル操作に問題が発生した場合、
そのノードは [新規コネクションブロックモード](MODE.html#0b67d3) へ移行します。

ファイル操作とはログ出力だったり、録画ファイルアーカイブ出力だったりです。

この状態が発生した場合、
ディスク破損、ディスク容量不足、パーミッション間違いなど、
致命的な問題が発生している可能性が高いため、ノードを停止し、問題の解決を行ってください。

### 問題があるノードを破棄する場合

レギュラーノードの場合は、
必ず [PurgeClusterNode](API_CLUSTER.html#13b35a) API を利用して対象のノードを消去し、新しいノードを追加してください。

テンポラリーノードの場合は、そのまま破棄して問題ありません。

## クラスターのレギュラーノード数

### 最低ノード数

Sora は最低 3 レギュラーノードのクラスターを構築してください。

- 1 レギュラーノードで構築されたクラスターはクラスターを構築していないのと同様です
- 2 レギュラーノードで構築されたクラスターは片方のノードが停止すると、正常なノードも過半数の条件を満たさなくなるため接続を受け付けなくなってしまいます

### ノード数

3 レギュラーノードの次は 5 レギュラーノードのクラスターを構築してください。
4 レギュラーノードは 2 レギュラーノードがダウンした時点でクラスターが利用できなくなるため、3 レギュラーノードと可用性が変わらないためです。

5 レギュラーノード以上は、特に制限はありません。

## クラスターのテンポラリーノード数

テンポラリーノードはレギュラーノードと違いクラスターの維持に影響しないため、最低ノード数の制限はありません。

## 最大ノード数

クラスターを構築するノードの最大数は 100 ノードを想定しています。
これ以上のノード数を検討されている場合はサポートまでご連絡ください。

## クラスター利用時のライセンス

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

### 最大ノード数ライセンス

[最大ノード数ライセンス](LICENSE.html#aee259) を利用することで、同一ライセンスを複数ノードで利用できるようになります。

クラスターを利用する場合は [最大ノード数ライセンス](LICENSE.html#aee259) を利用することを推奨します。

[最大ノード数ライセンス](LICENSE.html#aee259) への切り替えはサポートまでご連絡ください。

> **注釈**
>
> レギュラーノードもテンポラリーノードも、それぞれ 1 ノードとしてカウントされます。

### 最大ノード数ライセンスのみで利用できる機能

クラスター機能のいくつかの機能は [最大ノード数ライセンス](LICENSE.html#aee259) が必要になります。

- リレー機能
- リレー機能利用時のアフィニティ機能
- ライセンスで決められた最大同時接続数の合計を維持する機能
- テンポラリーノード機能

> **重要**
>
> [最大ノード数ライセンス](LICENSE.html#aee259) を利用せずに `sora.conf` でリレー機能やアフィニティ機能を有効にしても起動はできます。
> ただし `sora.jsonl` に警告ログが出力されます。

## ポートの開放

以下のポートを **クラスターを組むその他のノードにのみ** に対して開放してください。

> **危険**
>
> 絶対にパブリックインターネットへの公開はしないでください。

- epmd（Erlang Port Mapper Daemon）で利用する TCP ポート- 4369
- ノード間通信に利用する TCP ポート- 49010 - 49020
  - `sora.conf` の `cluster_listen_{min,max}_port` にて範囲を指定できます

## ノード間通信

Sora のノード間通信は **TCP/IP を利用しています** 。
そのため、安定したプライベートネットワークの利用を強く推奨します。

もし、マルチクラウドなどでノード間通信がパブリックネットワークを利用したクラスターを構築する場合は、
輻輳が発生する可能性があります。

そのため、特別な理由がない限りはプライベートネットワークを利用してください。

### リレー機能利用時のノード間通信

Sora クラスターリレー機能利用時のノード間通信では、大量のネットワーク帯域を消費します。
そのためリレー機能を利用する場合は **可能な限り** 同一データセンター内のプライベートネットワークを利用してください。

## ノード間通信の暗号化

Sora のノード間通信は **暗号化されておりません** 。
そのため、安全なプライベートネットワークの利用を推奨します。

もし、マルチクラウドなどでノード間通信がパブリックネットワークを利用したクラスターを構築する場合は、
ノード間の通信を暗号化する必要があります。その場合は Tailscale などの利用をお勧めします。

[Tailscale · Best VPN Service for Secure Networks](https://tailscale.com/)

参考までに、下記は弊社の Tailscale 利用事例です。

[ベアメタルサーバーを利用したクラウドサービスで発生する課題を Tailscale で解決する · Tailscale](https://tailscale.com/ja/customers/shiguredo/)

### リレー機能利用時

> **重要**
>
> リレー機能を利用する際に、マルチクラウドでの利用は非推奨です。

リレー機能利用時にはノード間通信が大量に発生する場合があるため、暗号化に多くの CPU リソースを消費します。
そのため、プライベートネットワークを利用し、ノード間通信には暗号化を行わないことを推奨します。

## テンポラリーノード機能の利用

3 ノードや 5 ノードのレギュラーノードで構築したクラスターを拡張する場合は、
テンポラリーノード機能の利用を検討してください。

テンポラリーノードは、クラスター維持に影響しないノードで、気軽に追加・削除できるノードです。

特にリレー機能利用時には、スケールアウト/スケールインが気軽に行えるようになるため、
レギュラーノードとテンポラリーノードを組み合わせてクラスターを構築することを推奨します。

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

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

一時的に離脱したノードは、再起動するとそのままクラスターに参加して動作します。

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

[ListClusterNodes](API_CLUSTER.html#a70901) API では現在クラスターに登録している全てのノードの一覧が返ります。

> **注釈**
>
> 結果に一時的に離脱したレギュラーノードを含めない場合には、 `include_all_known_nodes` に `false` を指定して API を実行します。

## クラスターからレギュラーノード情報の完全消去

> **重要**
>
> 恒久的にレギュラーノードを破棄する場合には、必ず、この作業を行ってください。

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

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

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

> **警告**
>
> PurgeClusterNode API はクラスターからレギュラーノードを完全に消去するための API です。
> 近い将来に再び参加するレギュラーノードに対しては基本的に使用しないでください。


## クラスターのローリングアップデート

> **重要**
>
> ローリングアップデートは並列で行うのではなく必ず 1 台ずつ行ってください。

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

> **警告**
>
> 自動でクラスターに参加する処理がエラーになる可能性もあるため、
> [ListClusterNodes](API_CLUSTER.html#a70901) API で新しいバージョンの Sora がクラスターに参加できているかを必ず確認してください。
> 確認せずに他ノードのローリングアップデートを行った場合、クラスターに参加できていないままのノードが出てしまう
> 可能性があります。
> さらに、複数のノードが参加できずに過半数を割ってしまうと、クラスターがまったく稼働しなくなる可能性もあります。

上の手順を 1 ノードごとに繰り返してください。

### アップデート対象のノードがローリングアップデートに成功したかどうかを確認する方法

[ヘルスチェック機能](OK.html) の `/.ok` を利用してください。

`200 OK` が返ってくればアップデートに成功し、クラスターへの再参加に成功しています。

成功を確認した後、
より詳細な状態が知りたい場合は [ListClusterNodes](API_CLUSTER.html#a70901) API を実行してクラスターの状態を確認してください。

### ローリングアップデート時に一時的に `CLUSTER-NOT-INITIALIZED` になる場合

アップデート対象のノードのアップデート後に [ListClusterNodes](API_CLUSTER.html#a70901) API を実行した際に、
`CLUSTER-NOT-INITIALIZED` が返ってくる場合がありますが、これは一時的な状態です。

そのため、少し待って [ListClusterNodes](API_CLUSTER.html#a70901) API を再度実行してください。

ローリングアップデートに成功したかどうかには [ヘルスチェック機能](OK.html) の `/.ok` を利用してください。

## クラスターのローリングアップデート中の挙動

### リレー機能の無効化

クラスターをローリングアップデートの最中に、
クラスターに複数のバージョンが混在するノードが存在する場合、
リレー機能は同一バージョン同士でしか行えません。

そのため、アフィニティ機能が無効な場合でも別ノードへのリダイレクトを試みる場合があります。

### 一部 API の local に false を指定することができない

クラスターをローリングアップデートの最中に、
クラスターに複数バージョンが混在するノードが存在する場合、
以下 API に `local` に `false` を指定することができません。

- [ListConnections](API_SIGNALING.html#d3da2a) API
- [ListChannels](EXPERIMENTAL_API_SIGNALING.html#b27c42) API
- [GetStatsAllConnections](API_STATS.html#ef695a) API
- [ListRtcStats](API_RTC_STATS.html#956f4b) API
- [ListUserAgentStats](OBSOLETE_API_USER_AGENT_STATS.html#f9a4fe) API

## Sora のバージョンダウン

[クラスターのローリングアップデート](CLUSTER_OPS.html#9dd5e5) の仕組みを使って、Sora のバージョンを下げることはできません。

そのため、クラスターのアップデート後に何らかの問題が発生し、
以前のバージョンに戻したい場合には、クラスターの再構築を行う必要があります。

再構築の際には、事前に全ノードを停止し、 `data/` ディレクトリを削除した上で、
以前のバージョンの Sora を使用してクラスターの構築を行ってください。

## Sora のローリングアップデート失敗

[クラスターのローリングアップデート](CLUSTER_OPS.html#9dd5e5) に失敗した場合は、クラスターの再構築が必要になります。

再構築の際には、事前に全ノードを停止し、 `data/` ディレクトリを削除した上で、
新しいバージョンの Sora を使用してクラスターの再構築を行ってください。

### ローリングアップデートの失敗判断

- 一定時間待っても [ListClusterNodes](API_CLUSTER.html#a70901) API の戻り値のノード数が一致しない場合
- 一定時間待っても [ListClusterNodes](API_CLUSTER.html#a70901) API の戻り値が `CLUSTER-NOT-MAJORITY` を返してくる場合


## クラスター破綻からの再構築手順

クラスターのうち、過半数のノードが停止した場合、クラスターのすべてのノードは接続を受け付けなくなります。
この状態からでも、停止したノードを再開させ、過半数のノードが正常に戻ったタイミングで、接続を受け付け始めます。
その場合は、ここで書かれている手順は必要ありません。

ただし、停止したノードがディスク障害などで `data/` ディレクトリを失ってしまったときや、使っていた
IP アドレスが使えなくなってしまうなどの理由で、再開できないこともあります。

ここでは、3 ノードクラスターを組んでいたものの、うち 2 つのサーバーがディスク障害で
`data/` ディレクトリを失った状態からの再構築手順を示します。

> **注釈**
>
> `data/` ディレクトリを失ってしまうと、「未初期化」状態になるため、再度クラスターへの参加手順が必要です。

> **注釈**
>
> `node_name` に IP アドレスを使う場合、IP アドレスが変わると `node_name` も変える必要があります。
> その際は `data/` ディレクトリを削除する必要があります。

- まずすべての Sora プロセスを停止状態にします
- ディスク障害がおきたサーバーでは- Sora をインストールしなおします
  - 必要に応じて sora.conf 設定を行います
- ディスク障害がおきなかったサーバーでは- `data/` ディレクトリを削除します
  - これを忘れると正しい再構築ができません、必ず削除してください
- ここまでで、完全にまっさらの Sora クラスターを構築する準備ができたことになります
- すべてのノードを起動します
- [InitCluster](API_CLUSTER.html#621990) API を発行し、クラスターを初期化します

以上の手順で、あたらしい 3 ノードのクラスターが動き始めます。


## ノード名変更手順

sora.conf の `node_name` を変更する際の手順を示します。

この手順は、例えば Sora が動作するサーバーの IP アドレスが変わり、 `node_name` の IP アドレス部分を
変更する場合に必要になります。

- 対象のノードを停止します
- `data/` ディレクトリを削除します- これを忘れるとノード名変更が正しく実行できません、必ず削除してください
- クラスターの起動しているノードのいずれかに対し、 [PurgeClusterNode](API_CLUSTER.html#13b35a) API を発行し、対象のノードをクラスターから削除します
- sora.conf の `node_name` を変更します
- 対象のノードを起動します
- [RegisterClusterNode](API_CLUSTER.html#09ed96) API でクラスターにあらたに登録します

> **警告**
>
> この手順では、 `data/` ディレクトリの削除を含むため、対象のノードは「未初期化」状態になります。
> 複数のノードの名前変更が必要な場合には、1 ノードずつこの手順を実行することを推奨します。
> 1 ノードの変更のたびに、 [ListClusterNodes](API_CLUSTER.html#a70901) を使ってクラスターに正しく
> 参加したことを確認して次のノードの手順に取り掛かってください。

## ロードバランサーの導入

> **注意**
>
> ロードバランサーを導入する場合は事前にサポートまでご連絡ください。

クラスター機能を利用した場合は、シグナリング URL に対してロードバランサーが利用できます。

ただし、ロードバランス自体は Sora が行うため、
ロードバランサーの導入はシグナリング URL の 1 本化が目的となります。

- ロードバランサーは WebSocket に対応している必要があります
- ロードバランサーで TLS 終端はせず TLS で Sora へ接続する必要があります
- Sora はロードバランサー経由以外にクライアントと直接接続できる必要があります- これはリダイレクトが発生し、ロードバランサーを経由せずに接続する可能性があるためです
- Sora の WebSocket 通信は 30 秒に 1 回通信を発生させるため、ロードバランサーのタイムアウトは 30 秒以上にする必要があります

```mermaid
sequenceDiagram
    participant C as クライアント
    participant LB as ロードバランサー
    participant S1 as Sora1
    participant S2 as Sora2
    participant A as アプリケーションサーバー
    C->>LB: wss://sora.example.com/signaling
    LB->>S1: wss://0001.sora.example.com/signaling
    note over B,S1: WebSocket 確立
    C->>+LB: "type": "connect"
    LB->>+S1: "type": "connect"
    S1-->>-LB: "type": "redirect"<br>"location": "wss://0002.sora.example.com/signaling"
    LB->>-C: "type": "redirect"<br>"location": "wss://0002.sora.example.com/signaling"
    note over B,S1: WebSocket 切断
    C->>S2: wss://0002.sora.example.com/signaling
    note over B,S1: WebSocket 確立
    C->>+S2: "type": "connect"
    S2->>+A: 認証ウェブフック
    A-->>-S2: "allowed": true
    S2->>-C: "type": "offer"
    C->>S2: "type": "answer"
    note over B,S2: WebRTC 確立
```

もしシグナリング URL の 1 本化を検討している方で、不明点がある場合はサポートまでご連絡ください。

## ログの出力

クラスターに関するログは `log/cluster.jsonl` に生成されます。

ログの詳細については [ログファイル](LOG.html) をご確認ください。


## クラスター構成情報ファイル

> **危険**
>
> このファイルは Sora が内部で利用するためのファイルのため、指示がある場合以外は触らないでください。

Sora でクラスターを利用する場合、クラスター参加後のノード情報を永続化するためのファイルを
`data/` ディレクトリ以下に生成します。

### Sora のバージョンアップ時

Sora をバージョンアップする際には `data/` ディレクトリをコピーを行わないでください。
このファイルが無い場合、 Sora の起動の際に生成されます。

## クラスターからのレギュラーノードの完全消去について

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

> **警告**
>
> [PurgeClusterNode](API_CLUSTER.html#13b35a) API はクラスターからレギュラーノードを完全に消去するための API です。
> 再度参加するレギュラーノードに対しては基本的に使用しないでください。

## PurgeClusterNode API で完全消去したレギュラーノードの再度の参加

> **警告**
>
> [PurgeClusterNode](API_CLUSTER.html#13b35a) API はクラスターからレギュラーノードを完全に消去するための API です。
> 通常は、再度参加するレギュラーノードに対しては使用しないでください。
> ここの説明は、長期間に渡ってクラスターに参加できないため、苦肉の策として
> [PurgeClusterNode](API_CLUSTER.html#13b35a) API を使った場合の説明です。

> **警告**
>
> [PurgeClusterNode](API_CLUSTER.html#13b35a) APIでクラスターから消去したレギュラーノードは、
> 以下に示す手順を経ずに再起動してはいけません。

クラスターからレギュラーノードを完全消去した場合、そのまま再度参加をすることはできません。
再度クラスターに参加する場合は `data/` ディレクトリを削除する必要があります。

[PurgeClusterNode](API_CLUSTER.html#13b35a) API を実行して、さらにそのレギュラーノードの `data/` ディレクトリを削除した場合、
そのレギュラーノードは完全に新規のレギュラーノードとして取り扱われます。

この状態になったレギュラーノードは新規レギュラーノードですので、 [RegisterClusterNode](API_CLUSTER.html#09ed96) API を利用してクラスターに登録できます。

## クラスターから消去されたレギュラーノードを別クラスターに参加させたい場合

あるクラスター A に参加していたレギュラーノードを、A から削除して別のクラスター B に参加させたい状況を考えます。

> **警告**
>
> [PurgeClusterNode](API_CLUSTER.html#13b35a) API はクラスターからレギュラーノードを完全に消去するための API です。
> 現在参加しているクラスターで再利用するレギュラーノードに対しては使用しないでください。
> Sora が稼働するサーバーを再利用して、別のクラスター B に参加する新規レギュラーノードが欲しい場合には、
> Sora リリースの tar.gz を展開して、まっさらな状態からの新規レギュラーノード構築を推奨します。
> ここの説明は、どうしても既存の tar.gz 展開ディレクトリを再利用したい場合の説明です。

[PurgeClusterNode](API_CLUSTER.html#13b35a) API で消去されたレギュラーノードは `data/` ディレクトリ内のデータに以前参加していたクラスター A のノード群を覚えています。

そのため、再利用する場合には `data/` ディレクトリを削除して、クラスター A に関する情報を無くす必要があります。

> **警告**
>
> 以上の手順を踏まずに別クラスター B に参加させようとすると、最悪の場合には A と B がひとつになった
> クラスターになってしまう場合があります。

上記の手順の後は Sora を起動し、クラスター参加の手順に従って、レギュラーノードをクラスターに参加させます。

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

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

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

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

たとえば、5 レギュラーノードのクラスターが 3 レギュラーノードからなるグループ A と
残りの 2 レギュラーノードからなるグループ B の 2 グループに分断されるということが起こりえます。
それぞれのグループ内では通信ができるものの、他グループへの通信ができないという状態です。

この状態ですべてのレギュラーノードが接続を受け付けると、同じチャネル ID を指定していても、
あるクライアントはグループ A に繋がり、別のクライアントは グルーブ B につながる可能性があり、
それぞれのクライアント間で音声と映像が届かなくなります。

このような状況を防止するため、Sora は自分が半数以下のグループに属した場合に、新規接続の受け付けを停止します。
また、レギュラーノード数が半数以下のクラスターに参加しているレギュラーノードは、接続中のすべての接続を切断し、その後は接続を受け入れません。

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


### 全ノードが半数以下のグループに所属した場合の挙動

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

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