JitsiMeetをUbuntu20.04にインストール～動作検証までしてみる

# はじめに
## 対象読者
* [Jitsi Meet](https://meet.jit.si/) をオンプレサーバで使ってみたい方

## Jitsi Meet って？
* オープンソースの WEB 会議システム
    * ひとことで言ってしまえば Zoom みたいなもの
    * 人数制限や時間制限みたいなものがない
    * Zoom や Google Meet などと比べれば、カスタマイズに自由が利きそう
* [GitHub リポジトリ - jitsi / jitsi-meet](https://github.com/jitsi/jitsi-meet)

## 環境
### OS
* Ubuntu 20.04

### スペック
* ConoHa VPS
    * CPU 3 Core
    * メモリ 2 GB
    * SSD 100 GB

## 用途
* 社内用 ( 特定の固定 ip からのみのアクセス ) 会議

## 前提
* サブドメインの用意


# 前準備
## パッケージ更新
```sh
sudo apt update
sudo apt upgrade
```

## Ubuntu 18.04 から 20.04 へアップグレード
* 2020/05/31 時点で ConoHa にはまだ Ubuntu 20.04 イメージが存在していないので
* 以下記事参照
    * [Ubuntu 18.04(LTS)→20.04(LTS)アップグレード方法](https://qiita.com/hitobb/items/2ee9b1c2c49760976e0f)

## swap メモリを追加
* このまま進んでいくとメモリ不足でつらいことになりそうなので……
* 物理メモリ／ swap メモリの確認方法

```sh
free -m
```

```
              total        used        free      shared  buff/cache   available
Mem:           1992         108        1637           5         246        1736
Swap:          2047           0        2047
```

* cd で swapfile 作成するところに移動した後で以下を実行
    * *** G のところが容量。 5GB 追加したければ、 5G と入力

```sh
fallocate -l 5G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
```

* /etc/fstab に下記の1行を追加

```:/etc/fstab
/swapfile none swap sw 0 0
```

* 再起動

```sh
shutdown -r now
```

## nginx + Let's Encrypt
### インストール + 初期設定
* 用意していたサブドメインを入力
* SSL redirect は設定しておいてよし

```sh
apt install certbot python3-certbot-nginx
certbot --nginx
```


### 確認
* 用意していたサブドメインへブラウザアクセスして nginx 初期画面が表示されれば OK

### nginx のチューニング
* 以下記事参照
    * [そこそこなスペックのサーバにおけるnginxチューニングの一例](https://qiita.com/Hi_Noguchi/items/9e099420cc3934c43f00)

```nginx:/etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
  worker_connections 4096;
  use epoll;
}

http {
  charset UTF-8;
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 20;
  client_header_timeout 20;
  client_body_timeout 20;
  send_timeout 20;
  reset_timedout_connection on;
  types_hash_max_size 2048;
  server_tokens off;

  include /etc/nginx/mime.types;
  default_type application/octet-stream;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers on;

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;

  gzip on;
  gzip_vary on;
  gzip_disable "MSIE [1-6]\.";
  gzip_proxied expired no-cache no-store private auth;
  gzip_min_length 1024;
  gzip_comp_level 6;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-enabled/*;
}
```

## UFW
### 設定
* 今回は社内用とするので、許可 ip からのみのアクセスとする

```sh
ufw allow from xxx.xxx.xxx.xxx
```

* 公開するなら以下も指定

```sh
ufw allow 'Nginx Full'
ufw allow in 10000:20000/udp
```

### 反映
```sh
ufw enable
```

### 確認
```sh
ufw status
```

## OpenJDK
### インストール
```sh
apt install openjdk-8-jre-headless
```

# Jitsi Meet
## インストール
### GPG key 設定
```sh
wget https://download.jitsi.org/jitsi-key.gpg.key
apt-key add jitsi-key.gpg.key
rm jitsi-key.gpg.key
```

### リポジトリ追加
* ファイル作成

```sh:/etc/apt/sources.list.d/jitsi-stable.list
deb https://download.jitsi.org stable/
```

### インストール
* インストール中、サブドメインについて訊かれるので用意していたものを入力

```sh
apt update
apt install jitsi-meet
```

* 既に Let's Encrypt で証明書を用意しているので、証明書については I want to use my own certificate を選択
* 鍵の場所は 2 度に分けて訊かれるので、それぞれ以下を指定
    * 結局後で編集するので何入れても良いかも……

```:1回目
/etc/letsencrypt/live/(subdomain_directory_name)/privkey.pem
```

```:2回目
/etc/letsencrypt/live/(subdomain_directory_name)/fullchain.pem
```

### nginx 設定
* このままだとサブドメインの設定が衝突するので以下のように編集
    * サブドメインの箇所は xxxxxxx.xxx としてあるので自身のものを入力

```nginx:/etc/nginx/sites-available/default
server {
  listen 80 ;
  listen [::]:80 ;
  server_name xxxxxxx.xxx;

  if ($host = xxxxxxx.xxx) {
      return 301 https://$host$request_uri;
  } # managed by Certbot

  location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    root         /usr/share/jitsi-meet;
  }
  location = /.well-known/acme-challenge/ {
    return 404;
  }

  return 404; # managed by Certbot
}

server {
  listen [::]:443 ssl ipv6only=on; # managed by Certbot
  listen 443 ssl; # managed by Certbot
  server_name xxxxxxx.xxx; # managed by Certbot

  add_header Strict-Transport-Security "max-age=31536000";

  ssl_certificate /etc/letsencrypt/live/xxxxxxx.xxx/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/xxxxxxx.xxx/privkey.pem; # managed by Certbot
  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

  root /usr/share/jitsi-meet;
  location / {
    try_files $uri $uri/ =404;
  }

  # ssi on with javascript for multidomain variables in config.js
  ssi on;
  ssi_types application/x-javascript application/javascript;

  index index.html index.htm;
  error_page 404 /static/404.html;

  location = /config.js {
    alias /etc/jitsi/meet/xxxxxxx.xxx-config.js;
  }

  location = /external_api.js {
    alias /usr/share/jitsi-meet/libs/external_api.min.js;
  }

  # ensure all static content can always be found first
  location ~ ^/(libs|css|static|images|fonts|lang|sounds|connection_optimization|.well-known)/(.*)$ {
    add_header 'Access-Control-Allow-Origin' '*';
    alias /usr/share/jitsi-meet/$1/$2;
  }

  # BOSH
  location = /http-bind {
    proxy_pass      http://localhost:5280/http-bind;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Host $http_host;
  }

  # xmpp websockets
  location = /xmpp-websocket {
    proxy_pass http://127.0.0.1:5280/xmpp-websocket?prefix=$prefix&$args;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $http_host;
    tcp_nodelay on;
  }

  location ~ ^/([^/?&:'"]+)$ {
    try_files $uri @root_path;
  }

  location @root_path {
    rewrite ^/(.*)$ / break;
  }

  location ~ ^/([^/?&:'"]+)/config.js$ {
    set $subdomain "$1.";
    set $subdir "$1/";

    alias /etc/jitsi/meet/xxxxxxx.xxx-config.js;
  }

  # Anything that didn't match above, and isn't a real file, assume it's a room name and redirect to /
  location ~ ^/([^/?&:'"]+)/(.*)$ {
    set $subdomain "$1.";
    set $subdir "$1/";
    rewrite ^/([^/?&:'"]+)/(.*)$ /$2;
  }

  # BOSH for subdomains
  location ~ ^/([^/?&:'"]+)/http-bind {
    set $subdomain "$1.";
    set $subdir "$1/";
    set $prefix "$1";

    rewrite ^/(.*)$ /http-bind;
  }

  # websockets for subdomains
  location ~ ^/([^/?&:'"]+)/xmpp-websocket {
    set $subdomain "$1.";
    set $subdir "$1/";
    set $prefix "$1";

    rewrite ^/(.*)$ /xmpp-websocket;
  }
}
```

* 以下削除

```sh
rm /etc/nginx/sites-enabled/xxxxxx.xxx.conf
```

### nginx リロード
* 以下実行でエラーが発生しなければ、ブラウザアクセスして Jitsi Meet の画面が表示されるようになっているはず

```sh
nginx -s reload
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/028fb257-7ea1-4eb9-ca23-e353b1888b04.png)


## 設定
### 認証まわり等の prosody コンフィグの場所
* ファイルは指定のサブドメイン名で存在しているので xxxxxx.xxx のところ自身のものに置き換えて読んでください

```sh
/etc/prosody/conf.avail/xxxxxx.xxx.cfg.lua
```

### 匿名ユーザの可否
* anonymous のところを internal_hashed にすると匿名ログインできなくなる
* 社内用途であれば anonymous のままで良さそう

```lua:/etc/prosody/conf.avail/xxxxxx.xxx.cfg.lua
-- 前略

        -- authentication = "anonymous"
        authentication = "internal_hashed"

-- 後略
```

### 基本設定のためのコンフィグの場所
* 以下にある

```
/etc/jitsi/meet/xxxxxx.xxx-config.js
```

* 見た目については以下で、次のような項目について設定できる
    * ウォーターマークの表示
    * デフォルトのユーザー名
    * ツールバーボタン

```
/usr/share/jitsi-meet/interface_config.js
```



# 動作検証
## PC からのアクセス
### 参加者 5 名
* 各参加者の画質／音質はサーバ依存のところでは特に問題なさそう
    * クライアント依存でマイクやスピーカーの品質にはもちろん依存する
    * クライアント側で chrome のメモリ使用量は増えるので、それでカクつくことはある
* 音質は高音域／低音域のカットが働いていて、人の声に特化させられているのが感じられる
* zoom と比較すると、周囲から聞こえてくる雑音カットの処理が弱い
* ベータ機能の背景にブラーをかけるものを適用すると、ものすごくカクつく


### 参加者 9 名
* シークレットウィンドウを使って二窓してみるなどして、疑似的に参加者を増やしてみた
* 参加者 5 名のときと、動作感としては変わらず


### 参加者 11 名
* 参加者 5 or 9 名のときと、クライアント側の動作感としては変わらず


### 参加者 30 名以上
* クライアント側の動作感として、ものすごいカクついてしまう。音声もしばし途切れる
* 基本、参加者のほとんどがビデオ OFF にしておいてもらわないとならなくなる
* たぶん 20 名以上くらいでやろうとすると、もっとスペック上げましょう、ということになると思います


### Prometheus でのリソースモニタ
* 会議時間は 1 時間 30 分 ほど
* 参加者 5 名 → 途中で二窓するなり疑似的に 9 名に増やした
* 参加者みんな画質は HD
* 一時間半ほどでメモリ使用量の遷移は以下のように。参加者を増やしても大して増えず

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/6909319d-32fa-08aa-0523-06f79ff59758.png)

* CPU ロードアベレージ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/e4d6c9a4-db24-4282-fcf5-3e898b4f6ef0.png)

* ネットワーク受信量

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/785d8280-7b85-e841-1bf2-86c8c88eea3b.png)

* ネットワーク送信量

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/ae06011f-04b2-f0cb-200f-487956b62a74.png)

 

## モバイルアプリからのアクセス
* Android 端末 ( Galaxy S10 ) で動作確認
* 大きな問題は感じられない
* バッテリ消費はだいぶ激しくなる
* 何かと PC 版と異なる UI というのは、他の会議サービスでも同様なので多くは言及しない


## 他の検証について
* Prometheus でのリソースモニタについてもっと詳細な検証を別記事で書く予定
    * 参加者 11 or xx 名の場合など
* 音質検証を別記事で書く予定
    * audio 設定はここ /etc/jitsi/meet/xxxxxx.xxx-config.js にある
    * カスタマイズ項目として以下を追記する

```js:/etc/jitsi/meet/xxxxxx.xxx-config.js
    // add
      stereo: true,
      // Acoustic Echo Cancellation
      disableAEC: true,
      // Highpass Filter
      disableHPF: true,
      // Noise Suppression
      disableNS: true,
      // Automatic Gain Control
      disableAGC: false,
```

# おわりに
## 感想
* 思ったよりも問題なく動作し、メモリ使用が少なくて済んでいたのが驚き
* ロードアベレージが最大で 0.35 超になっていたけれど、もう少し人数増えた時どうなるか……？
* 少人数限定の運用なら ConoHa のもう一つ下のプランでも耐えられそうなので、コスパは悪くない
* Zoom を置き換える場合、バーチャル背景がないというのは、結構なデメリットとして挙げられてしまうだろうなあという印象（ブラー背景が今のところは使いものにならないカクつきなので……）
* 音質設定、結構柔軟に変えられそうなので期待しているところ

## 次に
* [JitsiMeetをセキュアに利用するためホスト／ゲストの認証設定](https://qiita.com/Hi_Noguchi/items/3f535e72e1a9fc1d1696)
* Prometheus でのリソースモニタについてもっと詳細な検証を別記事で書く予定
* 音質の検証をおこなってみて、結果を別記事に書く予定

## 関連記事
* [JitsiMeetをUbuntu20.04から完全削除する](https://qiita.com/Hi_Noguchi/items/686744f5190e1715e198)

## 参考
### Jitsi Community より
* [What is the recommend Java version](https://community.jitsi.org/t/what-is-the-recommend-java-version/28955/5)
* [Configuring audio in meet.jit.si](https://community.jitsi.org/t/configuring-audio-in-meet-jit-si/33227)
* [Higher audio quality](https://community.jitsi.org/t/higher-audio-quality/31441/19)

### 他サイトより
* [How To Install Jitsi Meet on Ubuntu 18.04](https://www.digitalocean.com/community/tutorials/how-to-install-jitsi-meet-on-ubuntu-18-04)
* [Enable authentication in your instance of Jitsi Meet](https://jochen.kirstaetter.name/authentication-jitsi-meet/)