Mastodonインスタンスを立てたときの諸々メモ

2023年10月1日Mastodon/MisskeyFediverse,Mastodon

Mastodonインスタンスの立て方の記事は無数にあるはずだけれど、こういうののソースは多いにこしたことはない

記事内容について

自鯖Mastodon( Poketedon )の構築メモです。

今回の記事では最初のサーバーの立ち上げ部分を取り扱います。
移転も経験しましたが、それは別記事に(するはず)。

  • Mastodon v4.2.0 時点の情報です。
  • おひとり様サーバー前提です。
    複数人で使う想定をした負荷対策等はしていません。
  • UbuntuのVPSを借りて構築しています。
  • オブジェクトストレージにAWSのAmazon S3を使用しています。

サーバースペック

VPSを借りる場合、以下の点に注目。

  • OSはUbuntuが推奨(公式がそうなので)。
  • Ubuntuのバージョンは 20.04 がベストだが(公式がそうなので)、
    この手順では最新のLTSであるUbuntu 22.04を使っています。
    ※そのために公式手順を逸脱しているところがあります
  • メモリ2GB、CPU2コアあれば足りなくはないですが、
    できればメモリ4GBは欲しいかもしれません
    • 完全に引きこもる、外との関わりをほとんどしない場合…
      具体的にはリレーサーバーに入らない場合は、2GBで足りるはずです

参考情報として。一番最初に選んだのは、KAGOYA CLOUD VPSの2コア/メモリ2GB/SSD25GBプランでした。
Misskeyと違い(別記事にするはず)、ストレージ容量は25GBでも足りました。
現在は色々あってXServer VPSのプラン4GBで動かしています。

サーバー構築

あらかじめ決めておくこと

以下の内容は決めておきましょう。

  • ドメイン名
    • サブドメインでも可。
    • うちの場合、mstdn.pokete.com
    • ドメイン名は決めたら二度と変えられません。ActivityPubの掟です。
    • Mastodonを手放す日のことを考えると、うちのようにサブドメインで動かすのをおすすめします。
      Mastodonを手放した後しばらく「弊Mastodonは終わったよ!」アピールをしないといけない都合上、しばらく同一ドメインでは別サービスを使えなくなります…
    • VPS作成してIPアドレスが分かった段階でDNSに登録しておくと、浸透待ちの時間を有効活用できます。
  • オブジェクトストレージを使うかどうか
    • 使わない場合、サーバー内のストレージを消費します
    • 使う場合、使った分だけお金がかかります(MinIOなどは別)
    • もし使う場合は、Mastodonは標準で↓に対応しています。
      • DigitalOcean Spaces
      • Amazon S3 (←=AWS)
      • Wasabi
      • MinIO
      • Google Cloud Storage (←=GCP)
    • そして使う場合は、事前に準備が必要です。
      本稿ではAmazon S3を使う事前準備について記載してあります。
  • メールサーバーを外部のものを使うかどうか
    • 使わない場合、localhostからメールを送ることになります
      (本当に自分しか使わない場合はそれで握りつぶしてしまっていいかも)
    • 使う場合、SMTPにかかわる情報が必要なので、マニュアル等を見れるようにしておきましょう
      とくにSendgridの無料プランを日本代理店経由で使う場合、審査に1~2営業日はかかるので注意しましょう

作業用ユーザー作成

VPSを借りるときは、SSH鍵を作るはず(任意の場合は作ったほうがいい)。

その接続情報をもってしてSSHログイン。

  • 補足:初回接続情報
    • たぶん、公式マニュアルに書いてあります
    • KAGOYAの場合、ユーザー名はroot、パスワードは空で、VPS作成時に作った「ログイン用認証キー」をid_rsaとしてログインする
    • XServerの場合、ユーザー名はroot、パスワードはVPS作成時に記入したもの。
      あるいは、ユーザー名と作成したSSH Keyを使ってログインする。
       ↑こっちはパスワード不要。保安上こっちがオススメ。
    • いずれも『初回』ログイン情報で、以降の手順の序盤で使わなくなります。
  • 補足:ターミナルソフト
    • ターミナルソフトは、WindowsではRLoginがおすすめ。TeraTermでもいいけど。
    • iPadから接続しないといけないときはTermiusの無料版を使っています。

以下セットアップはVPSを借りたあと必ずやる儀式みたいなものなので、
Mastodon以外にも使えるはずです。

adduser {hogeUser}
# 例:adduser gorou12
# 以降 {hogeUser}はユーザー名。
# パスワードはいい感じのをつけておく

