インフィニットループ 技術ブログ

2022年02月22日 (火)

著者 : nob

Mozilla Hubs を VirtualBox でホストしてみた

みなさんメタバース盛り上がってますか!インフラエンジニアの nobuh です
Facebook も社名を Meta に変更するなど、メタバースが次世代のネットの主役になりえる?可能性があるということで、その動向には目を離せないところです。
すでに実現、実装されているメタバースや VR のプロダクトやサービスもいろいろありますが、今回は Mozilla 社がオープンソースで公開している Mozilla Hubs に注目し、そのサーバーを自分の手元の VirtualBox で動かすことに挑戦してみました!

Mozilla Hubs とは

Mozilla 社の作ったブラウザでアクセス可能な VR の空間共有サービスで、簡単に要約すると3つの形態があります。

  • hubs.mozilla.com : Mozilla 社が運営してくれている VR の道具一式がそろったサービスです。
  • Hubs Cloud Personal / Hubs Cloud Enterprise : AWS マーケットプレースまたは DigitalOcean でイメージやインスタンスを契約/購入し、それらを使って自分のカスタムドメインのクラウドインスタンスで Mozilla Hubs 一式をホストします。購入したといっても、自分のドメインで運用出来るようにセットアップするまではなかなか手数もかかるようです。
  • GitHub で hubs リポジトリ  https://github.com/mozilla/hubs や reticulum など Hubs に必要なサーバーのコードが公開されています

今回はこのうちの、GitHub のリポジトリのソースを使い、自分の PC 上の VirtualBox で myhubs.example というテスト用のカスタムドメインで Hubs のサーバーをホストしてみます!

VirtualBox でのサーバー準備

まずは VirtualBox で仮想サーバーを用意します。OS は Debian 11.2 bullseye の netinst iso 版を使いましたが、後述する DB や 言語は docker や asdf を使って用意していますので、おそらく Debian / Ubuntu であればどの Linux ディストリビューションでも問題ないと思います。
VirtualBox でのサーバー追加は全部解説するとスペースが足りませんので、設定の要点のみ列挙いたします。

  • OS : 64 bit Linux なら OK, 32 bit では試してないです
  • ネットワークアダプタ: ブリッジネットワーク を使います
  • CPU : 2個以上
  • メモリ :最少でも 5G 、推奨 8G 以上
  • 仮想ディスク: 20G ありばとりあえず足ります

同様に Debian のインストールも要点のみ列挙します

  • インストーラー CD : Debian 11.2 bullseye netinst iso
  • 国: Asia, Japan
  • ロケール : en_US.UTF-8 (必須ではないですがエラーメッセージ調査が簡単になるので US にしてます)
  • キーボード : Japanese
  • hostname : myhubs
  • domain : example
  • 追加 user : ops としました
  • パッケージ : ssh, standard utilities のみチェックを付けます。使わない Desktop とかはチェックを外します
  • ネットワーク:ブリッジネットワークにて DHCP で取得した IP をそのまま使っています。お好みで static ip に変更してもかまいません

まず最初に root でログインし ops に sudo 権限を付与します

apt-get update
apt install sudo
usermod -aG sudo ops

以降本記事では全て ops でログインし作業しているものとします

DNS 代わりの etc/hosts 設定

ブラウザでアクセスする PC と VirtualBox 上の myhubs サーバーと、両方の etc/hosts にサーバーの IP とホスト名を追加します。サーバーの IP が 192.168.0.5 だったときの例

192.168.0.5     myhubs.example

自己署名証明書作成

いわゆるオレオレ証明書ですが、myhubs.example を自分で承認したサーバー証明書を用意します。
ops ユーザーで cert ディレクトリを作成しそこで秘密鍵と CSR を生成します。
Common Name (CN) だけの証明書は非推奨の流れがありますので Subject Alt Name (SAN) も用意します。SAN を用意するのでワイルドカードのドメイン *.myhubs.example を追加しておきました。

mkdir ~/certs && cd certs
echo subjectAltName = DNS:myhubs.example,DNS:*.myhubs.example > san.txt
openssl genrsa -out server.key 2048
openssl req -out server.csr -key server.key -new

会社名など色々聞かれるが自由に記載してかまいません、 CN には myhubs.example を指定するところだけ忘れずに。
次に CSR と san.txt を合わせて署名して証明書 server.crt を生成します。

openssl x509 -req -days 3650 -signkey server.key -in server.csr -out server.crt -extfile san.txt

生成した証明書の内容を openssl コマンドで確認してみた例です。

