WebRTC(SFU形式)をOCI上で動かしてみた

# WebRTCとは
- Web Real-Time Communicationの略
- リアルタイム通信のためのオープンソースプロジェクト&規格
    - Googleが2011年からオープンソース化して開発
    - 2021年にW3C、IETFにて標準規格となる
- 動画ライブ配信で良く使われている
- 全主要ブラウザがサポート(Apple, Google, MS, Mozilla, Opera, etc.)
- OSSも数多くありバックエンド開発が容易な環境が揃っている


WebRTCの中でもSFU形式(Selective Forwarding Unit)を採用し、
豊富なOSSの中でもLiveKitを使用してOCI上で動かしてみました。
https://livekit.io/
https://docs.livekit.io/concepts/livekit-sfu/
※ライセンスはApache License 2.0

OCIはデータ転送コストが 月々10TBまで無料で、WebRTCのようなデータ転送の多いワークロードにとても有効です。
https://www.oracle.com/jp/cloud/networking/pricing/


# OCIの環境構築
まずは最もシンプルな構成として1つのVM(IaaS)で構築します。
1VMでsfuサーバ、webアプリを動かします。

### VCN
VCNを作成します。
パブリックサブネットを使用します。
![vcn.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846649/7d48dc88-2d2b-fa04-680b-b137ecda72af.jpeg)


イングレス/エグレスルールはLiveKitが使用するポートを適宜追加します。
![ingress.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846649/4702cae9-2d18-97d2-06f8-997beb98702a.jpeg)
![egress.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846649/8cf4d319-1b9a-75da-9cec-56c27acdcfe8.jpeg)


### VM
デモのため低スペックな1oCPU(=2vCPU)、16GB RAMで作成します。
LiveKitで使用するためグローバルIPを付与します。
![vm.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846649/72d33c71-f88f-e3dd-ed77-d5a129a4f2a1.jpeg)

1点注意点としてはOSで各ポートがfirewallでブロックされている場合はオープンにする必要があります。
LiveKitでの使用ポートは下記です：
https://docs.livekit.io/oss/deployment/vm/#firewall

OS上のfirewall設定を変更します。
https://docs.livekit.io/oss/deployment/vm/#instance-firewall
```shell: firewallポート確認
sudo firewall-cmd --list-all
```

```shell: firewallポート開放
sudo firewall-cmd --zone=public --permanent --add-port=80/tcp
sudo firewall-cmd --zone=public --permanent --add-port=443/tcp
sudo firewall-cmd --zone=public --permanent --add-port=7881/tcp
sudo firewall-cmd --zone=public --permanent --add-port=443/udp
sudo firewall-cmd --zone=public --permanent --add-port=50000-60000/udp
sudo firewall-cmd --reload
```



### ロードバランサ
単一ノードであるためデモ機能上は必要ないですが、
ドメイン付与のためフレキシブルロードバランサを設定します。
※フレキシブルロードバランサ↓
https://speakerdeck.com/ocise/ociji-shu-zi-liao-rodobaransa-gai-yao?slide=8

![lb.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846649/4e1c8cfc-197e-db87-f1e1-90cc512e83f2.jpeg)

LiveKitが使うポート(ex. 7780,5000, etc.)は今回そのままロードバランサで解放して叩かせるため、各ポートのリスナー設定を作ります。
![listener.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846649/6bdf3ca5-795e-a68a-ddb1-6299e19c3cba.jpeg)

ドメインを付与する場合は別途作成したSSL証明書をロードバランサに適用します。
![lb_domain.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846649/503cd152-90ea-62b5-50ab-5351fc0fa131.jpeg)

### DNS
ドメインを付与する場合はDNSサービスでAレコードを作成します。
![dns.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846649/bf358ab4-2688-9332-35d8-14e3f200b338.jpeg)



# LiveKitの環境構築
### SFUサーバ
下記github上の手順より実行できます。
https://github.com/livekit/livekit

簡単にまとめると、
Linuxの場合は上記で作成したVMにsshログインし、下記コマンドにてインストールできます。
```shell
curl -sSL https://get.livekit.io | bash
```
開発モードの場合は下記で起動します。
```shell
livekit-server --dev
```
起動時はconfig.yamlを作成します。
※今回はredis, turnはオフにします
```yaml: config.yaml
port: 7880
log_level: info
rtc:
  tcp_port: 7881
  port_range_start: 50000
  port_range_end: 60000
  # use_external_ip should be set to true for most cloud environments where
  # the host has a public IP address, but is not exposed to the process.
  # LiveKit will attempt to use STUN to discover the true IP, and advertise
  # that IP with its clients
  use_external_ip: true
redis:
  # redis is recommended for production deploys
  # address: my-redis-server.name:6379
keys:
  # key value pairs
  # your_api_key: <api_secret>
  devkey: secret
# when enabled, LiveKit will expose prometheus metrics on :6789/metrics
#prometheus_port: 6789
turn:
  enabled: false
  # domain must match tls certificate
  domain: <turn.myhost.com>
  # defaults to 3478. If not using a load balancer, must be set to 443.
  tls_port: 3478
```
yamlを指定して開発モードでLiveKitサーバを起動します。
```shell
livekit-server --dev --config /home/opc/config.yaml --bind 0.0.0.0 
```



### token発行API
実際にリアルタイム動画配信に参加するには、部屋とユーザ名をキーにしたアクセストークンを指定する必要があります。
開発モードの場合はLiveKitをインストールして使用できる下記cliを使用してサーバ側で払い出すことができます。

```shell
livekit-cli create-token \
    --api-key devkey --api-secret secret \
    --join --room my-first-room --identity user1 \
    --valid-for 24h
```
今回のデモではユーザがデモアプリから動的にアクセストークンを取得できるように、LiveKitサーバSDKを使用した簡易なapiを作成しました。
SDKの言語は主要なものはほぼ全てありますが、今回はts(js)を使用します。
https://github.com/livekit/server-sdk-js

- 固定ルーム名(roomname)
- ユーザ名をランダム文字列
- ポート5000使用
とするスクリプトです。

```typescript: createToken.ts
import * as http from "http";
import { AccessToken } from 'livekit-server-sdk';

const port = 5000;
const roomName = 'my-first-room';

const server = http.createServer(
  (request, response) => {

    response.setHeader("Access-Control-Allow-Origin", "*");
    const participantName = Math.random().toString(32).substring(2);
    // console.log("name=" + participantName);
    const at = new AccessToken('devkey', 'secret', {
      identity: participantName,
    });
    at.addGrant({ roomJoin: true, room: roomName });
    const token = at.toJwt();
    response.end(token);
  }
);
server.listen(port);
```

ts-nodeを使用して起動します。
```shell
ts-node token/createToken.ts
```

### LiveKitデモアプリ
こちらに公開されています。
https://github.com/livekit/components-js

※ただし今回は下記で構築
https://github.com/livekit/livekit-react

# デモ画面

PCと携帯からそれぞれルームにログインして動作確認を実施しました。
![demo2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2846649/474db945-2e83-493f-5a6b-55905d0b4804.jpeg)


# おわりに
WebRTCのOSSであるLiveKitを使用して、OCI上でSFUサーバを動かしてみました。
OSSを使用することで簡単にクラウドサーバ上にライブ配信を構築することができました。
kubernatesやredisを使ったWebRTC機能検証を今後実施予定です。