こんばんは.今後,handon.clubのサーバ構成について詳細な説明記事を書く予定です.しかしそのためには,まずはMastodon の動作原理について解説しておいた方がよいかなと思います.この記事では,Mastodon を構成する技術要素について,特にスケーリングの観点を中心に説明します.
今日の記事は主に,ソフトウェア技術者向け(ある程度のバックグラウンドがある方向け)に書いてみます.ただ,それだけではおもしろくないので,後日一般向けの記事にもチャレンジしてみるつもりです.読んでみて,ちんぷんかんぷんだった方は次回をお待ち頂けると嬉しいです!
全体の概要
以下に,Mastodon の動作原理の概要図を示します.ここで,図中では,正確性は犠牲にしており,わかりやすさのため単純化 していることはご留意下さい.
Mastodon の動作原理概要
まず,Mastodon はwebアプリケーションですので,全てのリクエス トは一度webサーバ(公式ではnginxが推奨されています)で受け,その後適切な振り分けを行います.ここで図中では,上からユーザ(または他のインスタンス )からアクセスが行われるイメージで記載しています.
nginxからの振り分け先は3つで,Webページ(テキスト等)表示用にはrails とNode.jsが,そして画像表示用にはS3またはSwiftなどのオブジェクトストレージが利用されています.また,Mastodon サーバ内部では,長期でデータを保持するDBとしてpostgresql が,短期でデータを保持するキャッシュとしてredisが用意されています.更に,非同期処理はsidekiqで実現されています.
要素名
ソフトウェア名
概要
web server
nginx
HTTP/HTTPS リクエス ト処理
web
rails
Web処理(通常処理)
streaming
Node.js
Web処理(Userstreamのみ)
media
swift
画像やカスタム絵文字の格納
db
postgresql
アカウント情報やトゥートの格納
cache
redis
キューキャッシュ
job queue
sidekiq
ジョブハンドラー
dockerについて
ここで注意点です.あなたがもしこれからMastodon サーバを建てようと思って調査を始めると,すぐに「docker環境」「非docker環境」という2つの環境があることに気づくと思います.dockerは,インストール済みのイメージをダウンロードしコンテナとして展開することで,自らの環境構築作業の手間を削減できる便利な技術です.Mastodon のgithub を参照すると分かりますが,Mastodon 用のdockerコンテナとして,図中に記載している「web」「streaming」「db」「redis」「sidekiq」の5つのコンテナイメージおよびその設定ファイル(docker-compose.yml)が用意されています(nginxは自らホスト上に建てる必要があります).当たり前なのですが,本記事で紹介する基本的な動作原理においては,「docker環境」であろうと「非docker環境」であろうと大きな差分はありません.
以下,それぞれ詳細を説明したいと思います.
Webサーバ(nginx)
www.slideshare.net
みんな大好きnginxです.Apache に比べとても軽量で,スケールすることが知られています.もちろんWebサーバならばなんでもよいのですが,Mastodon のGithub リポジトリ ではnginxが推奨されており,サンプルの.confファイルも提供されています.
具体的な役割ですが,設定例を見ると分かりやすいです.そこで,イメージを以下に示します(ただし,この設定は説明用のため,本質ではないところは省略しています.決してコピペして使わないで下さい ).
server {
listen 80;
listen [::]:80;
server_name example.com;
return 301 https://$host$request_uri ;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_protocols TLSv1.2;
ssl_ciphers EECDH+AESGCM:EECDH+AES;
ssl_ecdh_curve prime256v1;
ssl_prefer_server_ciphers on ;
ssl_session_cache shared:SSL:10m;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
add_header Strict-Transport-Security "max-age=31536000" ;
location / {
try_files $uri @proxy ;
}
location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
add_header Cache-Control "public, max-age=31536000, immutable" ;
try_files $uri @proxy ;
}
location @proxy {
proxy_pass http://127.0.0.1:3000;
}
location /api/v1/streaming {
proxy_pass http://127.0.0.1:4000;
}
}
主な役割は以下の通りです.
項番
役割
1
HTTPをHTTPS へリダイレクトする
2
HTTPS ,HSTSなどセキュリティ周りの設定を行う
3
特定のコンテンツに対してのみ,キャッシュを要求する
4
リバースプロキシ
1は分かりやすいと思いますので説明は割愛します.2, 3, 4について,以後で説明します.
Letsencript(HTTPS )
Let's Encrypt の使い方 - Let's Encrypt 総合ポータル
個人のWebサイトでHTTPS 対応をする場合,数年前まではそれなりに高額な費用が発生していました.しかし今となっては,「letsencrypt」を使えば,無料でかつ簡単に証明書を取得することができます.
Mastodon では,HTTPS を使うことが強く推奨されていますし,構築マニュアルでもこのletencryptが紹介されています.HTTPS 対応をしない場合,Google Chrome で閲覧すると警告画面が出てしまうだけでなく,Google 等の検索エンジン 最適化(SEO )でも不利だと言われています.letsencryptを使って,是非HTTPS化 には対応しておきたいところです.
letencryptは,提供されるいくつかの方法のうち,いずれか一つでサーバを認証すれば,HTTPS 対応に必要な証明書を発行してくれます.nginxのconfig中の項番「2」に該当する「 /etc/letsencrypt/live/example.com /fullchain.pem 」等が,実際にletsencryptから発行された証明書を表しています.
余談ですが,letencryptは,通常の証明書に比べその有効期限が非常に短い(3ヶ月)です.そのため通常は,cronなどのジョブスケジューラを使って,3ヶ月に1回以上の頻度で自動の証明書更新を設定する必要があります.
www.slideshare.net
CDN (Content Delivery Network)は,世界中に分散設置されたキャッシュサーバの塊です.例えばApple が,iPhone のOSイメージを配布するケースを考えましょう.アメリ カのデータセンタにだけサーバを立ててしまうと,日本やヨーロッパからの大量のアクセスがあった場合,都度アメリ カからデータを配信することになってしまいます.1万回ダウンロードの要求があれば,全く同じOSイメージが1万回も海を渡るのです.これでは非効率です.そこでCDN は,よくアクセスされるコンテンツを自動的にキャッシュします.そして,日本からのアクセスでは日本のキャッシュサーバから,ヨーロッパからのアクセスではヨーロッパのキャッシュサーバからコンテンツを配信します.
このCDN によって,ユーザがダウンロード時間の短縮というメリットを受けることができるだけではなく,配信サーバの管理者側もサーバ負荷を削減することができるのです.夢のような話ですね.これもLetencriptと同じで,昔は有料のサービス(Akamai など)しかなかったのですが,近年は「CloudFlare」に代表される無料CDN サービスが登場しました.なぜ無料なのか不思議でならないのですが,一定の制限はありつつも,普通に使えます.
Mastodon の場合,CDN を使うことは必須ではありません.しかしながら,特に投稿画像やカスタム絵文字などについては,CDN を通すことでユーザのレスポンスを大幅に改善することができます.更に,CDN 事業者によっては,DDoS攻撃 を緩和してくれるサービスを提供している場合もあります.つまり,CDN を通すことでセキュリティを高めることも可能なのです.そのため,一定規模以上のインスタンス では,CDN を活用しているケースが多いようです.
さて,nginxの話に戻ります.項番「3」は,CDN を強く意識した設定です.これは,CDN サーバに対して,「emoji(カスタム絵文字)」や「media_attachments(投稿画像)」については最大限キャッシュして下さいね,という指示を送るための設定です.nginxではこういった設定までも簡単に行えるのです.
リバースプロキシ
プロキシサーバとリバースプロキシサーバの違い | ITSakura
残りの項番「4」は,リバースプロキシの設定です.リバースプロキシは様々な目的で用いられますが,これまでに説明したようなnginxを通すことによるメリットを教授するため,Mastodon のWebサーバに直接接続するのではなくnginxを "噛ます" ための設定だとご理解下さい.
リバースプロキシは説明し始めると長いのでこの辺で終わりにします.
フロントエンド(rails ,Node.js)
Mastodon の主要部分はRuby で書かれています.実際ソースコード を眺めると,かなりの部分がRuby on Rails で書かれていることが分かるかと思います.この部分は詳細を説明し始めると長くなりますので割愛します.ただ,基本部分は前述の通りrails で実現されているものの,UserStreamだけはNode.jsで実現されていることがポイントかと思います.
Node.js
www.slideshare.net
Node.jsはストリーミングAPI を提供することのできるサーバサイドJavascript です.私はインフラ屋さんなのでWeb系の技術には大変疎いのですが,それでも知っている超有名なサーバサイドJavascript です.「ノンブロッキング I/O」を実現していて,シングルスレッドで実行するにもかかわらず応答が帰ってくるまでの間の排他を必要としないため,非常に高速かつスケーラブルであることが知られています.つまり,多量のユーザから同時にUserstreamの受信要求があったとしても,ある程度スケーリングするようになっています.
DB(postgres,swift)
postgres
employment.en-japan.com
pgtune.leopard.in.ua
基本的なデータベースとしてはSQL であるpostgresql が採用されています.つまり,皆さんの過去のトゥートは,全てこのpostgresの中に入っていることになります.例えば,ユーザがタイムラインを表示してくれというアクセスをした場合、そのユーザはrails (web)やNode.js(streaming)を通して,postgresのDB内を参照しています.
なお,postgresは大変取り扱いやすいSQL ではありますが,Mastodon において最もスケーリングが難しい部分と言われています(まあ,DBがネックになるのはMastodon だけではないのですが・・・).特にpostgresは,RAMをバカ食いします.RAMのチューニングがとても重要です.
チューニングについては,pgtuneというサイトが便利です.なんと自動で,マシン環境に応じた設定ファイルを生成してくれます.サンプルとして,以下に,pgtuneを使って作成した,postgresql .conf設定例を示します.
max_connections = 50
shared_buffers = 1GB
effective_cache_size = 3GB
maintenance_work_mem = 256MB
checkpoint_completion_target = 0.7
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 4
effective_io_concurrency = 2
work_mem = 20971kB
min_wal_size = 1GB
max_wal_size = 2GB
max_worker_processes = 2
max_parallel_workers_per_gather = 1
max_parallel_workers = 2
一番上に記載している「max_connections」は,とても重要な設定です.同時接続コネクションをいくつまで許容するかを決定します.そのうえで,他の部分で各コネクション毎のメモリの使い方を指定することで,postgres全体のチューニングを行いますとなります.postgresでは,同時にたくさんのコネクションを接続するためには多数のRAMを必要とするため,RAMが少ないマシンではコネクションを制限してRAM消費を減らす,などの工夫が必要となります.もし,マシンの搭載RAMではまかないきれないようなコネクション数を許容する設定にしてしまうと,常にswapが発生する状態となり,パフォーマンスに大きく影響します.pgtuneでは,この辺りの設定を自動で行ってくれるのです.
ただ実際は,よほど大規模なインスタンス ではない限り,pgbouncerと呼ばれるコネクションプールツールを使って,RAM不足の問題を簡単に解消することも可能です.コネクションプールは,postgresql がスケーリングしない理由の一つである「同時にたくさんのコネクションを接続するためには多量のRAMを必要とする」という問題を解決してくれるツールです.
swift/S3
画像の格納にはオブジェクトストレージが使われています.当初はS3にしか対応していませんでしたが,いつからかswiftにも対応しました.本家のS3はお高いので,代わりにS3互換ストレージか,OpenStack swiftを使っている人が多い印象です.
前述の話と合わせると,大半の画像データはCDN がキャッシュしているので、「CDN がS3/Swiftに画像を取りに行く」とも言えますね.
格納されている画像は,「アイコン画 像」「トゥートに添付された画像」「カスタム絵文字の画像」の3種類です.ここで,全ての種類の画像は,他のインスタンス から受信した画像も含まれる点に注意してください.言い換えると,自インスタンス の画像は,他インスタンス のDBにもキャッシュされていることになります.
なお,このような他インスタンス のキャッシュを持ち続けると,ストレージ容量が肥大化してしまいます.そこでMastodon では,ある程度時間のたった古いトゥートに添付された画像については,簡単に削除できるツールが提供されています.もちろん,自インスタンス の画像は消さずに残すことが可能です.一般には,このツールをcron等の定期実行ツールで動かすことが多いです.
余談ですが,この仕様は,過去Pawoo関連で大きな問題を生みました.詳細は割愛しますが,「ポルノ画像が全世界のMastodon サーバーにキャッシュされ,インスタンス 管理者が皆逮捕されてしまうのではないか?」との懸念から,Pawooをインスタンス 単位でブロックする流れが生まれたのです.現在はインスタンス 単位で画像をキャッシュする/しないを選択することができるため,ことなきを得ています(got ことなき).
なお,「docker環境」で構築した場合含め,初期設定では,画像は全てローカルに保存されています.つまり,オブジェクトストレージを一切使わない設定になってしまいます.以前のJP鯖の管理人であるnullkal氏も発言していましたが,このローカル保存は本当にスケーリングしませんし,また後からの環境変更も困難です.スケーリングのためには,AWS S3とまでは言いませんが,S3互換ストレージ,またはswiftを使うべきです.
バックエンド(redis,sidekiq)
sidekiq
Mastodon の全ての動作は同期処理で行われているわけではありません.主に非同期処理を担うのがsidekiqです.sidekiqは,Rails アプリケーションでよく使われるジョブキューとなります.またsidekiqは,待っているジョブをキャッシュしておく場所として,redisを必要とします.そのため,Mastodon もredisを持っています.
Mastodon における非同期処理について説明します.例えば,Mastodon のインスタンス 間のトゥート配信では,activitypubと呼ばれるプロトコル が使われています.例えばAさんが発言した場合,そのAさんが所属しているインスタンス は,Aさんをフォローしている人がいる他のインスタンス 全てに対して,トゥートを配信しなくてはなりません.これは非常に重たい処理ですし,仮に落ちているインスタンス があればリトライをしなくてはなりません.このような処理を同期処理で実施していては大事になってしまいます.そこで,Mastodon ではこういった非同期処理をsidekiqに担わせているのです.
その他の非同期処理としては,トゥート本文中のURLのインラインプレビューを更新する作業,メール送信機能などが上げられます.これら非同期処理はいくつかの「キュー」に分類されていますので,インスタンス 管理者は各キューにどれだけサーバリソースを割り当てるか設定することもできます.ただし,初期設定,特にdocker環境では,全ての「キュー」が対等に扱われるようになっています.
キュー
さて,「キュー」には,default,mail,pull,pushの4つがあります.pullとpushが他インスタンス との連携(activitypub)に関するキューで,defaultとmailが自インスタンス に完結する処理に関するキューです.
キュー名
内容
default
インスタンス 内部でのトゥート処理
mail
新規登録,フォロー通知などユーザへのメール配信
pull
他インスタンス からのトゥート受信
push
他インスタンス へのトゥート送信
ちなみに大規模インスタンス の一つであるPawooでは,スケーリングのためにわざわざこのキューを独自で拡張しているそうで,スケーリングのためにはキューを正しく使いこなすことが重要であることがお分かり頂けるかと思います.
スケーリングのために注意すべきは,sidekiqプロセス自体は,CPUのシングルスレッドしか使えないという点です.しかし,例えば4キューを別々のsidekiqプロセスで処理させることで,簡単にマルチスレッド化が可能です.つまり,使っているCPUが4スレッド以上あれば,うまく別スレッドに割り当てて互いの影響を最小限にすることが可能なのです.
よく,「sidekiqが詰まる」と言っているMastodon インスタンス 管理者を見かける,という方もいらっしゃるのではないでしょうか.これは,処理が間に合わず,キューが増え続ける状態を指しています.このように,sidekiqは,特に小中規模インスタンス では最もスケーリングが難しい部分です.ただし前述のとおり,プロセス数を増やすことで実行スレッドを簡単に増やせること,またDBのような排他処理も不要なため実行サーバを大量に増やすことで並列処理が容易なことから,サーバリソースさえつぎ込めばいくらでもスケールします.つまり,「金で殴る」ことが簡単な部分なのです.
少し余談です.
当然,Mastodon を構成する全てのコンポーネント はオープンソース として公開されています.ただし,Mastodon のソースコード 自身は,AGPLライセンスで公開されていることに注意が必要です.
AGPLライセンスは,GPL と大変よく似たライセンスです.GPL の条項の中には,「利用者からソースコード を求められた場合,必ず公開しなくてはならない」という旨の規定があります.このように,オープンソース をベースに別人が追加開発したソフトウェアであっても,それがオープンソース となることが保証される点が特徴です.オープンソース 界の発展に繋がる,たいへん"強い"ライセンスではありますが,一部企業等からは自ら開発したソフトウェアの収益化が難しくなるため,避けられることもあるライセンスです.
AGPLとGPL の違いは,この「利用者」の定義です.端的に言うと,AGPLでは,利用者の定義が「Mastodon のソースコード を改変してWebに公開した場合,そのインスタンス に登録したりトゥートを閲覧する人」にまで広げられています.GPL よりも"強い"と言うことも出来ると思います.
Pawooやfriend.nico など,Mastodon 本家のコードに追加開発を加えたインスタンス についても,そのソースコード が公開されていることは有名だとは思います.この理由はこのライセンス条項によるものだと考えられます.
終わりに
今日はMastodon を構成する技術要素について話をしました.私の運営する「handon.club」でどのような構成になっているか,という話まではできませんでしたので,今後の記事にご期待頂けると嬉しいです.また,非技術者向けにも分かりやすい解説記事についても書きたいと思っていますので,そちらを楽しみにしている方はしばしお待ち頂ければ幸いです.
もし当記事に明らかな誤り等有ればコメント等でご指摘頂けると大変嬉しいです・・・.
参考にさせて頂いた文献
私がMastodon の技術要素の理解だけでなく,インスタンス 構築に当たっても参考にさせて頂いた記事のご紹介です.もちろんこれだけではありませんが,3つに絞ってみました.
qiita.com
inside.pixiv.blog
http://amzn.asia/d/49xq410 amzn.asia
追記
2018/12/20 一部説明が不足していた部分を追記しました.まだまだ追記予定です.