usermod -aG sudo {hogeUser}
# sudoグループに所属させる
# セキュリティ上難があるのであとでよしなに調整する

su - {hogeUser}

ssh-keygen
# パスフレーズは設定した上で、とりあえず次々OKしていく

mv ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

# ~{hogeUser}/.ssh/id_rsa をSSH SCPなどでダウンロードする
# ターミナルからログアウトして、以降、rootユーザーではなく
# {hogeUser}で作業(SSH鍵も落としてきたid_rsaにする)

サーバー初期セットアップ

公式でいうとこれの話

# apt最新化
sudo apt update
sudo apt upgrade

# インストール中に Do you want to continue? [Y/n] と出てくるが、
# 大文字Yを入力してEnterで進める
# (この先も手順中に何度かあるが同じ)

# そのときに急にGUIが出てきて Pending Kernel Update とか
# Which services should be restarted? とか聞かれるが、
# 何も変えずOKでよい。
# (TABキーでOKボタンへジャンプする)

fail2ban設定

# fail2banインストール
sudo apt install fail2ban
sudo vi /etc/fail2ban/jail.local
# Mastodonドキュメントにあるやつを貼る
sudo systemctl restart fail2ban

iptables設定

Ubuntu22.04の場合、nftablesになってしまってる関係で
Mastodon公式にあるものがそのままは使えなくなってしまっています。

ここでは仕方ないので、nftablesではなくレガシーなiptablesを使うようにしてみました。

# iptables永続化
sudo apt install iptables-persistent
# 急にウィンドウが出てくるので、<いいえ>する

# <ここだけUbuntu 22.04の場合(20.04の場合はスキップ)>
sudo update-alternatives --config iptables
# legacyなiptablesを有効化する

# <ここから公式手順に合流>

sudo vi /etc/iptables/rules.v4
# Mastodonドキュメントにあるやつを貼る
sudo iptables-restore < /etc/iptables/rules.v4

# IPv6アドレスもあれば、そっちも作業する
sudo vi /etc/iptables/rules.v6
iptables-restore < /etc/iptables/rules.v6

ここで設定したFail2banの設定やiptablesの設定は後程見直していますが、環境依存があるため、そこは割愛します。
(この手順通りでMastodonはちゃんと動きます)

Mastodonインストール

公式でいうとこれの話

sudo su -
# ここからrootで作業

apt install -y curl wget gnupg apt-transport-https lsb-release ca-certificates

node.jsインストール

Mastodon公式にある手順は非推奨になっているので、node公式の手順にリライト。

mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg

NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
# ここまでnodeインストール準備
# (あとで一括でapt update, apt install nodejsする)

PostgreSQLインストール

せっかくなのでこちらもPostgreSQL公式風にリライト。

sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
# ここまでPostgreSQLインストール準備
# (あとで一括でapt update, apt install postgresqlする)

システムパッケージのインストール

ここからMastodon公式手順通り。

apt update
apt install -y \
  imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev file git-core \
  g++ libprotobuf-dev protobuf-compiler pkg-config nodejs gcc autoconf \
  bison build-essential libssl-dev libyaml-dev libreadline6-dev \
  zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev \
  nginx redis-server redis-tools postgresql postgresql-contrib \
  certbot python3-certbot-nginx libidn11-dev libicu-dev libjemalloc-dev

corepack enable
yarn set version classic

apt install git

Rubyインストール

mastodonユーザーで、~mastodon/live配下にセットアップする。

# Mastodonシステム用ユーザーを作成
adduser --disabled-login mastodon
# 設定情報全部空欄でEnter
# Mastodonユーザーでセットアップするのでユーザー切り替え
su - mastodon

# ここからRubyインストール
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec bash
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
# ↓公式手順だと3.0.6を入れるよう誘導されているが、
# Mastodon v4.2.0ではRuby 3.2.2が推奨なので書き換えなければならない
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 3.2.2
# インストールに5分ぐらいかかる。ターミナルが動かないがそのまま待つこと
rbenv global 3.2.2
gem install bundler --no-document
exit
# ↑rootユーザーに戻る

Mastodonインストール

# PostgreSQLでMastodon用DB作成
sudo -u postgres psql
CREATE USER mastodon CREATEDB;
exit
-- ↑もし効かなければ「\q」(円マークと半角小文字のQ)
su - mastodon
# ↑mastodonユーザーになる

