Mastodonあれこれ

# ■はじめに
これは[フレカフェ＆きりたん丼合同 Advent Calendar 2019 2日目の記事](https://adventar.org/calendars/4470) です。
mastodonの[個人インスタンス](https://kiritan.work)を2年ほど稼働させてきて、なんとなくわかってきた部分があるような気がするので記事にしてみます。
主に以下について書きます。

- サーバのお引越し
- チューニング
- カスタマイズ

# ■サーバお引越し
これまで、AWS→PC(Ubuntu Desktop)→Mac mini→MacBook→PC(Ubuntu Server) と何度かお引越しをしてきたので、どんな風にお引越ししたか書きます。

お引越し元の「データベースアーカイブ」「メディアデータ」「SSL証明書」「.env.production」があれば、概ね引っ越しできるはずです。


## お引越し先の準備
[公式ガイド](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Production-guide.md)等を参考にセットアップします。mastodonのバージョンは引越し元と同じものにしないと災いが起こります。
素の状態での動作確認は、[開発環境](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Development-guide.md)で行っておくと安心でしょう。（画像・動画投稿の動作確認など）

## メンテナンス中画面の準備
お引越し中はMastodonに繋がらないので、利用者向けに「何をしているのか」「いつ終わるのか」をお知らせする画面を用意しておくと親切かもしれません。お引越し元／先両方に用意しました。（どっちのサーバに繋がってるかわかるようにした）

![メンテナンス](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252429/77ffdd77-4844-d16e-07a1-528b1c4c28d6.png)


参考にした記事
[nginxでのメンテナンス画面表示](https://qiita.com/bzy/items/7db21c1d7c28c56fc8a7)


## お引越し元の荷造り
「データベースアーカイブ」「メディアデータ」「SSL証明書」をお引越し先に送るために準備します。

### データベースアーカイブ
データベースのバックアップデータを作成します。当然バックアップした後の変更分は持っていけないため、お引越し直前の戸締まりした後（サービス停止後）に行うと良いでしょう。

こんな感じのコマンドでバックアップできるはずです。
``` /usr/bin/pg_dump -Fc mastodon_production -U username -w > /pathto/backup20191202.dump ```

### メディアデータ
画像や動画が入っているので、そこそこのボリュームがあります。不要なデータ等を予め削除しておいた方が荷物の量が少なくて済みます。（うちのインスタンスで20GB程度あったかも）

古いメディアデータの掃除コマンド（のヘルプ）
```RAILS_ENV=production bundle exec bin/tootctl media help remove```
```RAILS_ENV=production bundle exec bin/tootctl preview_cards help remove```

掃除を終えたら、以下を丸ごとコピーしておけばいいと思います。

``` ~/live/public/system/* ```

引っ越し直前にコピーすると時間がかかりすぎるため、事前に（前日の夜中とか）一旦まるごとコピーしておき、引っ越し直前でrsyncコマンドで差分だけコピーするようにしておけば時短になります。
お引越し先に直接 scopy/rsync すれば楽かも。

S3にメディアを入れていて、これをお引越しするなら全部をダウンロードしないといけないです（多分）。
たしかこの[記事](https://teleporter.hateblo.jp/entry/bulk-download-from-s3)を参考にしてダウンロードしました。ダウンロードするためにたくさんアクセスするため、料金は高くなるかもしれません。

### SSL証明書
クラウドフレア等を使っていれば特になくても大丈夫でした。引越し先で新たに証明書を取得するのでもいいのかもしれません。なんかあんまり覚えてないです。ごめんね〜。

### .env.production
確かそのままコピーして持っていったはずです。キー情報とか記載してあるので必要なはず。

## お引越し先で荷解き
### データベース展開
以下のようなコマンドでお引越し先に展開します。
```pg_restore -C -c -d mastodon_production バックアップファイル名```

### メディアデータ
お引越し元から先へ直接 scopy/rsyncを使ってれば楽かも。

### SSL証明書
いい感じに。

### .env.production
確かそのままコピーでいいはず。

## 住所変更
荷解きが終わったら、住所変更を申請します。具体的には使用しているDNSサーバに登録している（自ドメインの向き先）IPアドレスを変更します。反映までは多少時間がかかるようです。
前述のメンテナンス画面上で「お引越し元／先」がわかるようにしていればわかりやすいかも。

## 動作確認
向き先が変わったら、メンテナンス画面をオフ（もしくは自分だけ接続できるような設定にしておく）して、サービスに接続します。画像や動画投稿など、一通りの動作確認を行ってみたりします。

もし何らかの問題が起きていてすぐに解決しそうになければ、DNSの向き先を元に戻して引っ越しを一時中断、お引越し元を再開するなど考えたほうがいいかもしれません。


## 小まとめ
多分だいたいこんな感じで行けたはずです。何か漏らしてたらごめんなさい。




# ■チューニングのお話
[公式ガイド](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Tuning.md)などを参考にチューニングしました。
インスタンスは登録者200人くらい、サーバはCPU:4コア8スレッド／メモリ:32GB程度の規模で、設定値を参考までに載せておきます。

## データベースサーバ（Postgres）
[PGTune](https://pgtune.leopard.in.ua/#/)を使うと、postgresのパラメータを算出してくれますので、それを自分のサーバの設定に反映するとよいかと思います。


![Postgres](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252429/5750ee2d-166d-ab92-bb62-6234daacb31a.png)


## 各サービスのプロセス数、スレッド数の調整
### Webサービス
同時に接続する利用者が多いならそれなりに確保する感じでしょうか。
執筆時点では４プロセス✕６スレッド、DB接続数(DB_POOL)は指定しなければWEB_CONCURRENCYの値になるらしいですが、なんか心配なので24にしてます。

```mastodon-web.service
<省略>
Environment="WEB_CONCURRENCY=4"
Environment="MAX_THREADS=6"
Environment="DB_POOL=24"
```

### Streamingサービス
デフォルトのままで良さそうです。

### sidekiqサービス
設定がイマイチだとすぐに詰まったりするので、バランスが難しいかもです。１プロセスでスレッド数を増やしすぎても良くないらしく（[sidekiq-wiki オプションの説明](https://github.com/mperham/sidekiq/wiki/Advanced-Options)）、プロセス数を増やしてスレッド数は10までに抑える感じにしてます。
rubyはGVLで１スレッドしか同時に動けないとかなんとか（[マルチスレッド/プロセスまとめ（Ruby編）](https://qiita.com/masashi127/items/b186bbf20b4c9632cc86)）

![sidekiq](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252429/cdc48d7b-9ec2-b09c-f20c-fd44ee68b402.png)


```mastodon-sidekiq-default-1.service
＜省略＞
Environment="DB_POOL=12"
ExecStart=/home/xxxx/.rbenv/shims/bundle exec sidekiq -c 10 -q default
```

DB_POOL値はスレッド数＋１以上が必要と[この記事](https://repl.info/archives/659/)で見たので、＋２してます。

## PgBouncer
データベースへの接続をプールして負荷を軽くしてくれる感じのやつです。公式ガイドでは接続数が100を超えるようなら導入をおすすめしている感じでした。

公式ガイドや導入記事を参考に真似して導入できました。うちでは導入後は接続数が15くらいに抑えられました。

[公式ガイド](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/PgBouncer-guide.md)
[[mastodon] PgBouncerを導入する](https://qiita.com/kxn4t/items/59a593f4e28b00af5e77)

## 開けるファイルの上限を増やす
特に「oo many open files」エラー等が出ていたわけじゃないですが、念の為に設定しました。
OS全体の設定を変えるより、各サービスごとに設定を変えるほうが安全とのことなので、各.service ファイルに以下を追記してあります。

```LimitNOFILE=65536 ```

## unixドメインソケット
１サーバで運営しているなら、サーバ内の各サービス間の通信をTCPで行うよりはunixドメインソケットで行うほうが速いらしいので設定しました。
（勝手なイメージですが、同じ部屋にいる人と会話するのにわざわざ電話で会話するか直接会話するか、みたいな違いがあるのかなと思ってます。会話自体のスピードは変わらないけど、会話が始まるまでの手続きの時間が少なくなる感じ。違ったらごめん）

### PostgresとRedisをunixドメインソケットで接続
この記事を参考にしました。

[Mastodon 保守メモ ](https://qiita.com/kumasun/items/bf4997f181f893130041#%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9%E3%83%81%E3%83%A5%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0)

### PgBouncerもunixドメインソケットで接続
postgresをunixドメインソケットにしたので、こっちも合わせて変更しました。

```pgbouncer.ini
; listen_addr = 127.0.0.1
listen_port = 6432
unix_socket_dir = /var/run/postgresql
```

ローカルホスト接続は無しにして、ポート番号（これは必要っぽい）とディレクトリ名（postgresと同じ）で良いようです。


```.env.production
DB_HOST=/var/run/postgresql
DB_PORT=6432
```

Mastodonの環境設定ファイルも忘れずに変更しましょう。


### Webサービス／Streamingサービスもunixドメインソケットで
公式ガイドには特に記載がなかったのですが（当時）、色々試したらできました。
.serveceファイルに以下の環境変数を追加します（指定するファイルの場所は任意でいいと思います）

```mastodon-web.service
Environment="SOCKET=/tmp/mastodon.web.sock"
```

```mastodon-streaming.service
Environment="SOCKET=/tmp/mastodon.stream.sock"
```

nginxの設定を変更して、"proxy_pass"に先程のunixドメインソケットを指定します。

```example.com.conf
location @proxy {
    proxy_pass http://unix:/tmp/mastodon.web.sock;
}

location /api/v1/streaming {
    proxy_pass http://unix:/tmp/mastodon.stream.sock;
}
```

## 小まとめ
チューニング前に比べて、もっさりしたりsidekiqのキューが詰まったりすることが減ってきました。
現状は比較的スペックのよい物理サーバに乗せているということもあり、うちの規模のインスタンスなら余裕のよっちゃんです。


# ■カスタマイズ
[公式ガイド](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Customizing.md)では、見た目のカスタマイズ、それを「テーマ」として設定する方法、ソースコードへのリンクを設定する方法が記載されてます。

## 見た目のカスタマイズ
見た目のカスタマイズはCSSをいじる感じになるので、お好みに合わせてゴリゴリやってください。
ここではうちのカスタマイズの中からおすすめなものをピックアップして紹介します。

### ファボ★を別の絵文字に

```kiritan.scss
/* ファボを肉球に */
.fa-star:before {
  font-size:large ;
  content: "\f1b0" ;
}
```

使える絵文字は [Font awesome](https://fontawesome.com/icons?d=gallery&m=free)で確認してみてください。うちでは肉球にしています。

### 未収載・非公開トゥートを色分け

```kiritan.scss
/*非公開色分け*/
.status-private {
    background: rgba(180,55,132,0.2) ;
}

/*未収載色分け*/
.status-unlisted {
    background: rgba(36,190,56,0.2);
}
```

こんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252429/1f42528c-b9c6-4c08-dba9-29d7ebe18865.png)

## ユーザ絵文字・お知らせ欄
ユーザ絵文字とは、各アカウントのアイコンを絵文字のように扱えるやつです。:@user_name: でアカウント名を括ると絵文字になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252429/04cf656f-d4c4-2941-28b0-c129b6d6b421.png)

お知らせ欄は、イベント告知などに便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252429/8b938652-a3d6-de28-395e-01556c2f7959.png)

こちらの機能は[best-friends.chat](https://github.com/best-friends/mastodon)に実装されているので、それをマージしてちょちょっと手直ししました。まだ、この機能の実装方法を解説できるレベルにないので紹介だけになります。

## 文字アニメーション
文字をぴょんぴょんさせたりびよんびよんさせたりする機能です。
![ダウンロード.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252429/8debe387-69a0-0330-b040-e2ee19e6c52d.gif)

(((文字)))[[[文字]]]{{{文字}}}等特定のカッコで括るとその部分がアニメーションする感じです。

### CSSでアニメーションを定義する

```yourcustom.scss
@keyframes rubberBand {
  from {
    transform: scale3d(1, 1, 1);
  }
  30% {
    transform: scale3d(1.25, 0.75, 1);
  }
  40% {
    transform: scale3d(0.75, 1.25, 1);
  }
  50% {
    transform: scale3d(1.15, 0.85, 1);
  }
  65% {
    transform: scale3d(0.95, 1.05, 1);
  }
  75% {
    transform: scale3d(1.05, 0.95, 1);
  }
  to {
    transform: scale3d(1, 1, 1);
  }
}

.rubberband{
  display: inline-block;
  animation: rubberBand 1.1s ease infinite;
}
```

びよんびよんはこんな感じ。
class="rubberband"がついた要素がアニメーションするようになります。
後はアニメーションさせたい文字を<span>タグで囲んで、class="rubberband" を設定する感じです。

```/app/javascript/mastodon/actions/importer/normalizer.js

// (((文字))) 3重カッコでくくられた箇所を抜き出し、クラスにrubberbandを設定する
const kiriAminefy = (text) => {
  var tmp_content = text;
  var match_result = tmp_content.match(/(\(\(\([^\)]+\)\)\))|(（（（[^）]+）））)/g);
  if (match_result && match_result.length > 0) {
      for (let p of match_result) {
          var replacement = `<span class="rubberband"><span>${p.slice(3, -3)}</span></span>`;
          tmp_content = tmp_content.replace(p, replacement);
      }
  }

export function normalizeStatus(status, normalOldStatus) {
// ＜省略＞
    normalStatus.contentHtml  = kiriAminefy(emojify(normalStatus.content, emojiMap));
// ＜省略＞

  return normalStatus;
}

```

詳細はソースコードを公開しているので[そちら](https://github.com/kiritan-pop/mastodon) を御覧ください。

## 未収載トゥートボタン／非公開トゥートボタン、簡易トゥートエリア（スマホ用） など

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252429/391ee189-5157-a2a2-e190-23505786e438.png)

公開設定を選ばずに、これらのボタンで未収載／非公開のトゥートをワンアクションで行えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252429/b81a9911-4b9c-90ef-cbd0-80399a5601e4.png)

タイムラインから目を離すことなくトゥートを入力し、トゥートできます。
右利き・左利きどちらでも使いやすいようにトゥートボタンを左右に配置しています。

さて、時間がなくなってきてしまったので、ここらへんのカスタマイズのお話は別の機会に回したいと思います。

## 小まとめ
UIをいじろうとした時に、どういじっていいかわからず、しばらくの間はソースコードを眺めてるばかりでした。
そのうち、React+Reduxという枠組みで（フロント側は）作られているということを知って、ようやく勉強する取っ掛かりができた気がします。
別の機会に書くであろう記事ではその辺を整理しながら書き出せればいいなと思います。

# まとめ
インスタンスを立ち上げて２年とちょっと経ちましたが、ようやく少しずつ弄ったりできるようになってきたかな、と思いました。もともとお勉強のために立ち上げたので、勉強ができてよかったです。