PeerTubeでVultrのオブジェクトストレージ使う

# なぜ
私のインスタンスはVultr Cloud Computeで動いているわけですが、Mastodonの方をVultrのオブジェクトストレージに移行したんで
https://qiita.com/atsu1125/items/fc44c2de0ec6ed197669
ついでにPeerTubeも移行してしまおうということです。
これのPleroma編もあります。
https://qiita.com/atsu1125/items/20b7afbccab6bc829fa2

# 環境
うちは以下のサーバーでPeerTubeのアプリケーションを実行しています。
データベースはまた別なサーバーにあります。
通常１台のサーバーだと思いますのでそれ向けの内容で書きます。
この方法はPeerTubeのバージョンが3.4以降でのみ使えます。
Vultr High Frequency Compute Tokyo Region
CPU 1Core / RAM 2GB / NVMe 64GB
Fedora 35 non Docker
PeerTube 3.4.1
そして今回はオブジェクトストレージのURLをそのまま公開することはせず、PeerTubeをホストしているNginxでリバースプロキシを行います。
そうしないとリージョンが遠くて単純に不利だし、転送量課金なのでなるべくキャッシュして帯域利用量を減らすべきなのです。
またオブジェクトストレージを将来的に廃止した場合や移転した場合は、自分のインスタンス上では設定ファイルの書き換えだけでURLを変更できちゃいますが、リモートインスタンスから見ればその画像を投稿した時点のURLを参照し続けるので、そのURLが消滅した時点で404になってしまいます。
しかしリバースプロキシしておけば、URLを変えずにオブジェクトストレージを変更できますし、最悪廃止した場合でも、そのURLでローカルのファイルを読み出すように設定すれば404になること回避可能です。

# VultrのWebからオブジェクトストレージを作成する
Vultrのアカウントにログインしてオブジェクトストレージを追加する。
今はアメリカのニュージャージー州にしか置けない。Labelは適当に。
ReadyになったらCreate Bucketsでバケット作成する。
このバケット名は同一リージョン内で他のユーザのものを含めユニークである（重複しない）必要があるので、
オリジナル性の高い名前（インスタンス名）とかにするといいんじゃないかしら。
バケット作成できたらそのタブは開きっぱなしにして次に。

# オブジェクトストレージのポリシーを設定する
Vultrのオブジェクトストレージはデフォルトだと非公開なので公開できるようにする。
以下のコマンドでまずオブジェクトストレージに繋がるようにする。
s3cmdの使い方については https://www.vultr.com/ja/docs/how-to-use-s3cmd-with-vultr-object-storage を見て。

```
sudo dnf install s3cmd
s3cmd --configure
```
Access Keyはさっき開きっぱなしにしてたページのAccess Keyを入力
Secret KeyはそのページのSecret Keyを入力
Default RegionはEnter
S3 Endpointは`ewr1.vultrobjects.com`を入力
DNS-styleは`%(bucket)s.ewr1.vultrobjects.com`を入力
あとはEnter, Enter, Enter, y+Enter, y+Enterで進む

できたら今度`peertube-media_policy`ってテキストファイルを作成
yourbacketnameは各自の設定したバケット名で置き換え

```php:peertube-media_policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AddPerm",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::yourbacketname/*"
    }
  ]
}
```

保存して、`s3cmd setpolicy peertube-media_policy s3://yourbacketname`を実行

次にCORSポリシーを設定します。
日本語ではオリジン間リソース共有と言いますが、
今回PeerTubeのサーバーとオブジェクトストレージの動画はそれぞれ異なるドメインから提供されますので
これを設定しなければ、セキュリティのために動画が再生されなくなってしまいます。
そのためまず`corsrules.xml`を作成します。
yourdomainは各自のPeerTubeのドメイン名に置き換えてください。

```php:corsrules.xml
<CORSConfiguration>
<CORSRule>
    <ID>Allow WebFont for PeerTube instance</ID>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
</CORSRule>
</CORSConfiguration>
```
これを保存したら以下のコマンドを実行して反映させます。
yourbacketnameは各自の設定したバケット名で置き換えてください。

```
s3cmd setcors corsrules.xml s3://yourbacketname
```

# ネームサーバーでメディア用プロキシのサブドメインのレコードを作成する
オブジェクトストレージのメディアを参照するためのWebサーバーのアドレスをサブドメインとして登録する。
私はGoogle Domainsなのだけど、ここでPeerTubeサーバーと同じアドレスで、s3っていうホスト名のサブドメインのAレコード, AAAAレコードを追加する。s3っていうのはawsで使われる名前ではあるけど、s3互換とか言うし短い名前だからいいんじゃないかしら。
ちなみに全然違うドメインにホストしても構わないからね。
misskey.ioだってs3.arkjp.netでメディア用プロキシをホストしてたりするし

# メディア用プロキシのNginx設定ファイルを作成する
そしたらそのメディア用のプロキシのためのNginxの設定ファイルを書いてくよ
私は/etc/nginx/conf.d/s3.yourdomain.confに設定ファイル書いてるけど
各自の運用に合わせて作成してみて
yourdomainを各自のドメイン名で置き換えるのと
proxy_pass の後のURLはyourbacketnameがバケット名なのでさっき作成したバケット名に置き換える。
`max_size=20g`（20GB）の部分は画像のキャッシュを保存する最大容量なので、Nginxを置くサーバーのストレージに余裕がない場合は減らすことを検討する。`max_size=256m`（256MB）と書くことも可能
また`proxy_cache_path`で保存先を余裕のあるストレージに変えることも可能