git clone https://github.com/mastodon/mastodon.git live && cd live
git checkout $(git tag -l | grep -v 'rc[0-9]*$' | grep -v 'beta[0-9]*$' | sort -V | tail -n 1)
# 公式手順だとrc[0-9]*だけを除外しているが、
# 4.2.0はbeta[0-9]*という書き方みたいなので、それも除かないといけない

bundle config deployment 'true'
bundle config without 'development test'
bundle install -j$(getconf _NPROCESSORS_ONLN)
yarn install --pure-lockfile
# ぼちぼち時間がかかる rubyのインストールと違って、こっちは文字が流れていく

S3セットアップ(必要なときのみ)

ファイルストレージにAmazon S3を使う場合のみ、この節をチェックしてください。

外部からファイルをRead/WriteできるS3バケットと、そのRead/Write権限を設定したアクセスキー・アクセスシークレットが必要です。

…というところが、この節の最終目標です。

AWSの基本知識や基本操作(そもそもアカウントの作成方法とは、を含め)は触れません。
すでにAWSアカウントがあり、しばらく使ったことがあるものとして、
要点だけかいつまんで記載します。

設定したい方は確認の上設定してください。
自信がない場合はS3は諦め、ローカルストレージでもいいかもしれません。
(ローカルストレージのデメリット:ディスク容量に限りがある
 メリット:いくらストレージを使おうとも、VPSの料金以上に課金は起きない)

S3バケットを作る

  • バケット名は自由だけれど、自分は使う予定のサブドメイン名をそのままバケット名にした
  • リージョンも自由。自分は大阪(ap-northeast-3)にした。
  • オブジェクト公開とかはしなくていい、はず。
  • (メモに「やっぱACL公開にしないといけなかった」って書いてて気になるけど、
    この手順をパーフェクトにすれば不要な気はするんだよね…)

CloudFrontでディストリビューションを作成する

  • オリジンドメインは、今作ったS3バケット
  • オリジンアクセスは、recommendされてるやつ
  • Origin access controlの横にある「コントロール設定を作成」ボタンを押す
  • WAFはいらない(とした)
  • 「ディストリビューションを作成」ボタンを押すと、
    「S3バケットポリシーを更新する必要があります」というのが出てくるので
    その横にある「ポリシーをコピー」をクリックして拝借する
  • S3の先ほどのバケットの画面を開き、バケットポリシーにそれを貼り付けて保存する

Certificate Managerでサブドメイン用の証明書を作る

  • 証明書はus-east-1で作らないといけない。(とCloudFrontが言ってる)
  • ドメイン名はファイル用サブドメイン
  • リクエストするとCNAME名・CNAME値が提示されるので、
    お使いのDNSの画面を開いて、そのとおりにCNAMEをセットする
  • 設定後、数分待つと承認される

CloudFrontに証明書と代替ドメイン名を登録する

  • 先ほど作ったディストリビューションを開いて、設定で「編集」を押す
  • 代替ドメイン名(CNAME)に、ファイル用サブドメインを追加する
  • カスタムSSL証明書 にさっき作った証明書をセットする
  • ほかは触らず保存する

DNSにCloudFrontディストリビューションドメイン名を設定

  • お使いのDNSの画面を開いて、ファイル用サブドメイン→CloudFrontディストリビューションへのCNAMEを設定する
  • すなわち、CNAME名がファイル用サブドメイン、CNAME値がCloudFrontディストリビューションドメイン名。

IAM設定

1.IAMポリシーを作成する。
S3フルアクセス権限でもいいが、ベストプラクティスに沿って最小権限でいくと、↓があれば足りるはず。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetBucketLocation"
            ],
            "Resource": "arn:aws:s3:::{@S3バケット名で置き換え@}"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObjectAcl",
                "s3:GetObject",
                "s3:AbortMultipartUpload",
                "s3:DeleteObject",
                "s3:PutObjectAcl",
                "s3:ListMultipartUploadParts"
            ],
            "Resource": "arn:aws:s3:::{@S3バケット名で置き換え@}/*"
        }
    ]
}

2.IAMユーザーグループを作成
さっき作ったポリシーをアタッチしておく

3.IAMユーザーを作成
さっき作ったグループに所属させる
そしてそのユーザーのアクセスキーを入手する。

AccessKeyとSecretAccessKeyを控えたら、↓の「IAMアクセスキー」「IAMシークレットアクセスキー」に埋める。

Mastodonセットアップ

セットアップコマンドを実行します。

途中でしくじると面倒なので、一発で通せるように、この節(と場合によっては↑の節)をあらかじめ準備のうえ、↓を流しましょう。