ops@myhubs:~/mozilla$ openssl x509 -text -noout -in ../certs/server.crt |grep -C 3 myhubs
Serial Number:
4b:99:86:72:ad:ac:40:5a:10:8c:d5:ac:de:ef:f9:ef:16:93:15:cb
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = JP, ST = Hokkaido, L = Sapporo, O = NOBUH, OU = HUBS, CN = myhubs.example
Validity
Not Before: Feb 19 14:33:09 2022 GMT
Not After : Feb 17 14:33:09 2032 GMT
Subject: C = JP, ST = Hokkaido, L = Sapporo, O = NOBUH, OU = HUBS, CN = myhubs.example
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
--
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:myhubs.example, DNS:*.myhubs.example
Signature Algorithm: sha256WithRSAEncryption
52:72:00:9b:b7:b5:c7:8a:30:ad:d7:9a:9a:87:7e:51:80:2f:
79:e8:00:52:54:37:1d:26:65:67:a2:7b:c2:5b:41:f2:cd:93:

PostgreSQL 11 のために Docker を用意

Hubs で指定されている DB が PostgreSQL 11 で多少古いため、docker を使って準備します。尚、psql クライアントは最新のでも操作できますので単独でインストールしておきます。

sudo apt-get install ca-certificates curl gnupg lsb-release postgresql-client
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

一度ログアウト後 docker-compose をインストールします

sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

PostgreSQL セットアップ

Hubs の各種リポジトリを入れるディレクトリ mozilla を造り、そこに docker-compose.yml ファイルを作成します。

mkdir ~/mozilla && cd mozilla
touch docker-compose.yml

docker-compose.yml

version: '3'
services:
db:
image: postgres:11
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
volumes:
- ./tmp/db:/var/lib/postgresql/data
ports:
- "5432:5432"

起動し psql で database 一覧を見るなど確認します

sudo docker-compose up -d
psql -U postgres -h 127.0.0.1 -c '\l'

asdf をインストール

asdf を使い各言語は任意のバージョンを指定して使えるようにします。

sudo apt install curl git dirmngr gpg gawk
git clone https://github.com/asdf-vm/asdf.git ~/.asdf
echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc
echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc
exec $SHELL -l

ます最初に node.js v12 (erbium) をインストールします

asdf plugin add nodejs
asdf install nodejs lts-erbium
asdf global nodejs lts-erbium

Reticulum サーバーのセットアップ

Mozilla Hubs は JS のフロントエンドのサーバーである Hubs と、ユーザー管理やルーム内部の同期などの全てを処理する Reticulum 、音声に使われる Dialog など複数のサーバーで処理を行っています。まず最初に Reticulum サーバーをセットアップします。

git clone https://github.com/mozilla/reticulum.git
cd reticulum
git reset --hard d627ff04 (今回の手順で構築出来てるのがこの commit で、その後未確認なのでここまで戻してください)

asdf を使って Elixir 1.12 + Erlang 23 を入れます

asdf plugin add erlang
asdf plugin add elixir
sudo apt install libssl-dev automake autoconf libncurses5-dev make unzip g++ inotify-tools
asdf install erlang 23.3.4.11
asdf global erlang 23.3.4.11
asdf install elixir 1.12.3-otp-23
asdf global elixir 1.12.3-otp-23

phoenix フレームワークのアプリのビルドを mix コマンドで行います

mix local.hex --force
mix deps.get
mix local.rebar --force
mix ecto.create
mkdir -p storage/dev

最初に作ってあったサーバー証明書を priv/cert ディレクトリを作ってシンボリックリンクで配置します

mkdir -p priv/cert
ln -s ~/certs/server.crt priv/cert/
ln -s ~/certs/server.key priv/cert/

次に Dialog との通信用鍵ペアを生成しますが、ここが少し面倒です。
https://travistidwell.com/jsencrypt/demo/ というサービスがありますのでそこで 1024 ビット RSA キーを生成し、priv/cert ディレクトリに以下のファイル名で秘密鍵と公開鍵の内容を保存しておきます(下のはあくまで例です)
priv/cert/perms.priv.pem

