[outdated] Mastodon メンテナンスメモ

備忘録として残しておきます

Mastodon専用ユーザにて、Mastodonのインストールディレクトリにて操作することを前提にしています。
(非Dockerなら`live`ディレクトリ)


# 重要なお知らせ

**2022/1/11時点で他サイトへ移行したため、以後、本記事はメンテナンスされません。**
経緯については[こちら](https://note.com/kumasunorz/n/nf80a6381e97f)、移行後の記事は[こちら](https://zenn.dev/kumasun/articles/7cb0dd98acbace)です。


# 参考資料

他の記事、ウェブサイトへのリンク集

## GitHubリポジトリ

[tootsuite / mastodon](https://github.com/tootsuite/mastodon) 

## 公式ドキュメント

[Mastodon documentation](https://docs.joinmastodon.org/)

## インストール手順

[Installing from source - Mastodon documentation](https://docs.joinmastodon.org/admin/install/)
公式ドキュメント。Ubuntuにインストールするならこの手順に従う。

[Mastodon構築手順・非Docker版](https://qiita.com/kumasun/items/543c62eedeb0f00e5afd)
非公式・手前味噌ドキュメント。RHEL系なら参考になるかも。

## アップデート手順

[Upgrading to a new release - Mastodon documentation](https://docs.joinmastodon.org/admin/upgrading/)
公式ドキュメント。非Docker。

[Mastodonアップデート手順（基本）・非Docker版](https://qiita.com/kumasun/items/f17754e57120f7b13a06)
非公式・手前味噌ドキュメント。内容としてはおおむね重複している。

## バックアップ手順

[Backing up your server - Mastodon documentation](https://docs.joinmastodon.org/admin/backups/)
公式ドキュメント。


## サーバ移行手順

[Migrating to a new machine - Mastodon documentation](https://docs.joinmastodon.org/admin/migrating/)
公式ドキュメント。非Docker。

[Mastodonサーバ移行手順（非Docker）](https://qiita.com/kumasun/items/520838806e4524b229b9)
非公式・手前味噌ドキュメント。内容としてはおおむね重複している。

## スパム対策

[Mastodonにおけるスパム対策](https://qiita.com/kumasun/items/b6e6b69fe2e33b820612)
非公式・手前味噌ドキュメント。内容が肥大化したため、別記事に分離しました。


# サービス保守

## tootctl

v2.5.0より、`tootctl`という保守用コマンドラインツールが追加された。
Rakeタスクでの引数指定方法等への不満から誕生したものである。

既存Rakeタスクは、初期セットアップに使う一部のものを除き、`tootctl`に置き換えられた。

tootctl周りについては、肥大化してしまったため勝手ながら別投稿に移動した。

→ [勝手 Mastodon tootctl リファレンス](https://qiita.com/kumasun/items/870769d7db4d95cde238)

なお、公式ドキュメントもある。
[Using the admin CLI - Mastodon documentation](https://docs.joinmastodon.org/admin/tootctl/)


### tootctl の実体

https://github.com/tootsuite/mastodon/tree/master/lib/mastodon

ここに入っているファイルたち。

## Rakeタスク

主に初期セットアップに使用するコマンドラインツール。
v2.5.0以前は管理タスクも含まれていた。

https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/List-of-Rake-tasks.md

あるいは

```bash
# すべてのRakeタスクを表示
$ RAILS_ENV=production bundle exec rails -T

# Namespace "mastodon" のタスクのみ表示
$ RAILS_ENV=production bundle exec rails -T mastodon
```

で、定義されているタスクが一覧(+簡単な説明文)表示される。
`v2.6.1`で確認したところでは、殆どの管理タスクが`tootctl`に移行されている。

<details><summary>タスク一覧の出力例</summary><div>

```text:mastodon_tasks(v2.6.1)
$ RAILS_ENV=production bundle exec rails -T mastodon
rails mastodon:setup                       # Configure the instance for production use
rails mastodon:stats                       # Report code statistics (KLOCs, etc)
rails mastodon:webpush:generate_vapid_key  # Generate VAPID key
```
</div></details>


### Rakeタスクの実体

https://github.com/tootsuite/mastodon/tree/master/lib/tasks


## tootctlやrakeタスクをcronで動かしたいとき

cronから直接実行すると、環境変数が読み込まれず動作しない模様

たとえば、毎日0時0分に`media remove`を実行する場合は下記のようにする

```text:crontab
0 0 * * * /bin/bash -l -c 'cd ~/live && RAILS_ENV=production bundle exec bin/tootctl media remove >> ~/log/media_remove.log 2>&1'
```

crontabでの時間指定の仕方については、ここでは省かせて頂く（検索するとたくさん出てくる）

これによりtootctl等のコマンドを通常通り実行することができる。

※Docker環境での動かし方について情報求む



## irbを使った操作（tootctlやrakeタスクを介さない操作）

`irb(Interactive Ruby Shell)`を用いれば、Mastodonの環境でRubyのコードを直接実行できる。
いろいろできるということは、それなりに危険を伴うのでオススメはできない。

→ [Mastodon用irbコードメモ](https://qiita.com/kumasun/items/45e7e5811f6f9137dda2)

### irbの起動のしかた

#### 非Docker

```bash
$ pwd
/home/mastodon/live
$ RAILS_ENV=production bundle exec rails console
irb(main):001:0>
```

#### Docker

```bash
$ docker-compose run --rm web rails console
irb(main):001:0>
```

※Docker環境がないため未検証





## トラブルシューティング

不具合かな？と思ったときの切り分け方法など

**まず落ち着いて！**

* **Lv 1: 異常に重たいけど機能している**
何かしらのバックグラウンドタスクでリソースを圧迫している可能性がある。
コンソール接続できる場合は、`top`コマンド等で何がリソースを消費しているか調べる。
※一般的に、アクセス集中やフォローリストインポート、アーカイブ生成などで一時的に重くなることがある。

* **Lv 2: 象バンバン（HTTP 503）が出た**
Mastodon関連サービスでエラーが発生している、もしくは記憶領域を使い果たした可能性がある。
コンソール接続できる場合は、ディスクの空き容量はあるか、Mastodon関連サービスが起動しているか、あるいは後述する「Mastodonサービスのログを見る」で何が原因か突き止める。

* **Lv 3: そもそもつながらない。ブラウザにエラーが出る。**
証明書関連のエラーが出た（信頼できないサーバです、など）場合は、証明書の有効期限をチェックする。
接続タイムアウトの場合は、ネットワークは正常か・サーバが起動しているか・Nginxサービスが起動しているか・ファイアウォール設定は正しいか・DNS設定が正しいか、などを順番にチェックする。


* **番外編: アップデート後に不具合が出た**
ログを確認するとともに、アップデート手順に抜けがないかチェックする。
リリースによっては、追加の作業が必要な場合もあるため。リリースノートを再度チェックする。
ハマりがちなところ：
  * **データベースマイグレーション実行忘れ** … システムが動作不良を起こす。mastodon-web のログに「column not found」などのエラーが出る。
  * **アセットコンパイル忘れ** … 画面の動きがおかしくなったり、画像がリンク切れになったりする。必要に応じてclobberをしてからprecompileを実施する。
  * **一時ディレクトリアクセス権の問題** … 画像のアップロードができなくなり、画面上に500エラーが出る。
   nginxのエラーログに「access denied」といったエラーが出る。エラーが出ているディレクトリの所有者を nginx.confで指定したユーザーにする
  * **サブリソース整合性の問題** … 画面が真っ白になるなど、正常に描画されなくなる。ブラウザのコンソールに`Failed to find a valid digest in the 'integrity' attribute for resource ～` などといった警告が出る。配信経路上でリソース(JavaScript等)の改竄が検出されたことによる読み込み阻止が働いている。たとえばCloudflare等のCDNで「JavaScript等の最適化」といったオプションが有効になっている場合はオフにすることで改善される。

## ログの見かた

非Docker環境です。
Docker環境での情報求む。

### Mastodonサービス

```bash
$ sudo journalctl -r -u mastodon-web
$ sudo journalctl -r -u mastodon-sidekiq
$ sudo journalctl -r -u mastodon-streaming
```

`-r` で新しいものを先に表示
`-u` で表示するモジュールを指定
`-f` も付ければ、継続監視が可能（`tail`みたいに）

### PostgreSQL

```bash
$ sudo journalctl -r -u postgresql
```


### Nginx

```bash
$ sudo tail /var/log/nginx/access.log
$ sudo tail /var/log/nginx/error.log
```
継続して監視したい時は、 `tail -f` が便利


よく使うやつのメモ：

```bash:連合からの投稿の受け口(inbox)を除外して継続監視
$ sudo tail -f /var/log/nginx/access.log | grep -v " /inbox "
```

```bash:Mastodonからのアクセスを除外して継続監視
$ sudo tail -f /var/log/nginx/access.log | grep -v " (Mastodon/"
```

```bash:特定のHTTPステータスコードを返しているものを探す
$ sudo cat /var/log/nginx/access.log | grep " 404 "
```

```bash:Botっぽいものを探す
$ sudo tail -f /var/log/nginx/access.log | grep -E "[Bb]ot"
```

## ディスクの空き容量を確保したいとき

連合経由でとめどなく情報が流れてきて蓄積していくので、何の対策もとっていないと、恐ろしい速度でディスク領域を食いつぶしていきます…

### メディアファイル類のキャッシュ削除

メディアファイル（添付された画像・音声・動画）、プレビューカードのサムネイルなどのキャッシュファイル。
空き容量が逼迫している場合、真っ先に着手すべきはこれらのクリーンアップを行こと。

**メディアファイルキャッシュ**

外部のメディアファイルの削除には、`tootctl`コマンドを用いる。
参考→ [勝手 Mastodon tootctl リファレンス - 外部サーバの古いメディアファイルを消す(remove)](https://qiita.com/kumasun/items/870769d7db4d95cde238#%E5%A4%96%E9%83%A8%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AE%E5%8F%A4%E3%81%84%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E6%B6%88%E3%81%99remove)

外部のメディアファイルは再取得可能で、最悪発信元サーバを見に行けば問題ない。
また、自サーバのメディアファイルについては対象としない。

**孤立メディアファイル**

どこからもリンクされていない、孤立したメディアファイルを削除する。
参考→ [勝手 Mastodon tootctl リファレンス - どこからも参照されていないメディアを削除する (remove-orphans)](https://qiita.com/kumasun/items/870769d7db4d95cde238#%E3%81%A9%E3%81%93%E3%81%8B%E3%82%89%E3%82%82%E5%8F%82%E7%85%A7%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B-remove-orphansv310)

これは、ファイルシステムを走査してどこからも参照されていないメディアファイルを見つけ出し、削除するもの。
※オブジェクトストレージ等、ディスク操作に課金されている環境の場合、十分に検討した上で使用しすること。


**プレビューカード**

参考→ [勝手 Mastodon tootctl リファレンス - 古いプレビューカードを削除する(remove)](https://qiita.com/kumasun/items/870769d7db4d95cde238#%E5%8F%A4%E3%81%84%E3%83%97%E3%83%AC%E3%83%93%E3%83%A5%E3%83%BC%E3%82%AB%E3%83%BC%E3%83%89%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8Bremovev300rc1)

プレビューカードを消したところでリンクの動作に問題が起こるわけではない。
また、一定期間後に再度リンクが貼られた場合、再取得しに行くため、恒久的に生成されなくなるわけではない。


### 投稿キャッシュ

誰からもフォローされていないユーザの古い投稿を削除する。

参考→ [勝手 Mastodon tootctl リファレンス - 過去の投稿を削除する(remove)](https://qiita.com/kumasun/items/870769d7db4d95cde238#%E9%81%8E%E5%8E%BB%E3%81%AE%E6%8A%95%E7%A8%BF%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8Bremovev280rc1)

参考資料のリンク先にも記載があるが、これを実行しただけではディスク使用領域を解放することができない。
`VACUUM FULL`もしくは`pg_repack`を実行すること。

### 存在しないユーザの削除

すでに存在しない外部ユーザを削除する。

参考→ [勝手 Mastodon tootctl リファレンス - 存在しないリモートユーザを削除する(cull)](https://qiita.com/kumasun/items/870769d7db4d95cde238#%E5%AD%98%E5%9C%A8%E3%81%97%E3%81%AA%E3%81%84%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%83%A6%E3%83%BC%E3%82%B6%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8Bcull)

アイコンやヘッダー画像のクリーンアップの意味合いが強いが、あまり効果的ではないように思う。


### メディアファイルをオブジェクトストレージに逃がす

ある程度のコスト増を飲めるのであれば、メディアファイルをオブジェクトストレージに逃がすことで、ローカルの空き容量を大きく確保することができる。

参考1 → [Mastodon docs - Configuring your environment - File storage](https://docs.joinmastodon.org/admin/config/#cdn)
参考2 → [MastodonのメディアファイルをOpenStack Swift互換オブジェクトストレージに移行する](https://qiita.com/neustrashimy/items/e86737534104a7db3843)

オブジェクトストレージの課金システムについては様々なバリエーションがあるので、注意深く選定されたし。



## Ruby Gem を再インストールしたいとき

稀に壊れたりすることがあるようです

```bash
$ bundle pristine
```
thanks @theoria !


あるいは

```bash
$ bundle exec gem uninstall -aIx
$ bundle install
```

引数 `-aIx` で、「全てを確認無しでアンインストール」
その後、通常通りインストールする

`bundler` 経由で操作しないと、グローバル環境のGemに作用する？らしいので注意。



## アセットをゼロから作り直したいとき

```bash
$ RAILS_ENV=production bundle exec rails assets:clobber
$ RAILS_ENV=production bundle exec rails assets:precompile
```

`clobber` で webpackの出力を全消しできる。
長いこと`clobber`をしていないと、古いアセットが残りっぱなしになることがある（らしい）ので、たまにやった方がいいかもしれない。












# パフォーマンスチューニング

（他にやれそうなことがあったら教えてください）

## スワップファイルの作成

低スペックのサーバ、特にメモリが2GB未満のサーバで動作させていると
`OOM Killer`（Out-Of-Memory Killer）によって`bundler`などの重要なプロセスが強制終了されてしまうことがある。

ローカルディスクの空き容量に余裕がある環境であれば、スワップファイルを用意することでスワップメモリとして容量を拡張することができる。

```bash
# 2GBの /swapfile を作成する 
$ sudo dd if=/dev/zero of=/swapfile bs=1M count=2048

# ファイルをスワップ領域としてマークし、マウントする
$ sudo mkswap /swapfile

# スワップを一時的に有効にする
$ sudo swapon

# スワップファイルを永続化する
$ sudo vim /etc/fstab
```

```text:/etc/fstabに追記するやつ
/swapfile                                 swap                    swap    defaults        0 0
```

ただし、スワップファイルはメインメモリに比べると **圧倒的に遅い** ため、できればメモリを2GB以上に拡張することをお勧めする。




## PostgreSQL

### サーバスペックに応じたパラメータを設定する

たとえば、pgTune( http://pgtune.leopard.in.ua/ )を参考に、サーバのスペックに合ったパラメータを設定する。


### 定期的にvacuumを行う

標準機能の`autovacuum`を有効にしたり、cron等で定期的に`vacuumdb`コマンドを実行する。
（環境によっては`autovacuum`が無効になっているので要確認。`postgresql.conf`に設定項目がある。）

それにより、データベースファイルの肥大化を防いだり、多少の速度アップが望めるかもしれない。

```text:cronでvacuumdbを行う場合のcrontabの例
0 4 * * * /usr/bin/vacuumdb -z -U dbuser -d dbname >> ~/log/vacuum.log 2>&1
```

上記例では、毎日4時に権限のある全テーブルに対し、バキュームおよびオプティマイザ用統計情報の計算を行う。
テーブルロックがかからないとはいえ、それなりに負荷がかかることが想定されるので、アクティブな時間を外すとよい。

### `pg_repack` する

通常の`vacuum` を定期的に行っても、テーブルサイズの肥大化を完全に抑えることはできない。
`vacuum full` を実行すれば余計な領域を解放させることが可能だが、処理中はテーブルロックがかかってしまうため、実行中はサービス提供ができない。
それに対する解決策が`pg_repack`である。

これは、最小限のロックで`vacuum full` 相当の操作をすることができるツール。
また、必要に応じてデータの並び替え（`cluster`相当）も行うことが可能。

[pg_repack 公式ドキュメント](https://github.com/reorg/pg_repack/blob/master/doc/pg_repack_jp.rst)

#### `pg_repack`のインストール

事前に必要なモジュールをインストールし、pg_repackのビルドとインストールを行う。

```bash:一般ユーザ作業
$ sudo yum install rpm-build postgresql-devel postgresql-static

$ git clone https://github.com/reorg/pg_repack.git
$ cd pg_repack
$ make
$ sudo make install
```

データベース拡張としてpg_repackを登録する

```bash:postgresqlスーパユーザ作業
$ sudo su - postgres
$ psql -c "CREATE EXTENSION pg_repack" -d mastodon
CREATE EXTENSION
$ 
```

#### `pg_repack`の効果測定

現在のテーブル容量を見る

```bash:postgresqlスーパユーザ作業
$ psql -d mastodon

mastodon# SELECT pg_size_pretty(pg_total_relation_size('statuses'));
 pg_size_pretty 
----------------
 3041 MB
(1 行)
mastodon# \q
```

pg_repackを実行する

```bash:postgresqlスーパユーザ作業
$ pg_repack -d mastodon -t statuses
```

実行後のテーブル容量を見る

```bash:postgresqlスーパユーザ作業
$ psql -d mastodon

mastodon# SELECT pg_size_pretty(pg_total_relation_size('statuses'));
 pg_size_pretty 
----------------
 26 MB
(1 行)
mastodon# \q
```

*マジか*

#### `mastodon`データベース全体に`pg_repack`を実行する

```bash:postgresqlスーパユーザ作業
$ pg_repack mastodon
INFO: repacking table "public.account_aliases"
INFO: repacking table "public.account_conversations"

.
. (中略)
.

INFO: repacking table "public.web_push_subscriptions"
INFO: repacking table "public.web_settings"
$ 
```

`vacuum full`相当の操作を行うものなので、タプルの追加削除が頻繁に行われるテーブルに対して行った方が効果が高い。(逆に、追加削除がほとんど行われていないテーブルに対して実行してもさして効果はない)
定期的に行うよりも、デッドタプルが増えてきたら実行する、くらいの感じがちょうどよいかもしれない。

[PostgreSQLのvacuumについて調べたメモ - vacuumすべきか否かの判断](https://qiita.com/kumasun/items/b3d64b749582b32ad0ff#vacuum%E3%81%99%E3%81%B9%E3%81%8D%E3%81%8B%E5%90%A6%E3%81%8B%E3%81%AE%E5%88%A4%E6%96%AD)



### 接続にSocketを使う

Unix Socketを使うことで、localhost接続よりも高速化が見込めるかもしれない。

```conf:/var/lib/pgsql/data/postgresql.conf
listen_addresses = '' # TCP/IPでの待ち受けが必要ない場合は空白とする

unix_socket_directories = '/var/run/postgresql' # ソケットファイルの場所を指定する
```
デフォルトでは `/tmp` をソケットディレクトリとして使用する。必要に応じて場所を変更する。
設定を変更したら、有効にするためPostgreSQLサービスを再起動する。


続いて、Mastodon側の設定も変更する。

```conf:/home/mastodon/live/.env.production
DB_HOST=/var/run/postgresql
```

設定を変更したら、Mastodonサービス3点セットを再起動する。


### pgBouncer を使う

**PgBouncer とは**

> PgBouncer は、軽量なコネクションプーラーです。
> 通常、クライアントからデータベースに対して大量の接続が発生すると、接続を確立する為の時間のコストや、接続を管理する為のコンピュータリソースが多く必要となります。
> このようなケースにおいて PgBouncer を使用すると、これらのコストを劇的に減少させる事ができます。
> http://postgres.sios.com/modules/newbb/viewtopic.php?forum=1&topic_id=149&post_id=150

コネクションの開閉にはそこそこパワーを使うので、プールしておいて有効に使おうよ、という仕組み。

Mastodon公式ドキュメントにも、pgBouncerに関する記事がある。

[Mastodon documentation - Scaling up your server - Transaction pooling with pgBouncer](https://docs.joinmastodon.org/admin/scaling/#pgbouncer)



## Redis

### Socketを使う

PostgreSQLと同様、Unix Socketを使うことで、localhost接続よりも高速化が見込めるかもしれない。

```conf:/etc/redis.conf
port 0 # TCP/IPでの待ち受けが必要ない場合はゼロとする

unixsocket /var/run/redis/redis.sock
unixsocketperm 777
```

デフォルトではソケットを使用しない設定になっているので、既存の設定値を消すかコメントアウトした上、ソケットの設定を追加する。

設定を変更したら、Redisサービスを再起動する。


続いて、Mastodon側の設定も変更する。

```conf:/home/mastodon/live/.env.production
REDIS_URL=unix:///var/run/redis/redis.sock
#REDIS_HOST=127.0.0.1
#REDIS_PORT=6379
#REDIS_PASSWORD=
```

**ソケットは`REDIS_HOST`でなく`REDIS_URL`に対して設定する点に注意！**

設定を変更したら、Mastodonサービス3点セットを再起動する。



## Mastodon

[Tuning Mastodon](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Tuning.md) (リンク切れ)

### `jemalloc` を使ってメモリ最適化

#### jemallocとは？

> jemalloc は、標準ライブラリで定義されているmalloc, free 等のメモリアロケーション APIの実装である。
> http://zonomasa.hatenablog.com/entry/jemalloc_about

詳しい機能については引用元のWebページを参考にして頂きたいが、わたしのざっくりとした理解では：

* メモリのフラグメンテーションを防止し、有効に使われるようにする
* マルチコア・スレッド環境においてパフォーマンス向上
* メモリ周りの不具合(リーク、オーバーフロー等)の検査ツール付


また、このパッケージについて公式ドキュメントでも紹介されている。
[Tuning Mastodon - Using jemalloc](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Tuning.md#using-jemalloc)

Debian系(Ubuntu等)においては、上記公式ドキュメント通りに設定すればよいが、Fedoraにおいてはパッケージ名等が違うので注意が必要。

* パッケージ名: `jemalloc` (`jemalloc-5.0.1-5.fc28.x86_64` 等)
* インストールパス: `/usr/lib64/libjemalloc.so.2`

#### jemallocの導入

```bash:yum-install
$ sudo yum install jemalloc jemalloc-devel
$ ls /usr/lib64/libjemalloc.so.2 
/usr/lib64/libjemalloc.so.2
```

Mastodonに対して設定する前に、Rubyを`jemalloc`に対応させておく必要がある。
`jemalloc`対応版かどうかを確認し、対応していない場合はRubyの再インストールが必要となる。

確認・アンインストール・インストール手順についての参考資料： [rbenvでインストールしたRubyのバージョンを上げる](https://qiita.com/kumasun/items/176f5c68887b5763e1f6)


#### Mastodonサービス側の設定：jemallocの有効化

`mastodon-web.service` および `mastodon-sidekiq.service` に対し、
jemallocを読み込ませるため `Environment="LD_PRELOAD=/usr/lib64/libjemalloc.so.2"` を追記する。
(下記はwebの例だが、sidekiqも同じように追記する。)

```conf:mastodon-web.service
[Unit]
Description=mastodon-web
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="PORT=3000"
Environment="LD_PRELOAD=/usr/lib64/libjemalloc.so.2"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec puma -C config/puma.rb
ExecReload=/bin/kill -SIGUSR1 $MAINPID
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
```

編集後、更新されたファイルを再読み込みし、webとsidekiqを再起動する。

```bash
$ sudo systemctl daemon-reload
$ sudo systemctl restart mastodon-{web,sidekiq}
```



# あまり知られていないであろう設定項目

おもに `.env.production`にて設定する項目たちです

## セキュア（Authorized Fetch）モード

インライン署名されたアクティビティを拒否し、他サーバが公開・非収載の投稿を取得しようとするときに認証を必須とする。
それにより、ブロックしているサーバが投稿を取得していくことを防ぐことができる。（完全に防ぐことを保証するものではない）

ただし、計算コストが上がり、古いバージョンと互換性がなくなる（たとえばMastodon v3.0以前）、連合リレーに参加できなくなる、など制限が発生する。


### 設定方法

**Step 1:** `.env.production` ファイルに、Authorized Fetchモードを有効にする一文を追記する。

```conf:.env.production
AUTHORIZED_FETCH=true
```

**Step 2:** Mastodonサービスを再起動する。


## 連合制限モード

どのバージョンからなのかは不明だが、ホワイトリストモードが追加された。
これは、 **特定のサーバと対してのみ** やりとりをできるようにするモードである。
v3.2.0より、「連合制限モード（limited federation mode）」と名前が改められた。


### 連合制限モードの特徴

* 許可リストに登録しているサーバ相手なら、普段通りやりとりができる。
* 途中で連合許可モードに切り替えた場合、許可リストに登録していないサーバを一括パージできるコマンドがある(`tootctl domain purge --limited-federation-mode`) → [参考資料](https://qiita.com/kumasun/items/870769d7db4d95cde238#%E5%A4%96%E9%83%A8%E3%82%B5%E3%83%BC%E3%83%90%E3%82%92%E3%83%91%E3%83%BC%E3%82%B8%E3%81%99%E3%82%8Bpurgev261)
* 非ログイン状態では、ほとんどのパブリックページへのアクセスができなくなる。
 * `/about`、`/about/more`、`/pubiuc`、`/@username`(ユーザの個別ページ) 等へのアクセスは、ログイン画面にリダイレクトされる。
 * API経由でも同様。`/api/v1/insances`、`/api/v1/accounts/:id` 等へのアクセスにはログインが必要となる。
* 許可リストにないサーバからでもアカウント名を決め打ちで検索すれば、アカウントの存在を知ることはできる。
* 許可リストにないサーバからの接触（フォロー、リプライ等）は弾かれると思われる。（未検証）


### 設定方法

**Step 1:** `.env.production` ファイルに、連合許可モードを有効にする一文を追記する。

```conf:.env.production
# v3.1.5まで
WHITELIST_MODE=true

# v3.2.0rcから
LIMITED_FEDERATION_MODE=true
```



**Step 2:** Mastodonサービスを再起動する。

**Step 3:** 管理者でログインし、設定画面の [管理] → [既知のサーバー] ページを開き、[連合を許可]より許可リストへ追加を行う。

**Step 4:** 許可リストにないサーバーを切り離す

```bash
$ sudo su - mastodon
$ RAILS_ENV=production bundle exec bin/tootctl domains purge --limited-federation-mode
```




# その他小ネタなど

* ホスティングサービス等を使用せず、独自にサーバを立て、収益を上げるような仕組み（広告、アフィリエイト、寄付など）を設け、自分以外の第三者にも使用できる状態にした場合、各地域の総合通信局に「電気通信事業届出」が必要となります。（未届で行った場合、一応罰則もあるようです）
* 「サイト設定」内に連絡先を記載。メールアドレス、管理者欄を埋める。他のサーバのアカウントなどがあれば併記しておく。サーバに何かあったときに、誰かが知らせてくれるかも。


## 「お知らせ」の時差（JST to UTC）

タイムラインの上に出る「お知らせ」を作成する際、日付時刻の範囲や掲載開始日時を設定できるが
これはJST（日本標準時）でなくUTC（協定世界時）で設定しなければならない。

**UTC・JST間の時差は +9 時間である。**

つまり、日本標準時で昼の12時を指定したい場合は、協定世界時の早朝3時にセットする必要がある。

時差を計算するサイトも多数あるので、それらを使うと楽。

[keisan by CASIO](https://keisan.casio.jp/exec/system/1240186937)

[時差計算](https://www.jisakeisan.com/?t1=jst&t2=utc)



## アーカイブファイルの読み方

Mastodonの「 `設定 > インポート・エクスポート > アーカイブのリクエスト` 」を用いると、自分が投稿した文章や画像をまとめてダウンロードできるようになります。
しかし、内容は`ActivityPub`形式の大きなJSONファイルのため、そのまま読める形にはなっていません。

手前味噌ですが、`ActivityPub`形式の`outbox.json`を読める簡易リーダーを試作しました。
https://gist.github.com/Neustrashimy/51442fe6932c86d2555e18bada0d6c93

使い方は下記投稿を参考にしてください。
https://md.objektiv2.net/@kumasun/103099863276308470


全文検索に対応していないサーバでも、この方法であればブラウザのテキスト検索機能を使用して検索することができます。

## 謎のファイルが `/tmp` を食いつぶす問題

### `/tmp/magick-*` (ImageMagickの一時ファイル)

Mastodon側で画像の縮小・メタ情報削除を行う処理を行うようになっているため、そのような一時ファイルが生成されます。
また、何らかの原因により、一時ファイルが削除されず残ってしまうことがあるようです。

`/tmp` は様々なアプリケーションが一時ファイル置き場として使うので、使い切ってしまうとサーバごと機能不全に陥る可能性があります。


応急処置としては、手動で一時ファイルを削除することで解消できます。

```bash
$ rm /tmp/magick-*
```

再発する場合は、ImageMagickが使用するディスク領域を制限することで解消を図ります。

```bash:ImageMagickのリソース制限値表示（Before）
$ identify -list resource
Resource limits:
  Width: 214.7MP
  Height: 214.7MP
  List length: unlimited
  Area: 2.0403GP
  Memory: 972.867MiB
  Map: 1.90013GiB
  Disk: unlimited
  File: 768
  Thread: 2
  Throttle: 0
  Time: unlimited
```

`Disk: unlimited` になっているため、限界まで使用しようとします。これを制限します。

```bash
$ sudo vim /etc/ImageMagick-6/policy.xml
```

```xml:policy.xml
<policymap>
  <policy domain="resource" name="disk" value="256MB"/>
</policymap>
```

`<policymap>` 以下にコメントアウトされている設定値が羅列してありますので、該当部分をコメントアウトして設定し、保存します


```bash:ImageMagickのリソース制限値表示（After）
$ identify -list resource
Resource limits:
  Width: 214.7MP
  Height: 214.7MP
  List length: unlimited
  Area: 2.0403GP
  Memory: 972.867MiB
  Map: 1.90013GiB
  Disk: 256000000B
  File: 768
  Thread: 2
  Throttle: 0
  Time: unlimited
```

Disk値が制限されました。

とりあえず、これで様子を見てみます。



### Paperclipの一時ファイル(v3.1.4)

```bash
$ ll /tmp
total 138992
-rw------- 1 mastodon mastodon   388526 May 21 13:21 8d777f385d3dfec8815d20f7496026dc20200521-41299-100pphy
-rw------- 1 mastodon mastodon   201603 May 21 13:21 8d777f385d3dfec8815d20f7496026dc20200521-41299-102g7ck
-rw------- 1 mastodon mastodon   106475 May 21 13:21 8d777f385d3dfec8815d20f7496026dc20200521-41299-1052yfm
-rw------- 1 mastodon mastodon    41792 May 21 13:21 8d777f385d3dfec8815d20f7496026dc20200521-41299-10g2myt
-rw------- 1 mastodon mastodon    54901 May 21 13:21 8d777f385d3dfec8815d20f7496026dc20200521-41299-10indac
-rw------- 1 mastodon mastodon    26021 May 21 13:21 8d777f385d3dfec8815d20f7496026dc20200521-41299-10rv5ny
-rw------- 1 mastodon mastodon   416685 May 21 13:21 8d777f385d3dfec8815d20f7496026dc20200521-41299-10ytz90
-rw------- 1 mastodon mastodon    23994 May 21 13:21 8d777f385d3dfec8815d20f7496026dc20200521-41299-116kyt4
-rw------- 1 mastodon mastodon     9726 May 21 13:21 8d777f385d3dfec8815d20f7496026dc20200521-41299-11d09gv
```

* 先頭の文字列(`8d777f385d3dfec8815d20f7496026dc20200521`)はSidekiqのJID
* 数字(`41299`)は、`mastodon-sidekiq`のプロセスID
* 末尾の文字列(`11d09gv`等)はSidekiqのTID

であり、Paperclip(ファイル等を操作するライブラリ)によって生成された一時ファイルだろうと思われる。

Sidekiqの管理画面上で該当するジョブが走っていなければ、おそらく消し忘れなので、手動で消しても差し支えないように思う。




## PostgreSQL照合順序によるインデックスの不整合

* [Mastodon公式サイトの情報](https://docs.joinmastodon.org/admin/troubleshooting/index-corruption/)
* [PostgreSQL公式サイトの情報](https://wiki.postgresql.org/wiki/Locale_data_changes)

2018年頃、PostgreSQLデータベースの照合順序として使われている`glibc`のロケールデータが更新された。
その結果、本来一意になるはずのインデックスの重複判定がうまくされないことによって、データが重複してしまう可能性がある。

`glibc 2.2.8` 以前でインデックスを作り、それ以降のバージョンでインデックスの再作成をしていない場合、影響を受ける可能性がある。

```bash:参考：glibcモジュールのバージョン確認
# rpmコマンドで
$ rpm -q glibc
glibc-2.31-4.fc32.x86_64

# またはdnf、yumなどのパッケージマネージャで
$ dnf list installed | grep glibc
glibc.x86_64                                2.31-4.fc32                      @updates               
glibc-common.x86_64                         2.31-4.fc32                      @updates               
glibc-devel.x86_64                          2.31-4.fc32                      @updates               
glibc-headers.x86_64                        2.31-4.fc32                      @updates               
glibc-langpack-en.x86_64                    2.31-4.fc32                      @updates      
```



### 自分の環境が影響を受けているのかチェック

Mastodonデータベースの照合順序をチェックし、影響を受ける可能性があるか見る。
ディストリビューションによって影響を受ける照合順序が異なるようなので、各自 [PostgreSQL公式サイトの情報](https://wiki.postgresql.org/wiki/Locale_data_changes) を参照し、判断を行うこと。

```bash:参考：データベースの照合順序を調べる。Postgresユーザ作業
$ psql -c "SELECT datcollate FROM pg_database WHERE datname=current_database()" -d mastodon
 datcollate  
-------------
 en_US.UTF-8
(1 row)
```

以下の情報は、PostgreSQL公式Wikiの情報を和訳したものです。（20201230版）
最新ではなく、誤訳の可能性があるので、できれば [ソース](https://wiki.postgresql.org/wiki/Locale_data_changes#What_Linux_distributions_are_affected) を読んでください。


**Debian**

Version 8 (jessie) と 9 (stretch)は古いロケールデータを使っています。それらのリリースでは互換性のない変更は確認されていません。Version 8 から 9 へのアップグレードは安全です。
Version 10 (buster) は新しいロケールデータを使っています。そのため、アップグレード時に注意が必要です。

以下の情報も参照してください：
https://lists.debian.org/debian-glibc/2019/03/msg00030.html


**Ubuntu**

Ubuntuは Version 18.04 (bionic) まで古いロケールデータを使っています。
新しい glibc 2.28 ロケールデータは (LTS版でない) Version 18.10 (cosmic) で登場しました。
bionicもしくはそれ以前から、cosmicより新しいバージョンへのアップグレードを行う際は、緩和手段が必要です。

**RHEL/CentOS**

バージョン 6 と 7 は古いロケールデータを使っています。バージョン 6 から 7 へのアップグレードは安全ですが、 `de_DE.UTF-8` ロケールが使用されていた場合は別です。（その他のロケール、および`de_DE.UTF-8`を除く`de_*.UTF-8`ロケール、たとえば `de_AT` などは安全です。）
Version 8 は新しいロケールデータを使っています。そのため、アップグレードする際は注意が必要です。


### 実際にインデックスが壊れているのかチェック

Mastodon公式サイトでは、PostgreSQLの`amcheck`拡張によるチェック方法が載っている。
`amcheck`拡張は`postgresql-contrib`パッケージに含まれているので、事前にそれをインストールしておく。

amcheck拡張について、詳しくは下記ページを参照されたし。
[PostgreSQL 10.5文書 - 付録F 追加で提供されるモジュール - F.2. amcheck](https://www.postgresql.jp/document/10/html/amcheck.html)


```bash:一般ユーザ作業
$ sudo dnf install postgresql-contrib
```

```bash:amcheck拡張を登録し、psqlを起動する。Postgresユーザ作業
$ psql -c "CREATE EXTENSION IF NOT EXISTS amcheck" -d mastodon
CREATE EXTENSION

$ psql -d mastodon
psql (12.4)
Type "help" for help.
```

下記SQLは、どのリレーションが影響を受けたのか分かりやすくするため`c.relname`を表示するようにしているが、それ以外はMastodon公式情報と全く同じ。

```sql:Postgresユーザ作業(psql)
SELECT c.relname, bt_index_check(c.oid)
FROM pg_index i
JOIN pg_class c ON i.indexrelid = c.oid
WHERE c.relname IN ('index_account_domain_blocks_on_account_id_and_domain',
  'index_account_proofs_on_account_and_provider_and_username',
  'index_accounts_on_username_and_domain_lower', 'index_accounts_on_uri',
  'index_accounts_on_url', 'index_conversations_on_uri',
  'index_custom_emoji_categories_on_name',
  'index_custom_emojis_on_shortcode_and_domain',
  'index_devices_on_access_token_id', 'index_domain_allows_on_domain',
  'index_domain_blocks_on_domain', 'index_email_domain_blocks_on_domain',
  'index_invites_on_code', 'index_markers_on_user_id_and_timeline',
  'index_media_attachments_on_shortcode', 'index_preview_cards_on_url',
  'index_statuses_on_uri', 'index_tags_on_name_lower',
  'index_tombstones_on_uri', 'index_unavailable_domains_on_domain',
  'index_users_on_email', 'index_webauthn_credentials_on_external_id'
);
                           relname                          | bt_index_check 
-----------------------------------------------------------+----------------
 index_accounts_on_username_and_domain_lower               | 
 index_users_on_email                                      | 
 index_account_domain_blocks_on_account_id_and_domain      | 
 index_account_proofs_on_account_and_provider_and_username | 
 index_devices_on_access_token_id                          | 
 index_invites_on_code                                     | 
 index_conversations_on_uri                                | 
 index_custom_emojis_on_shortcode_and_domain               | 
 index_accounts_on_uri                                     | 
 index_accounts_on_url                                     | 
 index_custom_emoji_categories_on_name                     | 
 index_domain_allows_on_domain                             | 
 index_domain_blocks_on_domain                             | 
 index_unavailable_domains_on_domain                       | 
 index_markers_on_user_id_and_timeline                     | 
 index_email_domain_blocks_on_domain                       | 
 index_media_attachments_on_shortcode                      | 
 index_preview_cards_on_url                                | 
 index_tombstones_on_uri                                   | 
 index_statuses_on_uri                                     | 
 index_tags_on_name_lower                                  | 
 index_webauthn_credentials_on_external_id                 | 
(22 rows)
```

`bt_index_check`の列が空白であれば影響を受けている可能性は小さい。
逆に何かしらメッセージが出た場合は影響を受けている可能性がある。

メッセージが出なかった場合、さらに深く（親子関係も含めた）チェックを行うことが推奨されている。
このチェックは`ShareLock`を要求するため、実行中はデータベースへの書き込みができなくなるので注意。

```sql:Postgresユーザ作業(psql)
SELECT c.relname, bt_index_parent_check(c.oid)
FROM pg_index i
JOIN pg_class c ON i.indexrelid = c.oid
WHERE c.relname IN ('index_account_domain_blocks_on_account_id_and_domain',
  'index_account_proofs_on_account_and_provider_and_username',
  'index_accounts_on_username_and_domain_lower', 'index_accounts_on_uri',
  'index_accounts_on_url', 'index_conversations_on_uri',
  'index_custom_emoji_categories_on_name',
  'index_custom_emojis_on_shortcode_and_domain',
  'index_devices_on_access_token_id', 'index_domain_allows_on_domain',
  'index_domain_blocks_on_domain', 'index_email_domain_blocks_on_domain',
  'index_invites_on_code', 'index_markers_on_user_id_and_timeline',
  'index_media_attachments_on_shortcode', 'index_preview_cards_on_url',
  'index_statuses_on_uri', 'index_tags_on_name_lower',
  'index_tombstones_on_uri', 'index_unavailable_domains_on_domain',
  'index_users_on_email', 'index_webauthn_credentials_on_external_id'
);
                           relname                          | bt_index_check 
-----------------------------------------------------------+----------------
 index_accounts_on_username_and_domain_lower               | 
 index_users_on_email                                      | 
 index_account_domain_blocks_on_account_id_and_domain      | 
 index_account_proofs_on_account_and_provider_and_username | 
 index_devices_on_access_token_id                          | 
 index_invites_on_code                                     | 
 index_conversations_on_uri                                | 
 index_custom_emojis_on_shortcode_and_domain               | 
 index_accounts_on_uri                                     | 
 index_accounts_on_url                                     | 
 index_custom_emoji_categories_on_name                     | 
 index_domain_allows_on_domain                             | 
 index_domain_blocks_on_domain                             | 
 index_unavailable_domains_on_domain                       | 
 index_markers_on_user_id_and_timeline                     | 
 index_email_domain_blocks_on_domain                       | 
 index_media_attachments_on_shortcode                      | 
 index_preview_cards_on_url                                | 
 index_tombstones_on_uri                                   | 
 index_statuses_on_uri                                     | 
 index_tags_on_name_lower                                  | 
 index_webauthn_credentials_on_external_id                 | 
(22 rows)
```

`bt_index_parent_check`の列が空白であれば問題ないため、`db:migrate`時に出るメッセージを無視することができる。


### 影響を受けてしまっているようですが、どうすればいいですか？

修正するためのタスクが用意されているので安心してください！

```
RAILS_ENV=production tootctl maintenance fix-duplicates
```

このコマンドを実行することにより、各テーブルのインデックスを一時解除し、重複データの確認と削除を行ったのち、再度インデックスを張り直すという一連の操作を行うことができる。

手前味噌ですが以下の記事にも少し情報があるので参考にされたい。
[勝手 Mastodon tootctl リファレンス - メンテナンスタスク系\重複データをマージ・削除する](https://qiita.com/kumasun/items/870769d7db4d95cde238#%E9%87%8D%E8%A4%87%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E3%83%9E%E3%83%BC%E3%82%B8%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8Bfix-duplicates-v322)


### この問題を回避するには？

`libc`がアップデートされた際、ただちにインデックスの再作成を行うこと。



## Mastodonサーバの閉じ方

`tootctl self-distruct`というコマンドができています。

参考 → [勝手 tootctl リファレンス - サーバを連合から切り離す(self-destruct)](https://qiita.com/kumasun/items/870769d7db4d95cde238#%E3%82%B5%E3%83%BC%E3%83%90%E3%82%92%E9%80%A3%E5%90%88%E3%81%8B%E3%82%89%E5%88%87%E3%82%8A%E9%9B%A2%E3%81%99self-destructv280rc1)

これを実行し、sidekiqキューを流しきった後、サーバを閉じるとよいかと思います。



<details><summary>無条件に「410 Gone」を返すことについて</summary>
過去記事（削除済み）にて「無条件に`410 Gone`を返すといいのではないか」という提言をしましたが、それだけではあまり意味がありませんでした。
繋がっていたサーバ側で`tootctl accounts cull`を実行した際、`410 Gone`が返ってこればそのアカウントは削除されたものとされ、繋がっていたサーバからも消されるはずです。
結果的に「そのサーバに属しているユーザがゼロ名」になることによって、投稿等の配信が行われなくなるのでは、という推測に基づいたものでした。
閉じたサーバが`410 Gone`を返していたとしても、繋がっているサーバが`tootctl account cull`もしくは`tootctl domains purge`をしない限り残り続けてしまい、
いつかサーバそのものが消え、名前が引けなくなったりタイムアウトを起こしたりした際に、またSidekiqの再試行キューに上がってきてしまいます。
</details>





# あとがき

* 自分用にメモした内容です。必要に応じて読み替えてください。
* 使用は自己責任でお願いします。何か不具合が起こっても責任取れません。
* こうしたほうがいいよ的なアドバイス、誤記や記載漏れ、動作の違い等の指摘を頂けると大変助かります。
* 連絡先： [Mastodon1](https://nagoyadon.jp/@kumasun), [Mastodon2](https://md.objektiv2.net/@kumasun)




以上