まえがき
この記事は Fediverse Advent Calendar 2021 第2会場) の1日目の記事です。本日が初日ということでこちらに寄稿させていただきます。前半がMastodonアプリケーション周り・後半がインフラ周りの内容です。
私は現在大学生をしています。文系泣かせであるいわゆる理系の経済学が専門となります。趣味でMastodonなどFediverseのソフトの開発と運用をしています。どちらかと言えば運用の方が好きです。コードはシェルスクリプトとPythonを少し書けるくらいです。今回はatsuchan.pageの運用についてです。一般的な「おひとりさま建ててみた!」とは異なり技術的な内容強めになります。
Plumeとは(簡単に)
ここのサイトはPlumeというActivityPub互換のソフトウェアを使用したブログインスタンスとなっております。もしかしたらご存じの方もいらっしゃるかもしれません。
同じActivityPubのブログソフトウェアとしてwritefreelyとかLemmyというものもありますが、Plumeが一番使いやすかったのでここに落ち着きました。
Mastodon(主にソフトウェア周り)
さてPlumeの紹介はこれくらいにして…本題のMastodonのお話に。
Mastodonが何かはここにいる方には説明するまでもないでしょう。ActicityPubをしゃべれるソフトウェアの中ではおそらく一番多く用いられています。
私は2020年6月頃からMastodonを利用しています。最初に使ってたのが板橋丼です。小規模なインスタンスではありますが歴史は長いのと鯖缶が有名なのでそれなりには知られているかもしれません。ここのインスタンス結構おもしろい独自機能もあって使いやすくて今でもお気に入りなのですが、どこのインスタンスがよいという話は別の方に書いていただきたい。
私はその後もいろんなインスタンスにアカウントを作って徐々にMastodon…Fediverseについて知っていくのですが、なかなか定住先が決まらない問題に遭遇しました。どこのインスタンスもそれぞれいい点があって、特に独自機能についてはかなりこだわりがあったので、板橋丼のこの機能が欲しいけど小田急丼のこの機能も欲しいしFedibirdのこの機能も欲しいという事態なってしまいました。
鯖缶に機能の追加をお願いするのも一つでしょうが、そもそも改造機能の取り込みも単にcherry-pickをすればいいという話でもないので非常に手間がかかり、それは現実的ではありません。そのためすでに多くの改造機能が取り込まれたMastodon Glitch Editionをフォークして、独自に開発をしていくことにしました。1
現在公開している私のMastodonのリポジトリ(atsu1125/mastodon)にどのような機能が追加されているかは記載されておりますが、いろいろと追加したものですから私も全ては把握しておりません。 便利だなと感じているものいくつかピックアップします。
一つは全文検索で全部検索できるようにしています。2これは小田急丼のリポジトリを参考にしてちょっと弄りました。特定の事柄について気になることがあれば、Google検索をする感覚でFediverseの世界から情報を収集することができます。もちろん自分のインスタンスに流入してこないトゥートは検索できませんが、現在は3つのリレーサーバー3に接続しているため、あらかたトゥートを拾ってくることができています。
他にはcat featureがあります。これも小田急丼からのcherry-pickですが、Misskeyインスタンスから見た場合でも猫になることができるので、すごくおもしろいと思っています。
あとはMarkdown, HTML形式での投稿を可能としています。これはMastodon Glitch Editionからの機能ですが、私がMarkdown形式で書きたいことが多いので気に入っています。これに対応していない他のMastodonインスタンスから見ると通常の投稿と同じようになってはしまいますが、Pleromaなどは対応してくれているので、便利に使っております。
Mastodon(主にインフラ周り)
さて先ほど書いてきたような機能を整備しても運用するインスタンスがなければ使用することができません。実はMastodonのコードに手を入れる前からインスタンスは持っていました。ただ実際のところバニラの(何もソースコードを改変しない)状態でインスタンスを運用するメリットなど普通はないように感じていて…私もあまり活用していませんでしたから、使っていくものとしました。
どのように運用していったかですが、ざっくりとは沿革としてまとめたものを見ていただくと分かります。
最初はさくらのVPS(RAM 1GB, SSD50GB)で月額880円というプランで運用していました。「え、これで動くの?」という感じですが、当初連合してませんでしたし、その後も制限付きの連合をしていたため十分でした。ただやはり連合を本格的に始めて関わるユーザーが増えていくとかなり重くなっていきましたので、途中で何度か変更したのちに現在の構成になっています。
かなり見にくい図となってしまいました。
言葉でも説明しておくと、
- DNSラウンドロビンという負荷分散技術を用いて、2台のNginxにアクセスを振り分けてます。
- Nginxでどちらか最適なMastodonアプリケーションに処理を受け渡します。「最適な」というのは最低限ダウンしていないこと、そして処理が遅くならないということです。
- メディアアクセス(/system)の場合はメディア用オブジェクトストレージにプロキシします。 詳細
- データベース(PostgreSQLとRedis)はデータベース専用マシンにPgBouncerを用いて接続します。同じPrivate Networkならそのまま暗号化はせず、別リージョンからの接続はSSL暗号化もしくはSSHトンネリングを施しています。
- PostgreSQLはWAL-Gを用いてバックアップ用オブジェクトストレージに常にバックアップしています。 詳細
- 状態監視にはBetter Uptimeで /health のhttpステータスを監視しています。
- CDNはSPOFになるので使用してません。
- メールサーバーは設置せずMastodonからのお知らせ送信はMailjet、管理上のメール送受信はMicrosoft Exchangeで行っています。
- その他メンテナンス用のシェルスクリプトがいくつか定期実行されます。
- Squidを経由して通信しているのは外向きの通信に制限をかけるためです。
2つ目の詳しい説明は別記事としてQiitaにでも書いておきたいボリュームですが、例えばNginxの設定ファイルのupstreamの箇所にこんなものを書いてます。
upstream backend {
server 127.0.0.1:3000 fail_timeout=0;
server 127.0.0.1:2999 fail_timeout=10 backup;
}
この場合127.0.0.1の3000番は同じサーバーのMastodonに、2999番はSSHポートフォワードで接続しているもう一方のサーバーのMastodonの3000番につながっています。Mastodonの仕様としてlocalhostからしか接続を受け付けないのでちょっと工夫が必要でした。ひとまずこれで普段は同じサーバー内でプロキシを行い、ダウンしてる時はbackupの方にプロキシするようになりました。ちなみにアプリケーションサーバーとデータベースサーバーが離れていると10秒とかのレベルですごく遅延しますので、APIリクエストは常にデータベースサーバーに近い方に振り分けます。
9つ目のメンテナンスのためのシェルスクリプトというのはいくつかありますが一つはこういうもの4です。これをsystemdのtimerとして登録しております。SidekiqのSizeを監視させていて溜まってくるとメンテナンス用のアカウントでトゥートさせます。このアカウントをフォローして投稿通知をオンにしておくことで、お知らせとして受け取ることができます。ただ一度お知らせを発出したあとも条件を満たす限り連投されてしまうので、解消するまでは黙るようにしています。そもそも処理が詰まってるのにトゥートさせるのはどうなのかとは思いますが、まあいいでしょう。
以前はオブジェクトストレージを使用してなかったのですが、Mastodonアプリケーションサーバーを冗長化する際にメディアファイルをどうするかという問題にぶつかりました。最初はlsyncdを使ってほぼリアルタイムで/systemを同期していましたがタイムラインの更新に間に合わず、再読み込みするまで画像が表示されなかったので、その方法はなしとなりました。そしてサーバーのストレージではメディア容量を削減する必要があったので残容量に応じて自動で削除させていました。
最後に
この現在の構成でだいたい月額4000円程度となっております。
おひとりインスタンスとしては高いですが、安全に運用するにはこのくらい必要経費かなと思っています。実は私はこれ以外にもいくつかインスタンス5を持っているので、そちらの運営費を加味すると結構になりますが。こういう活発に生きているサーバーを管理する機会なんてなかなかありませんから、その勉強代と思うだけでも結構ありがたいものです。私自身楽しくてやっているところも大きいのでこれからも末長く続けられたらなと思っています。これからもよろしくお願いします。
ちなみに…登録開放してるインスタンスはMT湘南ひらつかですが、こちらはインフラ周りがまるで異なり、ソースコードは私のatsu1125/mastodonと同じものです。性能限界もあり積極的に勧誘を行なっておりませんが登録は自由です。
よくある質問
Q. なんでおひとりインスタンス持ってるの?
A. 私のソースコード・Dockerイメージでホストしてくれる人がいれば頼んだかもしれない、どちらにせよ自分でインフラの経験は積みたかったから。
Q. なんでおひとりさまなのに冗長化してるん?
A. ごくたまにネットワーク障害とかで切断されることがあったので対策した。
Q. なんでおひとりさまなのにオブジェクトストレージ契約してるの?
A. 冗長化の際に画像の置き場をサーバー外部にする必要があったのと、サーバーの容量もカツカツになっていたから。
Q. データベースのレプリケーションはしないの?
A. 外向きの通信量が多いこと(1日20GBとか)と実際復旧速度が多少速くなるくらいで普段の性能に貢献しない(むしろ低下するのでは)のでバックアップで妥協。
Q. ぶっちゃけ安定してるの?
A. 過去3ヶ月で 99.957% uptime です。
Q. データベース飛ばしたことないの?
A. 少なくともMastodonではない、実験用のインスタンスはバックアップシステム構築してないのでよくやる。
Q. どうやって勉強したの?
A. Google検索とMastodonの検索機能とのえるさんとめいめいさんに聞いた。
Q. 開放してるインスタンスの方の構成教えてくれないの?
A. ある意味特殊な環境で動作しており、現状説明の準備が整ってないのでごめんね、重すぎて使えないということはないよ。
Q. 鯖缶なんだから女装写真上げてくれないの?
A. 最近モチベーションがなくて限界男子大学生の格好してます。
注釈
実際にglitch-socのプロジェクトに翻訳のコントリビュートは行いました。稚拙な日本語で申し訳ないですが、、
他のインスタンスでもできるのではないかと思われるかもしれませんが、現状まともに検索できるのが、私が知る限りではうちとmisskey.io, meisskey.oneくらいでした。Mastodonのソースコードそのままだと日本語検索がまともに機能しないですし、Sudachiを使う方法でも検索結果に漏れが出てきてしまうように感じていました。Mastodonでまともに検索できるインスタンスがあれば教えていただきたいです。うちの検索機能の元になってる小田急丼・板橋丼では公開範囲に制限を設けているので若干情報量が減る印象ですが、概ね検索できています。
#!/bin/bash
cd /home/mastodon/live
RAILS_ENV=production bin/rails r "require 'sidekiq/monitor'; Sidekiq::Monitor::Status.new.display('queues')" > public/system/queues.txt
date '+%Y/%m/%d %T' >> public/system/queues.txt
DEFAULT=`cat /home/mastodon/live/public/system/queues.txt | grep default | awk '{print $2}'`
PULL=`cat /home/mastodon/live/public/system/queues.txt | grep pull | awk '{print $2}'`
PUSH=`cat /home/mastodon/live/public/system/queues.txt | grep push | awk '{print $2}'`
SCHEDULER=`cat /home/mastodon/live/public/system/queues.txt | grep scheduler | awk '{print $2}'`
size=25
access_token=""
instance_domain=atsuchan.page
visibility=public
hashtag1=#bot
hashtag2=#atsuchan_page
if ((${DEFAULT} > ${size})) && [ ! -e public/system/count.txt ]; then
echo "${DEFAULT}のため警告をトゥート"
curl -X POST \
-d "status=ご注意ください。mastodon-sidekiqのDEFAULTは${DEFAULT}件、PUSHは${PUSH}件、PULLは${PULL}件、SCHEDULERは${SCHEDULER}件溜まっています。 ${hashtag1} ${hashtag2}" \
-d "visibility=${visibility}" \
--header "Authorization: Bearer ${access_token}" \
-sS https://${instance_domain}/api/v1/statuses
touch public/system/count.txt
else
if ((${DEFAULT} > ${size})) && [ -e public/system/count.txt ]; then
echo "${DEFAULT}で警告済みのためスキップ"
else
echo "${DEFAULT}のため正常"
rm -f public/system/count.txt
exit 0
fi
fi
Comments
No comments yet. Be the first to react!