Mozilla Hubsをセルフホストする

この記事は, [対偶TUT Advent Calendar 2023](https://adventar.org/calendars/9089) 21日目の記事です.

昨日の記事は, 與田レオ【よだ れお】さんの, 「[推しができて人生が進化した話](https://note.com/hatimitumanjuu/n/n7df23b15078b)」でした. 

## はじめに
DDliaと申します. 情報・知能工学課程の学部2年生です. 去年に引き続き, TUT Advent Calendar に参加させていただきます. 
今回は, Mozilla Hubsという, WebXRでよく用いられるソフトウェアを手元のサーバで動かしてみようと思います.

TUT Advent Calendar 2023 の1枚目であるTUT Advent Calendar及び, 2枚目の裏TUT Advent Calendar 2023にも参加しております. よければそちらもごらんください.

TUT Advent Calendar 2023 13日目: https://note.com/ddlia/n/n58dee466d2b7
対偶TUT Advent Calendar 2023 13日目: https://qiita.com/dodolia907/items/b92614bf84be47551ee2
裏TUT Advent Calendar 2023 16日目: https://note.com/ddlia/n/n2dd560a80296

## Mozilla Hubsって何？
WebXR関連のサービスでよく使われるソフトウェアです. おそらく, 説明するよりも先に使ってもらったほうがわかりやすいと思います.  
豊橋技科大のメタバースポータルサイトでMozilla Hubsが使われているので, 早速使ってみましょう.  
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">【ギカダイNews】テック・メタバースの「ポータルサイト」を開設しました。一般の方も体験できるメタバースもありますので、ぜひご体験ください。<br>スマートフォンやコンピュータのブラウザ、あるいはヘッドマウントディスプレイで体験できます。<a href="https://t.co/25qmvdL1k4">https://t.co/25qmvdL1k4</a><a href="https://twitter.com/hashtag/TUT?src=hash&amp;ref_src=twsrc%5Etfw">#TUT</a> <a href="https://twitter.com/hashtag/%E8%B1%8A%E6%A9%8B%E6%8A%80%E8%A1%93%E7%A7%91%E5%AD%A6%E5%A4%A7%E5%AD%A6?src=hash&amp;ref_src=twsrc%5Etfw">#豊橋技術科学大学</a></p>&mdash; 国立大学法人豊橋技術科学大学 Toyohashi University of Technology (@Toyohashi_Tech) <a href="https://twitter.com/Toyohashi_Tech/status/1727854546125459657?ref_src=twsrc%5Etfw">November 24, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

GO TO THE FUTURE(GTTF)の周辺だとか, A-101講義室が細かく再現されていますね.  
本当にリアルですごいと思います.

## なぜセルフホストか？
Mozilla Hubsはセルフホストせずとも無料でも使うことができます. しかし, 無料版は制限が大きく, カスタマイズできるところが限られている上, 有料版もなかなか良いお値段がします.  
10月16日以前もAWS(Amazon Web Service)でのセルフホストはサポートされていたのですが, こちらも良いお値段がするので, どうにか自宅サーバで動かしたいと考えていました.

## 試行錯誤
6月くらいに, [インフィニットループさんの技術記事](https://www.infiniteloop.co.jp/tech-blog/2022/02/mozilla-hubs-on-virtualbox/)を見て, Mozilla Hubsをセルフホストしたいな～と思っていたのですが, 当時はMozilla公式がセルフホストをサポートしておらず, うまくいきませんでした. 　
しかし, 今年の10月16日にMozillaから[セルフホストをサポートするとの記事](https://hubs.mozilla.com/labs/welcoming-community-edition/)が発表され, 試してみることにしました.  

## 動かしてみる
基本的には, この[README](https://github.com/mozilla/hubs-cloud/tree/master/community-edition)のとおりに操作していくだけです. 
https://xr.technotut.net で公開することを想定して作業します.

### 事前準備
必要なもの
- Kubernetesクラスタ
- ドメイン
- 以下のポートを開放できる環境
TCP: 80, 443, 4443, 5349
UDP: 35000-60000
- メールサーバ(SMTPサーバ)

Kubernetesクラスタは[対偶TUT Advent Calendar 13日目の記事](https://qiita.com/dodolia907/items/b92614bf84be47551ee2) で準備したものを使います.  
環境は以下の通りです.  

HITACHI HA8000/RS220-h
OS: Oracle Linux 9.3
CPU: Intel Xeon E5-2630 v2 2枚 (12コア24スレッド)
メモリ: 104GB

ドメインは音楽技術部のものを ~~勝手に~~ 使います.  

ポート開放は, 学生宿舎だとDoCANVASの動的か固定IPオプションを契約するとできるようになるので, それを使います.  
具体的な設定については, 豊橋技術科学大学 コンピュータクラブが発行している, [とよぎぃ通信 Vol.9](http://tut-cc.org/toyogy.html) に書いていますので, そちらをご覧ください.  

メールサーバーは, 自分で構築しても良いのですが, 面倒なので[さくらのメールボックス](https://rs.sakura.ad.jp/mail/) を使います. 年1000円とちょっとで使えます. Gmailを使えば無料でもできるらしいです.

### Kubernetesクラスタの準備
対偶13日目の記事の続きから作業を行います.  
Mozilla Hubsをデプロイするにあたって, ロードバランサーサービスが必要なので, [MetalLB](https://metallb.universe.tf/)を導入します.  

MetalLBはBGPを使ってIPアドレスを広報するらしいのですが, すでにCNIのCalicoがルータとピアを張っているので, [公式ドキュメント](https://metallb.universe.tf/installation/)通りにインストールすると動きません.  
CalicoとMetalLBを共存させる方法がわからず, [launchpencil](https://twitter.com/launchpencil)大先生に泣きついたらマニフェストを頂けたのでこれを`apply`します.
```
kubectl apply -f https://github.com/launchpencil/lp-infra/blob/main/k8s/setup/metallb.yaml
```

### メールサーバの準備
[さくらのメールボックス](https://rs.sakura.ad.jp/mail/) を契約して, 初期ドメインを設定します.  

独自ドメインを使いたいので, さくらのレンタルサーバ コントロールパネルから設定します.  
`ドメイン新規作成` から `他社で取得したドメインを移管せずに使う`の`追加`を選択します.  

ドメインのネームサーバの設定を開き, `ns1.dns.ne.jp`
`ns2.dns.ne.jp`をNSレコードで追加します.
音楽技術部の場合は, `technotut.net`をCloudflareで管理していますので, Cloudflareのダッシュボードを開き, 設定を行います.  
以下は`mail.xr.technotut.net`というドメインを利用する場合の設定例です.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2725916/df02963a-b7ab-2dad-3eb5-bc1a38d4de61.png)


### インストール・デプロイ
GitHubのREADME通りにやります.  
まずは`clone`します.
```
git clone https://github.com/mozilla/hubs-cloud.git
cd hubs-cloud/community-edition
```
`cd`コマンドでcommunity-editionのディレクトリに移動して, `render_hcce.sh` を編集して, `ChangeMe`を適切な値に変更します.
```
# ドメインの設定
export HUB_DOMAIN="xr.technotut.net"
# 管理者権限を付与するアカウントのメールアドレスを入力
export ADM_EMAIL="hubs-cloud.admin@mail.xr.technotut.net"

# KubernetesのNamespaceの設定
# 変えたかったら変える
export Namespace="hcce"

# データベース設定
export DB_USER="postgres"
export DB_PASS="123456"
export DB_NAME="retdb"
export DB_HOST="pgbouncer"
export DB_HOST_T="pgbouncer-t"
export PGRST_DB_URI="postgres://$DB_USER:$DB_PASS@pgbouncer/$DB_NAME"
export PSQL="postgres://$DB_USER:$DB_PASS@pgbouncer/$DB_NAME"

# メールアドレス(送信サーバ)の設定
export SMTP_SERVER="<初期ドメイン>.sakura.ne.jp"
# Portは587以外だと動かない(っぽい)
export SMTP_PORT="587"
export SMTP_USER="hubs-cloud.admin@mail.xr.technotut.net"
export SMTP_PASS="<password>"

# ここらへんは適当に変える(たぶん)
export NODE_COOKIE="changeMe"
export GUARDIAN_KEY="changeMe"
export PHX_KEY="changeMe"

# とりあえず触らなくて良い
export SKETCHFAB_API_KEY="?"
export TENOR_API_KEY="?"
```
変更したら, `bash render_hcce.sh`を実行して, hcce.yamlを生成し, `kubectl apply -f hcce.yaml`します. 
Namespaceがないと怒られたら, `kubectl create namespace hcce` で作ります.
`watch kubectl -n hcce get pods` で見守りましょう.  

私の手元の環境だと, `dialog`と`coturn`が`Pending`から`Running`に移行しませんでした.  
`kubectl -n hcce describe pods <dialogのPod名>`, `kubectl -n hcce describe pods <coturnのPod名>`を実行してみると, `1 node(s) didn't have free ports for the requested pod ports.`というエラーが出ていました(該当ノードのポートは開いている).  

調べてみると`HostNetwork` が`true`になっているとよくないらしいので, `hcce.yaml`の`HostNetwork：true`を削除し, `hostPort: 4443`, `hostPort: 5349`も削除します.  

編集したら, `kubectl apply -f hcce.yaml`で適用します. また, `kubectl -n hcce get pods`でPod名を確認し, `kubectl delete <reticulumのPod名>`でreticulumを再起動します.  
`watch kubectl -n hcce get pods` で見守って, すべて`Running`になっていることを確認します.  

`kubectl -n hcce get svc lb to find it's external ip`を実行して, IPアドレスを確認します.  

### ポート開放
`kubectl -n hcce get svc lb to find it's external ip` で出てきたIPをインターネットから接続できるように設定します. 
ルータによって設定方法が異なりますが, TCP: 80, 443, 4443, 5349, UDP: 35000-60000 を開放します.

NEC IXシリーズでは, 以下のように操作することでポート開放が可能です.  
```
interface GigaEthernet0.1
ip napt static <server-ip> tcp 80
ip napt static <server-ip> tcp 443
ip napt static <server-ip> tcp 4443
ip napt static <server-ip> tcp 5349
ip napt static <server-ip> udp 35000-60000
```

### ドメインの設定
ドメインのネームサーバの設定を行い, `xr.technotut.net`, `assets.xr.technotut.net`, `stream.xr.technotut.net`, `cors.xr.technotut.net`のAレコードを設定します. 

### ちょっと待つ
DNS情報が浸透するまで待ちます. 数時間から48時間待ちましょう.

### 証明書の設定
hubs自体はもう起動しているのですが, オレオレ証明書を使っているので https://xr.technotut.net にブラウザでアクセスすると証明書エラーが出ます.  
Let's Encryptで証明書を発行して, このエラーを解消します.  

Let's Encryptで証明書を発行するスクリプトはMozilla側で用意していただいているので, これを使います.  
`cbb.sh`を開いて, 以下のように編集します.  
```
export Namespace="hcce"
export ADM_EMAIL="hubs-cloud.admin@mail.xr.technotut.net"
export HUB_DOMAIN="xr.technotut.net"
```
編集が終わったら, `bash cbb.sh`で証明書を発行します.    

※注意 Let's Encryptは時間あたりの証明書の発行上限が決まっており, 発行に失敗しすぎて上限に達すると1週間程度証明書が発行できなくなります.
※私は試行錯誤してたら上限に引っかかりました. 1週間待ちです.

## 使ってみる
証明書がきちんと発行できていれば, https://xr.technotut.net にアクセスすることで, Mozilla Hubsを使うことができます.  
私のように, しばらく証明書が発行できなくなった場合は,  
https://assets.xr.technotut.net
https://stream.xr.technotut.net
https://cors.xr.technotut.net
https://xr.technotut.net
を開き, 以下の画像のように証明書エラーを無視するようにしてあげれば, Mozilla Hubsを使うことができます.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2725916/b13a8e9d-2b79-7ab9-4af4-c1f699393506.png)

ということで, 今回は証明書の発行に失敗したので, 上のURLをそれぞれ開いて, 証明書エラーを無視してやれば...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2725916/7eda705d-b56a-fd3c-8c10-3ea54e668d1d.png)
来ました！

初回起動の場合は, 設定した管理者権限を付与するメールアドレスを入力する必要があります.  
その後, シーンやアバターをダウンロードして設定すると, ルームを作れるようになります.  
https://xr.technotut.net では, 以上の設定を済ませたため, ユーザ登録なしで, 使えるようになっています.  

左下の, Public Roomsに入ると...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2725916/ae38b1c7-f688-b99f-eff3-f07bedc1a0c7.png)
無事に入ることができました.  

## 追記(12/21 5:00)
CloudflareとZeroSSLで証明書を発行し, 手動で証明書を配置することでとりあえず解決しました.  
`*.technotut.net`はCloudflareのUniversal SSLの対象なので, `xr.technotut.net`は`Origin CA certificates`を発行します. 
`*.xr.technotut.net`は無料枠ではCloudflareのUniversal SSLの対象でないので, ZeroSSLで証明書を発行します.  
### Cloudflareで証明書を発行
Cloudflareのダッシュボードを開き, `SSL/TLS`の`Origin Server`を開きます.  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2725916/023cf5eb-aeea-07be-147d-2ac661519335.png)

`Create Certificate`から証明書を作ります. RSAかECC(楕円)か選んで, ホスト名を入力して`Create`します.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2725916/50430f6c-c795-55da-e655-e3e395239cb6.png)

`Origin Certificate`と`Private Key`が出力されるので, コピーして`cf-cert.pem`, `cf-key.pem`などの適当な名前で適当なディレクトリに保存します.  

`kubectl -n hcce create secret tls cert-xr.technotut.net --cert=/<path>/cf-cert.pem --key=/<path>/cf-key.pem` を実行して, 証明書をインストールします.  
`error: failed to create secret secrets "cert-xr.technotut.net" already exists` と怒られたら `kubectl -n hcce delete secret cert-xr.technotut.net` で一旦消してからインストールします.  

### ZeroSSLで証明書を発行
ZeroSSLのダッシュボードを開き, `New Certficate`を選択します.  
`Enter Domain`に`assets.xr.technotut.net`, `Validity`を`90-Day`に, `CSR & Contact`を適切に設定して, `Next Step`を選択します.  
ドメインの検証が始まるので, 今回はメール認証で検証を行います. 受信できるメールアドレスを一覧から選択し, `Next Step`を選択します.  
選択したメールアドレス宛にメールが届くので, そのリンクを踏んで認証します.  
CAAレコードがない, といったエラーが出ることがあるので, その際は以下のようなCAAレコードを作成して対応します.  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2725916/baaac825-1ee2-3e3c-8c7c-3ce818cf14ec.png)

認証が完了すると, 証明書をダウンロードできるようになるので, ダウンロードしてサーバに転送します. zipで圧縮されているので解凍もしておきます.  

先ほどと同様の手順で証明書をインストールします. `kubectl -n hcce create secret tls cert-assets.xr.technotut.net --cert=/<path>/certificate.crt --key=/<path>/private.key` を実行します.  
同様の手順で, `stream.xr.technotut.net`, `cors.xr.technotut.net`の証明書も発行します.  

### DNSの設定
Cloudflareのダッシュボードを開いて, `DNS`>`Records`を編集します.  
`xr.technotut.net` は`Proxied`に, `assets.xr.technotut.net`, `stream.xr.technotut.net`, `cors.xr.technotut.net` は`DNS only`に設定します.  

### リバースプロキシの設定  
自宅のIPアドレスを見せておくと怖いので, リバースプロキシを経由させて隠します.  
任意のVPSを用意して, そこにリバースプロキシを建てます. 今回はNTTPCのWebARENAを使いました.  
nginxを自前でビルドして, 設定を行います. 設定を行ったら, `assets.xr.technotut.net`, `stream.xr.technotut.net`, `cors.xr.technotut.net`のDNS設定を変更します.

### done
これで, https://xr.technotut.net にアクセスすると, 使えるはずです.

## おわりに
いかがでしたか？　　
今回はKubernetesクラスタ(？)上にMozilla Hubsをセルフホストしました.   
6月に試した手順よりも, 大分楽になったような気がします. なにより簡単に動くので感動します.
いつか, VRコモンズのワールドを作って, みんなでわちゃわちゃするのも楽しそうだなぁ～と考えたりしています.

明日の記事は, ゔぉるふさんの「同世代へ薦めたいバンド(くどい文でゴメンナサイ)」です. お楽しみに！

## 参考資料
[Mozilla Hubs を VirtualBox でホストしてみた](https://www.infiniteloop.co.jp/tech-blog/2022/02/mozilla-hubs-on-virtualbox/)
[Community Edition Is Here!](https://hubs.mozilla.com/labs/welcoming-community-edition/)
[mozilla/hubs-cloud: Resources for self hosted Hubs Cloud instances](https://github.com/mozilla/hubs-cloud/)