```
mkdir /var/cache/nginx/proxy_cache_images
chown -R nginx: /var/cache/nginx/proxy_cache_images
```


```php:s3.yourdomain.conf
server {
  listen 80;
  listen [::]:80;
  server_name s3.yourdomain;
  location /.well-known/acme-challenge/ { allow all; }
  location / { return 301 https://$host$request_uri; }
}

proxy_cache_path /var/cache/nginx/proxy_cache_images levels=1 keys_zone=images:2m max_size=20g inactive=90d;

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name s3.yourdomain;
    ssl_session_cache shared:ssl_session_cache:10m;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;

    location / {
      proxy_ignore_headers set-cookie;
      proxy_hide_header set-cookie;
      proxy_set_header cookie "";
      proxy_hide_header etag;
      resolver 127.0.0.53 valid=30s ipv6=off;
      proxy_pass https://yourbacketname.ewr1.vultrobjects.com$request_uri;
  proxy_buffering on;
  proxy_cache images;
  proxy_cache_valid 200 302 90d;
  proxy_cache_valid any 5m;
  proxy_ignore_headers Cache-Control Expires;
  proxy_cache_lock on;
  add_header X-Cache $upstream_cache_status;
    }
}

```

これを保存したら`sudo nginx -t`で設定ファイル確認して大丈夫ならOK

# メディア用プロキシのサブドメインのSSL証明書を取得する
先ほどs3っていうホスト名のサブドメイン作ったのでSSL証明書が必要ですね。
今回はcertbotのnginxプラグインで一発で取得しちゃいます。
yourdomainは各自のドメインにしてね。
`sudo certbot --nginx -d s3.yourdomain`
それで成功したならば、`sudo nginx -t`で確認して`sudo systemctl reload nginx`で反映

# Nginxのsystemd serviceファイルの編集

Nginxでオブジェクトストレージをプロキシするようにしたのですが、どうやらサーバーを再起動した後にNginxの起動に失敗するようになってしまうケースがあるようです（私がそうだった）  
原因としてはNginxが`network-online.target`のあと`multi-user.target`で起動するのですが、その際にリンクアップしているもののまだインターネットにつながってないことがあるため、Nginxの起動時に`nginx -t`を実行してupstreamに接続できないっていうエラーを出してしまうからです。  
対策として  
```
systemctl edit --full nginx.service
```
でNginxのサービスファイルを開いたら、`[Service]`の中に
```systemd:/etc/systemd/system/nginx.service
Restart=always
RestartSec=5
```
と追記します。  
できたら`systemctl status nginx`でエラーが出ていないことを確認してください。  
この設定ではNginxのプロセスが何らかの原因で落ちてしまった時に5秒待って再起動するようにしています。  
これでインターネットにつながるまで待つことができるので何回目かの再起動にて正常に起動できます。  

# PeerTubeの設定ファイルの編集
PeerTubeユーザーに入ったら`/www/peertube/config/production.yaml`を開いて編集
以下の設定値を反映
yourbacketname, youraccesskey, yoursecretkey, yourdomainは各自の値で置き換え

```python:prod.secret.exs
object_storage:
  enabled: true

  # Without protocol, will default to HTTPS
  endpoint: 'ewr1.vultrobjects.com' # 's3.amazonaws.com' or 's3.fr-par.scw.cloud' for example

  region: 'us-east-1'

  credentials:
    # You can also use AWS_ACCESS_KEY_ID env variable
    access_key_id: 'youraccesskey'
    # You can also use AWS_SECRET_ACCESS_KEY env variable
    secret_access_key: 'yoursecretkey'

  # Maximum amount to upload in one request to object storage
  max_upload_part: 4GB

  streaming_playlists:
    bucket_name: 'yourbacketname'

    # Allows setting all buckets to the same value but with a different prefix
    prefix: 'streaming-playlists/' # Example: 'streaming-playlists:'

    # Base url for object URL generation, scheme and host will be replaced by this URL
    # Useful when you want to use a CDN/external proxy
    base_url: 'https://s3.yourdomain' # Example: 'https://mirror.example.com'

  # Same settings but for webtorrent videos
  videos:
    bucket_name: 'yourbacketname'
    prefix: 'videos/'
    base_url: 'https://s3.yourdomain'

```
問題なければ、sudoユーザーに戻り`sudo systemctl restart peertube`で再起動し設定ファイルを反映
もしエラーが出るなら`sudo journalctl -u peertube -f`などで内容を確認
またブラウザからPeerTube開いてみて、おそらくこの時点では画像が何も出てこないけど、試しに画像をアップロードして、DMの公開範囲で投稿してみるとその画像だけ見えるようになれば、大丈夫

# 既存のメディアファイルをオブジェクトストレージにコピー
今のままだと見えない画像だらけなのでオブジェクトストレージにコピーしたい

```
cd /var/www/peertube/peertube-latest
sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run create-move-video-storage-job -- --to-object-storage --all-videos
```

これでローカルからオブジェクトストレージに移動できます。
転送が終われば多分普段通りにPeerTubeが使えるようになってるはずです。