riq0h.jp/content/post/かゆいところに手が届くインスタンス運用の初級テクニック集.md
Rikuoh 78f55dd1b0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fix
2023-11-28 19:57:57 +09:00

213 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "かゆいところに手が届くインスタンス運用の初級テクニック集"
date: 2023-07-22T20:47:25+09:00
draft: false
tags: ['tech']
---
ソロインスタンスを建ててそろそろ2週間が経とうとしている。おかげさまで絶好調だ。本稿では僕がMastodonインスタンスの運用を改善していく上で、手頃ながら日本語情報が乏しかった情報について取り上げる。
## SSLの対応をCloudflareに丸投げする
>**前提**
>・運用中のドメインをCloudflareで管理しているか、またはネームサーバを向けている。
>・CloudflareのSSL/TLS設定で暗号化モードを **フル(厳密)** に設定している。
各種の文献ではインスタンスを建てる過程でLet's Encryptを紹介しているものが多い。確かにこの証明書は様々な用途において気安い選択肢だが、反面、3ヶ月に1回の更新作業が地味に面倒だったり、cronで自動化してもなぜかcertbotがコケていたりと微妙な使い勝手の悪さは否めない。
そこで、僕はSSLの対応をCloudflareに丸投げすることを提案したい。多少の面倒を押してひとたび設定してしまえば以降は二度と証明書の顔を見なくて済む最高の選択肢がここにある。まずはCloudflareのページから運用中のドメインを指定して、**SSL/TLS → オリジンサーバ**へと進む。
![](/img/200.png)
次に「証明書を作成」をクリックして画面下の「作成」を押す。設定項目は特にいじらなくても差し支えない。証明書の有効期限はデフォルトで**15年間**となる。西暦2038年7月22日……。その頃には紙より薄いスマホの上にインスタンスが建っていそうだ。
暗号鍵が作成されると画面上に文字列が現れるので、速やかにコピペして指示通りの拡張子で保存する。この画面は再び開けないため、インスタンスを動かしているサーバ上だけでなく安全なローカル環境にも予備を保存しておくのが望ましい。サーバにそれぞれの鍵を保存したら、先にLet's Encryptで発行したSSL証明書を削除する。以降の操作はroot権限で行う。
```zsh
$ certbot revoke --cert-path /etc/letsencrypt/live/あんたのドメイン名/cert.pem
```
実行確認を承諾すると証明書は直ちに失効される。cronを設定している人は`crontab`を実行して自動更新も忘れずに解除しよう。続いて、nginxの設定に進む。手始めに必要なディレクトリを作成してCloudflareの暗号鍵を移動する。
```zsh
$ mkdir /etc/ssl/certs
$ mkdir /etc/ssl/private
$ mv あんたのドメイン名.pem /etc/ssl/certs/あんたのドメイン名.pem
$ mv あんたのドメイン名.key /etc/ssl/private/あんたのドメイン名.key
```
移動したら任意のエディタでnginxの設定ファイルを編集する。
```zsh
$ vim /etc/nginx/sites-available/あんたのドメイン名.conf
ssl_certificate /etc/ssl/certs/あんたのドメイン名.pem;
ssl_certificate_key /etc/ssl/private/あんたのドメイン名.key;
```
編集後、念のために`nginx -t`でエラーを確認して問題がなければ`systemctl restart nginx`でnginxを再起動する。Web UIに接続して証明書の有効性が確認できたら作業は完了だ。
## データベースのバックアップをCloudflareに丸投げする
>**前提**
>・Docker環境でインスタンスを動かしている。
**■2023年11月28日改訂**
以前はバックアップツールにwranglerを採用していたが、転送容量に制限があることからrcloneを利用する形に改めた。事の顛末は[この記事](https://riq0h.jp/2023/11/12/190427/)に記されている。
誉れ高き丸投げシリーズその2。どんどん丸投げしていこう。我々はすでに巨人の肩に乗っているし、どうせ今さら降りることなどできない。Cloudflareのページで**R2 → 概要**と進んでバケットを作成する。バケットの名前はなんでも構わない。ついでに設定から自動削除をスケジュールすると容量の節約になる。
![](/img/201.png)
次にrcloneを導入する。rcloneは各種オンラインストレージにファイルを転送するツールだ。Amazon S3に対応しているため、S3互換を持つCloudflare R2にも使用できる。
```zsh
$ apt install rclone
```
導入後、通常は`rclone config`で対話形式のセットアップウィザードを起動する方法が一般的だが、本稿では`root/.config/rclone/rclone.conf`に前もって設定ファイルをぶち込んでおく。このファイルは自動化の都合上、root以下に保存する必要がある。
```ini
[r2]
type = s3
provider = Cloudflare
access_key_id = あんたのアクセスキー
secret_access_key = あんたのシークレットキー
endpoint = https://あんたのS3API.r2.cloudflarestorage.com
acl = private
```
アクセスキーおよびシークレットキーはR2のトップ画面右側の「R2 API トークンを管理」から発行する。発行直後の一度きりしか表示されないので紛失に注意すること。準備が整い次第、バックアップスクリプトの作成に移る。
```zsh
#!/bin/env bash
set -euo pipefail
# Configurable variables
INSTANCE_DIR="/home/あんたのユーザ名"
BACKUP_DIR="/home/あんたのユーザ名/バックアップファイルの保存場所"
BACKUP_LIFETIME_DAYS=ローカルにファイルを保存したい日数
DATE_FORMAT="%Y%m%d_%H-%M-%S"
DB_CONTAINER="mastodon-db-1"
DB_USER="mastodon_user"
DB_NAME="mastodon_db"
RCLONE_DESTINATION="r2:R2のバケット名"
# Error handling
trap 'echo "😢 An error occurred. Exiting." && exit 1' ERR
# Start backup
echo "🚀 Backup ready..."
cd "$INSTANCE_DIR"
# Database backup
BACKUP_FILE="$BACKUP_DIR/$(date +$DATE_FORMAT).gz"
docker exec $DB_CONTAINER pg_dump -Fc -U $DB_USER $DB_NAME | gzip -c >"$BACKUP_FILE"
echo "✅ Success!"
# Sync backup
echo "🔄 Syncing..."
find "$BACKUP_DIR" -mtime +$BACKUP_LIFETIME_DAYS -name "*.gz" -exec rm -f {} \;
rclone copy "$BACKUP_DIR" $RCLONE_DESTINATION
echo "👍 Mastodone."
```
なお、Mastodon以外のAP実装を運用している人は各自、気の利いたジョークで末尾の内容を書き換えなければならない。義務 上記のスクリプトをcronに登録すると自動化が達成できる。
```zsh
# rootで実行する。
sudo crontab -u root -e
# 毎日午前5時に指定された場所のスクリプトを実行する。別に好きな時間でいい。
0 5 * * * sh /home/ユーザ/ファイル.sh
```
最高だね。面倒なことは全部機械にやらせよう。ただし、cronのやつは油断すると裏切るのでたまにCloudflareのバケットを見に行った方がいいかもしれない。
## Mastodonのリモートメディアを確認して削除する
>**前提**
>・Docker環境でMastodonインスタンスを動かしている。
Web UIのサーバ設定でもリモートメディアを自動削除するようにできるが、具体的に何GBのキャッシュが存在していて何GBぶん減らせたのか判らないところがちょっと物足りない。下記の平易なスクリプトでそれを補える。
```zsh
#!/bin/bash
cd /home/ユーザ/インスタンスのディレクトリ/
echo "Check media usage..."
docker-compose run web bundle exec bin/tootctl media usage
read -p "Enter to proceed..."
echo "Removing..."
docker-compose run web bundle exec bin/tootctl media remove -d 1
echo "Done."
```
この記述例ではメディアの使用量を照会した後に処理の続行を確認して、Enterキーを押すと24時間以前のリモートメディアが削除される。予期せぬ請求やストレージの圧迫を避けるためにもそれなりの頻度で実施しておきたい。
## Mastodonの投稿読み込み数上限を破壊する
>**前提**
>・Docker環境でMastodonインスタンスを動かしている。
Mastodonは投稿の読み込み数に制限がある。おそらく負荷対策だろう。過去の投稿は最大で800までしか読み込めない。いちユーザの立場では変えられないゆえ不便を被っている者も少なくないと思われるが、我々は圧倒的権力を誇る鯖缶だ。いくらでも好きな数字に書き換えられる。
```ruby
# mastodon/app/lib/feed_manager.rb
MAX_ITEMS = 2000
```
さしあたり僕は2000にした。編集後は`sudo docker-compose build`で再ビルドしなければ反映されない。これで深夜帯に蓄積された投稿の一部しか読めないなどという理不尽から解き放たれる。
## Mastodonの画像リサイズ制限を破壊する
>**前提**
>・Docker環境でMastodonインスタンスを動かしている。
Mastodonは画像の最大解像度が1080p相当に抑えられている。多くのユーザを限られたリソースで支える状況下ではやむをえないが、ソロインスタンスの支配者には意味のない制約だ。4K画質相当まで上げてしまおう。
```javascript
// resize_image.js
const MAX_IMAGE_PIXELS = 8847360;
```
```ruby
# media_attachment.rb
IMAGE_STYLES = {
original: {
pixels: 8_847_360,
file_geometry_parser: FastGeometryParser,
}.freeze,
```
こっちも再ビルドを忘れてはならない。もちろん4Kを越える解像度のカメラやディスプレイを持っている人は8Kなどにしてもよい。
## Mastodonの文字数上限を破壊する
>**前提**
>・Docker環境でMastodonインスタンスを動かしている。
500文字もあれば十分と思いきや、ここ一番の時に足りない場合が意外とあったりする。実装系にもよるがだいたいどれも8000文字くらいは受け取れるらしいので不要は制限は予め取り払っておいた方が楽だ。僕は9999文字に設定した。ここでも2つのファイルを編集するが、当該のファイル内を「500」で検索すれば容易に修正箇所を見つけることができる。
```ruby
# mastodon/app/javascript/mastodon/features/compose/components/compose_from.javascript
return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 9999 || (isOnlyWhitespace && !anyMedia));
};
<CharacterCounter max={9999} text={this.getFulltextForCharacterCounting()} />
```
```ruby
# mastodon/app/validators/status_length_validator.rb
class StatusLengthValidator < ActiveModel::Validator
MAX_CHARS = 9999
```
余談だが、最近華々しいリニューアルを果たしたMisskeyフォークの[Firefish](https://joinfirefish.org)は一瞬だけ最大文字数を2億5000万文字に設定できたらしい。いい心意気だ。