-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgGelwkR0D5Eaox0+NisT4T4xSIz63FOmGK2hrP49LokyxKqYHPPx
KHO4Jgsof84oSRfBI9TRP/htlTl5eY979CyUzR2UjENVE36NihcPEAsrjcKk6Km8
LW5sJwswFglLLTdLRH0Au0397Isux764hPDlfYI0UETQ3icSjgFtHewPAgMBAAEC
gYA1UuVIfIGJwK+MmvYZYYfvjEFsLp/t9TUbF2O+BVIMye6+abXzlu2d427HLNXc
BYPdUcOSePk1YYN1Z1awCDCNm2JnquRT0Z1/HgmMeLHFvKXKLmpsKJhd2viianmS
1ND........................................................1CAPT
5I+S4s/RYIDbQC71gn0KKYO2srZXPnlYfcUU7XGgjuSu8b4es6zDB0lMgqf9ZElm
2dK2ji0CQQCc5hs5g0CvZBw0YYNVrK8hrcTV6Ypb8i4Z3uR9RonWDQuY81fj+hqV
wmO/vRrJoFHK6rKOxMrgwYxl/zNnrUSrAkBbmk6lY7yi1mvn/t/AMVUwMn6ubZSf
mxa6ekWwdcbp4+oQOrGtjj9rnAeqsWR6khDLFL0epBXlvuiSpeZ5L69lAkBpCfgh
+cf9Y7UqMDo/yjr4/h+v4gjZ43mPolQvtmCi59riy88EdjUEG76x58UeRPFdOuDN
idwUuh7nTgG5IBu/AkBN94zKUzG2YRZ0wAz+xhQtprZnV3WkYGfs25mYmB12UDzM
DKoQJvNX+i5HLiZASybFEvF/4+mm8fFjqVW0awwL
-----END RSA PRIVATE KEY-----

priv/cert/perms.pub.pem

-----BEGIN PUBLIC KEY-----
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgGelwkR0D5Eaox0+NisT4T4xSIz6
3FOmG......................................................UzR2U
jENVE36NihcPEAsrjcKk6Km8LW5sJwswFglLLTdLRH0Au0397Isux764hPDlfYI0
UETQ3icSjgFtHewPAgMBAAE=
-----END PUBLIC KEY-----

次に sed を使って秘密鍵の中味の改行コードを素の文字列 \n に置き換えたものを perms.priv.pem.1line として作成します。

sed -z 's/\n/\\n/g' priv/cert/perms.priv.pem |sed -E 's/\\n$/\n/g' > priv/cert/perms.priv.pem.1line

いよいよ設定ファイルの config/dev.exs 内を修正します

  • ホスト名を myhubs.exampole に変更
  • 証明書ファイル名のパス priv/cert〜 に変更
  • perms_key で定義している秘密鍵の内容を perms.priv.pem.1line をコピペしたものに入れ替える
  • Dialog (janus host) のポートを 4443 に変更

元ファイルを dev.exs.original として diff したのがこちらです

ops@myhubs:~/mozilla/reticulum$ diff config/dev.exs.original config/dev.exs
5,8c5,8
< host = "hubs.local"
< cors_proxy_host = "hubs-proxy.local"
< assets_host = "hubs-assets.local"
< link_host = "hubs-link.local"
---
> host = "myhubs.example"
> cors_proxy_host = "myhubs.example"
> assets_host = "myhubs.example"
> link_host = "myhubs.example"
13c13
< dev_janus_host = "dev-janus.reticulum.io"
---
> dev_janus_host = "myhubs.example"
28,29c28,29
<     keyfile: "#{File.cwd!()}/priv/dev-ssl.key",
<     certfile: "#{File.cwd!()}/priv/dev-ssl.cert"
---
>     keyfile: "#{File.cwd!()}/priv/cert/server.key",
>     certfile: "#{File.cwd!()}/priv/cert/server.crt"
175c175
< config :ret, Ret.PermsToken, perms_key: (System.get_env("PERMS_KEY") || "") |> String.replace("\\n", "\n")
---
> config :ret, Ret.PermsToken, perms_key: "<-----BEGIN RSA PRIVATE などのヘダー含めて、ここに 1line 化した秘密鍵をコピペで貼り付けます>"
201c201
< config :ret, Ret.JanusLoadStatus, default_janus_host: dev_janus_host, janus_port: 443
---
> config :ret, Ret.JanusLoadStatus, default_janus_host: dev_janus_host, janus_port: 4443

以下のコマンドで dev モードの Reticulum を起動します。

iex -S mix phx.server

Public な room を検索する API https://myhubs.example:4000/api/v1/media/search?source=rooms&filter=public&cursor=0 をブラウザで試してみます。下記のようなJSON を返せば OK です

{"meta":{"source":"public_rooms","next_cursor":1},"entries":[],"suggestions":null}

Coturn セットアップ

WebRTC Mediasoup で作られた Dialog を使うために、必要となる STUN/TURN サーバーの coturn をセットアップします。

sudo apt install coturn
sudo systemctl stop coturn
sudo cp /etc/turnserver.conf /etc/turnserver.conf.original

/etc/turnserver.conf にて 127.0.0.1 の PostgreSQL ret_dev データベースのスキーマ coturn でアクセスするように設定し、以下の diff の様に設定します

