# クラスター機能チュートリアル

## 概要

このチュートリアルでは、Sora のクラスター機能をひととおり使ってみるところまでを説明します。

## 前提

クラスター特有ではない Sora の設定や起動方法、シグナリングで使用する nginx の設定等については [チュートリアル](TUTORIAL.html) を参照してください。

### ライセンス

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


## クラスターの構築
































まず、ここでは 3 ノードの Sora クラスターを構築する手順を示します。

それぞれのノードのノード名は **sora1@192.0.2.101**, **sora2@192.0.2.102**, **sora3@192.0.2.103** とします。

> **重要**
>
> `ノード名（node_name）` はクラスター内のすべてのノードのそれぞれがユニークである必要があります

> **注釈**
>
> クラスターを構築する際、クラスター間の通信に使うネットワークは、
> 可能な限りプライベートネットワークを利用してください。
>
> クラスター間の通信には各ノードの `node_name` で指定した @ 以下の IP アドレスまたはドメイン名を使用します。

ここでのクラスター構築では、3 台のサーバーがそれぞれ TCP/IP で通信できる状態になっていること、
そして Sora の tar.gz が展開されて `bin/sora` が実行できるようになっていることを前提としています。

1. クラスター間の通信に必要な TCP のポートをすべて開放します- TCP 4369- ポート番号の変更はできません、必ずこのポートを開放してください
   - TCP 49010-49020- `sora.conf` にて変更できますがデフォルトをお勧めします
2. 最初のノード(**sora1@192.0.2.101**)の `sora.conf` にクラスターの設定をします```ini
   node_name = sora1@192.0.2.101
   cluster = true
   external_signaling_url = wss://sora-node1.example.com/signaling
   external_api_url = https://sora-node1.example.com/api
   contact_node_name_list = sora1@192.0.2.101, sora2@192.0.2.102, sora3@192.0.2.103
   ```
3. 最初のノード(**sora1@192.0.2.101**)の Sora を `bin/sora daemon` で起動します
4. 次のノード(**sora2@192.0.2.102**)の `sora.conf` も最初のノードと同様にクラスターの設定をします```ini
   node_name = sora2@192.0.2.102
   cluster = true
   external_signaling_url = wss://sora-node2.example.com/signaling
   external_api_url = https://sora-node2.example.com/api
   contact_node_name_list = sora1@192.0.2.101, sora2@192.0.2.102, sora3@192.0.2.103
   ```
5. 次のノード(**sora2@192.0.2.102**)の Sora を `bin/sora daemon` で起動します
6. 最後のノード(**sora3@192.0.2.103**)の `sora.conf` にもクラスターの設定をします```ini
   node_name = sora3@192.0.2.103
   cluster = true
   external_signaling_url = wss://sora-node3.example.com/signaling
   external_api_url = https://sora-node3.example.com/api
   contact_node_name_list = sora1@192.0.2.101, sora2@192.0.2.102, sora3@192.0.2.103
   ```