# これを実行するとこのコードブロックの下にあるものが次々と聞かれるので、
# あらかじめ準備しておくべし
sudo su - mastodon
cd ~/live
RAILS_ENV=production bundle exec rake mastodon:setup

必要な情報と例・補足は以下の通り。
最新の聞かれる内容はソースを見て追うとよいでしょう。
メモ帳にでもコピって、全部埋めておくといいでしょう。

  • ドメイン名
    • 我がトドンならmstdn.pokete.com
    • 一度このドメインで運用を始めたら、サーバー破棄以外変更の手段はない!!
      (それがActivityPubの掟)
      慎重に決めるべし!!!
  • シングルユーザーモード?(y/N)
    • 有効にすると、Webブラウザ上ではユーザーを追加できなくなります。
      コマンドラインからなら追加できます。
    • 完全クローズにしたい場合は有効にしましょう。
      我がトドンは有効です。
    • ここ以降、2択の質問では「y/N」または「Y/n」という書き方が出てきます。
      コマンドでは「y」または「n」を入力してEnterします。
      無入力でEnterすると、大文字になっているほうが選ばれます。
      この選択肢の場合、無入力EnterならNoになります。
  • Dockerを使う?(Y/n)
    • この記事ではDockerを使わずダイレクトインストールしています
    • したがって、この記事どおりならnoです。
  • PostgreSQLについて…
    • ホスト名(/var/run/postgresql)
      • ここ以降、カッコ書きの中身が初期値です。
        違う場合だけ入力しなおします。
      • この記事通りなら、以降も初期値どおりです。
    • ポート番号(5432)
    • DB名(mastodon_production)
    • 接続ユーザ(mastodon)
    • 接続パスワード(空)
  • Database configuration works! 🎆
    • …が出なければエラーが出てるはずなので、それを参考に対応しましょう。
  • Redisについて…
    • ホスト(localhost)
    • ポート(6379)
    • パスワード(空)
  • Redis configuration works! 🎆
    • …が出なければエラーが出てるはずなので、それを参考に対応しましょう。
  • ファイルストレージについて
    • ファイルはクラウドストレージにアップロードするか?(y/N)
      • この記事ではyを選びました
      • Nを選ぶ、すなわちローカルストレージを使う場合は「メールについて」までジャンプ
    • ファイルストレージのプロバイダ
      • (DigitalOcean Spaces | Amazon S3 | Wasabi | Minio | Google Cloud Storage)からキーボードで選択
      • ここではAmazon S3を選択
      • 色々下準備が必要です!!!
        事前に準備しておきましょう。S3の場合については前の節を見てください。
        ↓をすべて埋められるようになったら戻ってきてください。
    • バケット名
    • リージョン名
    • ホスト名
    • IAMアクセスキー
    • IAMシークレットアクセスキー
    • アップロードファイルのドメイン名を別に設定するか?(Y/n)
      • そのドメイン名
  • メールについて
    • メールはlocalhostから送る?(y/N)
      • この記事ではNを選びました
      • yを選ぶ、すなわちlocalhostを使う場合は「メール送信時のFrom…」までスキップ
    • SMTPサーバ
    • SMTPポート
    • SMTPユーザー名
    • SMTPパスワード
      • ここまでは使おうとしているメールサービスを見ると・設定するとわかるはず。
    • SMTP認証(plain)
      • うちのサーバーはplain認証でした。
      • ほかにはlogin認証などがあります
    • SMTP OpenSSL認証モード
      • (none|peer|client_once|fail_if_no_peer_cert)からキーボードで選択
      • うちのサーバーはいったんnoneにしました
    • SMTPのSTARTTLS認証
      • (auto|always|never)から選択
      • うちはalwaysです
    • メール送信時のFromの設定(Mastodon <notifications@{ドメイン名}>)
      • 初期値でいいかもしれないけど飾りたいときは変えましょう
    • メールをためしに送信してみる?(Y/n)
    • ためしに送信する場合、テストメールの送り先To
      • ここで指定したメールアドレス宛に、件名「Test」本文「Mastodon SMTP configuration works!」が来ればOK
      • Mastodon的にメール送信に失敗したら、失敗した旨のお知らせが出て、メールについての再設定を促されます
  • Mastodonの重要な更新を自動確認してお知らせしてほしい?(推奨)(Y/n)
  • ここまでを .env.production に保存する?(Y/n)
    • ここでNoを押すと、ここまでの設定をすべて破棄して無かったことにします。
      やり直したい場合はNoで。
  • 当該データベースがすでに存在する場合は消しちゃうけど、今からDBの設定をしていい?(Y/n)
    • ここでNoを押しても先に進みます。
    • あとからやる場合のコマンドは、 RAILS_ENV=production rails db:setup
  • 最後のステップだよ。
    だいぶメモリを食うけど、いまからCSSとJSのアセットをコンパイルする?(Y/n)
    • ここでNoを押しても先に進みます。
    • あとからやる場合のコマンドは、 RAILS_ENV=production rails assets:precompile
  • All done! You can now power on the Mastodon server 🐘
    • ぱおんぬ
  • いますぐ管理者ユーザを作る?(Y/n)
    • 作らない場合、コマンドで手動でユーザーを追加することになるのでY推奨。
  • Username
    • Mastodonのユーザー名。ユーザー名は半角英数とアンダーバーが使用可能。
      (gorou-12はダメでした…)
    • 一度設定したUsernameは改名できないので注意。ActivityPubの掟です。
  • E-mail
    • Misskeyと違いMastodonはメールアドレス必須
  • パスワードは自動生成されて、ここで画面に表示されます。
    ログイン後に好きなものに変更しましょう。