$ sudo diff /etc/turnserver.conf.original /etc/turnserver.conf
299c299
< #psql-userdb="host=<host> dbname=<database-name> user=<database-user> password=<database-user-password> connect_timeout=30"
---
> psql-userdb="host=127.0.0.1 dbname=ret_dev user=postgres password=postgres options='-c search_path=coturn' connect_timeout=30"

設定が終わったら sudo systemctl start coturn でスタートさせます

Dialog セットアップ

Reticulum, Coturn の次に音声サーバーの Dialog をセットアップします

cd ~/mozilla
git clone https://github.com/mozilla/dialog.git
cd dialog
git reset --hard 4edefad (今回の手順で構築出来てるのがこの commit で、その後未確認なのでここまで戻してください)
npm install

証明書を配置し、reticulum の方で作成しておいた通信用公開鍵を配置します

mkdir certs
ln -s ~/certs/server.crt certs/fullchain.pem
ln -s ~/certs/server.key certs/privkey.pem
ln -s ~/mozilla/reticulum/priv/cert/perms.pub.pem certs/perms.pub.pem

次のコマンドで自分のサーバーの IP (例 192.168.0.5)を指定して起動します

MEDIASOUP_LISTEN_IP=0.0.0.0 MEDIASOUP_ANNOUNCED_IP=192.168.0.5 npm start

Hubs セットアップ

最後に Web のフロントサーバーの Hubs をセットアップします
ファイルシステムの inotify がデフォルトの 8000 などだと小さいので予め拡張します。
/etc/sysctl.conffs.inotify.max_user_watches=50000 の1行を追加し sudo sysctl –system で反映します。
ソースを取得し、ここでも手順で実績ある commit まで戻しておきます

git clone https://github.com/mozilla/hubs.git
cd hubs
git reset --hard 926dbde62

最初に作っておいた証明書をここでも使います。ファイル名は hubs/certs/cert.pemkey.pem 固定です

mkdir certs
ln -s ~/certs/server.crt certs/cert.pem
ln -s ~/certs/server.key certs/key.pem

.defaults.env.env にコピーしホスト名を変更します。

< SHORTLINK_DOMAIN="hub.link"
---
> SHORTLINK_DOMAIN="myhubs.example:4000"
8c8
< RETICULUM_SERVER="dev.reticulum.io"
---
> RETICULUM_SERVER="myhubs.example:4000"
11c11
< CORS_PROXY_SERVER="hubs-proxy.com"
---
> #CORS_PROXY_SERVER="hubs-proxy.com"
21c21
< NON_CORS_PROXY_DOMAINS="hubs.local,dev.reticulum.io"
---
> NON_CORS_PROXY_DOMAINS="hubs.local,dev.reticulum.io,myhubs.example:4000"

webpack.config.js 内も myhubs.example に変更します。以下 diff 例です。

241c241
<   const host = process.env.HOST_IP || env.localDev || env.remoteDev ? "hubs.local" : "localhost";
---
>   const host = process.env.HOST_IP || env.localDev || env.remoteDev ? "myhubs.example" : "localhost";
287c287
<       allowedHosts: [host, "hubs.local"],
---
>       allowedHosts: [host, "myhubs.example"],

ビルドします。(メモリを大量に使い、時間もかかります)

npm ci

dev サーバースタート

npm run dev

自己署名証明書をブラウザに例外認識させるために一連の URL を踏んでから、Hubs のフロントにアクセスします

Hubs にアクセス

dev 環境でのとりあえずの使い方

ルームを作る

  • Sign In / Sign Up の機能はまだ上手くうごかせてませんので、部屋を作成する ボタンでランダムなハッシュで部屋を生成し、Join Room を選らんで入ります。
  • アバターの名前は自由に設定できますが英数のみのようです


入っている部屋に招待する
招待用のリンク機能がまだ上手く使えてませんので、とりあえず最初に作って入った部屋の URL を直接他の人の PC やブラウザで入力してください

(例)https://myhubs.example:8080/hub.html?hub_id=rU6ePqt

今はまだ真っ暗な部屋で移動して、マイクで会話したり文字のチャットをしたり、などしか出来ていませんが、マルチプレーヤーの VR という雰囲気だけでも味わっていただけたらと思います!

引き続き、自営サーバーでのシーン作成やアバター作成、それらの部屋でのロード、などにも挑戦して行きますので続編にもご期待下さい!!!
弊社ではメタバースに興味のある xR エンジニアやプログラマ、インフラエンジニアなどを積極募集しておりますので、ご興味ある方はぜひご応募くださいー ⇒ 採用情報

ブログ記事検索

このブログについて

このブログは、札幌市・仙台市の「株式会社インフィニットループ」が運営する技術ブログです。 お仕事で使えるITネタを社員たちが発信します!