7. 最後のノード(**sora3@192.0.2.103**)の Sora を `bin/sora daemon` で起動します
8. 最初のノード(**sora1@192.0.2.101**)の Sora に [InitCluster](EXPERIMENTAL_API_CLUSTER.html#621990) APIを実行し、3 ノードのクラスターとして初期化します```console
   $ http POST 127.0.0.1:3000/ x-sora-target:Sora_20221221.InitCluster \
       node_name_list:='["sora1@192.0.2.101", "sora2@192.0.2.102", "sora3@192.0.2.103"]'
   ```
9. 最初のノード(**sora1@192.0.2.101**)の Sora に [ListClusterNodes](EXPERIMENTAL_API_CLUSTER.html#a70901) APIを実行し、ノード一覧を取得します```console
   $ http POST 127.0.0.1:3000/ x-sora-target:Sora_20211215.ListClusterNodes
   ```

結果として、 **sora1@192.0.2.101** から **sora3@192.0.2.103** の 3 ノードの一覧が取得できれば、クラスターの構築は完了です。

### クラスター構築のトラブルシューティング

クラスター構築時に躓きやすいポイントと確認事項を示します。

- ノード間通信ができない場合- Sora はノード間通信を行うため、ファイアウォールの開放が必要です
  - [ポートの開放](CLUSTER.html#90df52) にあるとおりに、EPMD と sora の Listen ポートの開放を確認してください
- ライセンス違反のエラーが出る場合- [最大ノード数ライセンス](LICENSE.html#aee259) ではないライセンスを使っている場合には、すべてのノードで異なるライセンスが設定されている必要があります
  - [最大ノード数ライセンス](LICENSE.html#aee259) のライセンスを使っている場合には、指定されたノード数まで同じライセンスを利用できます
  - [最大ノード数ライセンス](LICENSE.html#aee259) とそうではないライセンスを混在させて利用することもできますが、それぞれについて上記の条件が満たされている必要があります
- contact_node_not_intialized エラーが出る場合- 未初期化のノードを contact_node_name に指定した場合に発生します
  - concact_node_name に指定するノードは [InitCluster](EXPERIMENTAL_API_CLUSTER.html#621990) で初期化したノードか、[JoinCluster](EXPERIMENTAL_API_CLUSTER.html#17f3f5) API でクラスターに参加したノードを指定する必要があります

## クラスター操作

上記のクラスターの構築の手順で、クラスターの構築と初期化が完了していることを前提とします。

### クラスターのノード一覧の取得

構築済みのクラスター内のノード名 **sora1@192.0.2.101**、 **sora2@192.0.2.102**、 **sora3@192.0.2.103** のいずれかで [ListClusterNodes](EXPERIMENTAL_API_CLUSTER.html#a70901) API を実行して、
クラスター内のノード一覧を取得します。

```console
$ http POST 127.0.0.1:3000/ x-sora-target:Sora_20211215.ListClusterNodes
HTTP/1.1 200 OK
access-control-allow-headers: Origin, X-Requested-With, Content-Type, Accept, x-sora-target
access-control-allow-methods: POST, OPTIONS
access-control-allow-origin: *
access-control-max-age: 1000
content-length: 1057
content-type: application/json
date: Fri, 08 Dec 2023 07:51:19 GMT
server: Cowboy

[
    {
        "connected": true,
        "external_api_url": "https://sora-node1.example.com/api",
        "external_signaling_url": "wss://sora-node1.example.com/signaling",
        "license_max_connections": 100,
        "license_max_nodes": 3,
        "license_serial_code": "DOC123-SRA-E002-201303-N3-100",
        "license_type": "Experimental",
        "mode": "normal",
        "node_name": "sora1@192.0.2.101",
        "version": "2023.2.0"
    },
    {
        "connected": true,
        "external_api_url": "https://sora-node2.example.com/api",
        "external_signaling_url": "wss://sora-node2.example.com/signaling",
        "license_max_connections": 100,
        "license_max_nodes": 3,
        "license_serial_code": "DOC123-SRA-E002-201303-N3-100",
        "license_type": "Experimental",
        "mode": "normal",
        "node_name": "sora2@192.0.2.102",
        "version": "2023.2.0"
    },
    {
        "connected": true,
        "external_api_url": "https://sora-node3.example.com/api",
        "external_signaling_url": "wss://sora-node3.example.com/signaling",
        "license_max_connections": 100,
        "license_max_nodes": 3,
        "license_serial_code": "DOC123-SRA-E002-201303-N3-100",
        "license_type": "Experimental",
        "mode": "normal",
        "node_name": "sora3@192.0.2.103",
        "version": "2023.2.0"
    }
]
```

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

接続済みのチャネルのノードへの割り当てを確認するために、[ListClusterChannels](EXPERIMENTAL_API_CLUSTER.html#0a4459) API を実行します。

まず、構築済みのクラスターに複数のチャネルで接続しておきます。

次に、クラスター内のいずれかのノードで [ListClusterChannels](EXPERIMENTAL_API_CLUSTER.html#0a4459) API を実行して、クラスターのチャネル割り当ての一覧を取得し、チャネルと割り当てられているノードを確認します。

```console
$ http POST 127.0.0.1:3000/ x-sora-target:Sora_20211215.ListClusterChannels
HTTP/1.1 200 OK
access-control-allow-headers: Origin, X-Requested-With, Content-Type, Accept, x-sora-target
access-control-allow-methods: POST, OPTIONS
access-control-allow-origin: *
access-control-max-age: 1000
content-length: 342
content-type: application/json
date: Mon, 11 Dec 2023 03:53:49 GMT
server: Cowboy

[
    {
        "channel_id": "hisui",
        "owners": [
            {
                "connected": true,
                "node_name": "sora1@192.0.2.101"
            }
        ]
    },
    {
        "channel_id": "kohaku",
        "owners": [
            {
                "connected": true,
                "node_name": "sora2@192.0.2.102"
            }
        ]
    },
    {
        "channel_id": "sora",
        "owners": [
            {
                "connected": true,
                "node_name": "sora3@192.0.2.102"
            }
        ]
    },
    {
        "channel_id": "zakuro",
        "owners": [
            {
                "connected": true,
                "node_name": "sora3@192.0.2.102"
            }
        ]
    }
]
```

### クラスターからノードを削除する

構築済みのクラスター内からノードを削除してみます。

ここでは、ノード名 **sora1@192.0.2.101**、 **sora2@192.0.2.102**、 **sora3@192.0.2.103** の 3 ノードで構築されているクラスターから、
**sora3@192.0.2.103** のノードを削除します。

ノードの削除には [PurgeClusterNode](EXPERIMENTAL_API_CLUSTER.html#13b35a) API を使用します。

初めに、 **sora3@192.0.2.103** の Sora を bin/sora stop で停止します。

次に、 **sora3@192.0.2.103** ノードの `data/` ディレクトリを削除します。

**sora1@192.0.2.101** のノードで [ListClusterNodes](EXPERIMENTAL_API_CLUSTER.html#a70901) API を実行して、
クラスターのノードから **sora3@192.02.103** のノードが見えなくなっていることを確認します。

```console
$ http POST 127.0.0.1:3000/ x-sora-target:Sora_20211215.ListClusterNodes
HTTP/1.1 200 OK
access-control-allow-headers: Origin, X-Requested-With, Content-Type, Accept, x-sora-target
access-control-allow-methods: POST, OPTIONS
access-control-allow-origin: *
access-control-max-age: 1000
content-length: 704
content-type: application/json
date: Fri, 08 Dec 2023 08:06:39 GMT
server: Cowboy

[
    {
        "connected": true,
        "external_api_url": "https://sora-node1.example.com/api",
        "external_signaling_url": "wss://sora-node1.example.com/signaling",
        "license_max_connections": 100,
        "license_max_nodes": 3,
        "license_serial_code": "DOC123-SRA-E002-201303-N3-100",
        "mode": "normal",
        "node_name": "sora1@192.0.2.101",
        "version": "2023.2.0"
    },
    {
        "connected": true,
        "external_api_url": "https://sora-node2.example.com/api",
        "external_signaling_url": "wss://sora-node2.example.com/signaling",
        "license_max_connections": 100,
        "license_max_nodes": 3,
        "license_serial_code": "DOC123-SRA-E002-201303-N3-100",
        "license_type": "Experimental",
        "mode": "normal",
        "node_name": "sora2@192.0.2.102",
        "version": "2023.2.0"
    }
]
```

クラスターから **sora3@192.0.2.103** ノードの情報を削除するために、 **sora1@192.0.2.101** または **sora2@192.0.2.102** ノードのいずれかで [PurgeClusterNode](EXPERIMENTAL_API_CLUSTER.html#13b35a) API を実行します。
この API を実行する際の `target_node_name` には削除するノード名を指定します。
今回指定するノード名は、 **sora3@192.0.2.103** です。

```console
$ http POST 127.0.0.1:3000/ x-sora-target:Sora_20220629.PurgeClusterNode target_node_name=sora3@192.0.2.103
HTTP/1.1 200 OK
access-control-allow-headers: Origin, X-Requested-With, Content-Type, Accept, x-sora-target
access-control-allow-methods: POST, OPTIONS
access-control-allow-origin: *
access-control-max-age: 1000
content-length: 40
content-type: application/json
date: Mon, 11 Dec 2023 02:03:09 GMT
server: Cowboy

{
    "target_node_name": "sora3@192.0.2.103"
}
```

### クラスターにノードを追加する

構築済みのクラスターにノードを追加してみます。

ノード名 **sora1@192.0.2.101**、 **sora2@192.0.2.102** の 2 ノードで構築されているクラスターへ、
API を使用して **sora4@192.0.2.104** ノードを追加します。

ノードの追加には [JoinCluster](EXPERIMENTAL_API_CLUSTER.html#17f3f5) API を使用します。

クラスターに **sora4@192.0.2.104** ノードを追加するために、 **sora4@192.0.2.104** ノードで [JoinCluster](EXPERIMENTAL_API_CLUSTER.html#17f3f5) API を実行します。
この API を実行する際の `contact_node_name` には既存のクラスターのノード名を指定します。

今回指定するノード名は、 **sora1@192.0.2.101** または **sora2@192.0.2.102** のいずれかです。

```console
$ http POST 127.0.0.1:3000/ x-sora-target:Sora_20211215.JoinCluster contact_node_name=sora1@192.0.2.101
HTTP/1.1 200 OK
content-length: 80
content-type: application/json
date: Mon, 11 Dec 2023 02:33:35 GMT
server: Cowboy

{
    "node_name_list": [
        "sora1@192.0.2.101",
        "sora2@192.0.2.102",
        "sora4@192.0.2.104"
    ]
}
```

[ListClusterNodes](EXPERIMENTAL_API_CLUSTER.html#a70901) API を使用して、クラスターに追加されていることを確認します。

```console
$ http POST 127.0.0.1:3000/ x-sora-target:Sora_20211215.ListClusterNodes
HTTP/1.1 200 OK
content-length: 1057
content-type: application/json
date: Mon, 11 Dec 2023 02:34:04 GMT
server: Cowboy

[
    {
        "connected": true,
        "external_api_url": "https://sora-node1.example.com/api",
        "external_signaling_url": "wss://sora-node1.example.com/signaling",
        "license_max_connections": 100,
        "license_max_nodes": 3,
        "license_serial_code": "DOC123-SRA-E002-201303-N3-100",
        "license_type": "Experimental",
        "mode": "normal",
        "node_name": "sora1@192.0.2.101",
        "version": "2023.2.0"
    },
    {
        "connected": true,
        "external_api_url": "https://sora-node2.example.com/api",
        "external_signaling_url": "wss://sora-node2.example.com/signaling",
        "license_max_connections": 100,
        "license_max_nodes": 3,
        "license_serial_code": "DOC123-SRA-E002-201303-N3-100",
        "license_type": "Experimental",
        "mode": "normal",
        "node_name": "sora2@192.0.2.102",
        "version": "2023.2.0"
    },
    {
        "connected": true,
        "external_api_url": "https://sora-node4.example.com/api",
        "external_signaling_url": "wss://sora-node4.example.com/signaling",
        "license_max_connections": 100,
        "license_max_nodes": 3,
        "license_serial_code": "DOC123-SRA-E002-201303-N3-100",
        "license_type": "Experimental",
        "mode": "normal",
        "node_name": "sora4@192.0.2.104",
        "version": "2023.2.0"
    }
]
```