おわり。

準備はできましたか?できたら箇条書きの上にあるコマンドを叩きましょう。

なお、ここまで終わっても、まだMastodonにはアクセスできません。

nginxの設定と証明書設定

ここからrootユーザーでの作業。
mastodonユーザーであればexitして、rootになりましょう。

なお、本稿執筆時点の公式手順通りにするとLet’s Encryptの設定で100%失敗するので、ここに記載の手順で作業してください。

systemctl stop nginx

# nginx.confのテンプレートをMastodonから持っていく
cp /home/mastodon/live/dist/nginx.conf /etc/nginx/sites-available/mastodon
# sites-enabledにシンボリックリンク
ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon

# 必要な箇所を編集する
# example.comを新ドメインに置き換える(viなら、:%s/example.com/あたらしいドメイン/g がはやい)のと、
# SSL証明書に関するコメントアウトを外すのは必須。(ssl_certificateとssl_certificate_keyの2行)
vi /etc/nginx/sites-available/mastodon

# まずnginxを通さずスタンドアロンで認証する
certbot certonly --standalone -d あたらしいドメイン
# メールアドレス入力、利用規約の同意、(任意)ニュースレター登録をする

nginx -t
# nginx設定のテスト。
# 通ったら起動する
systemctl start nginx

# 起動して改めて、正攻法(?)で証明書を取り直す
certbot --nginx -d あたらしいドメイン
# 1: 既存の証明書を残す、2: 新しい証明書で置き換える
# という選択肢を迫られるので、2で上書き
# この方法でやると、certbot的にちょうどいいように
# /etc/nginx/sites-available/mastodonが勝手に書き換わる。

# -----
# 諸々こわいので今のうちにcronで自動更新するようセットしておく
certbot renew --dry-run
# ドライランで特にエラーが出ないことを確認
# (出るなら設定を見直す)

certbot renew
# まだ更新期日じゃないよって出ることを確認
# dry-runと違って叩き倒すとブロックされちゃうらしいので気を付ける

# このあとcronで設定するコマンドが走ることを(強制更新で)確認
/usr/bin/certbot renew --force-renewal --deploy-hook "systemctl restart nginx"

vi /etc/cron.daily/letsencrypt-renew

毎日↓が走るようにする

#!/bin/sh
/usr/bin/certbot renew --deploy-hook "systemctl restart nginx"

保存して、cronを読み込む

systemctl restart cron

Mastodonをデーモンとして登録する

そうしないと満足に起動しない。
ついでにサーバー起動時に勝手に立ち上がるようにもする。

cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming

おわり

これで最初に決めたドメインにアクセスすると、つながるはず。
つながらなかったら何かおかしい。

一般的なバージョンアップ手順

リリースノートで何か特別な作業が指示されてない限り(v4.2.0では指示された)、
↓の手順で大丈夫。

sudo su -
systemctl stop mastodon-{web,sidekiq,streaming}

su - mastodon
cd /home/mastodon/live
git fetch origin
git checkout v4.1.1
bundle install
yarn install
RAILS_ENV=production bundle exec rake db:migrate
RAILS_ENV=production bundle exec rake assets:precompile
exit

systemctl start mastodon-{web,sidekiq,streaming}

その他

VPSが調子悪かったりしたので、サーバー引っ越しや、Sidekiqの拡張、監視シェルの仕込みなどを行いましたが

それについてはまた別記事にするかも…?