Ubuntu20.04 LTSでmastodonをNon-Dockerで建ててみた

# はじめに
先日、自身で運営しているmastodonサーバーを私の手違いで再起不能にしてしまいました。
これは、自鯖の立て直しをした際の自分用の備忘録として書いておりますが、ついでに誰かのお役に立てれば幸いです。
なお、私の経験値はひよっこ以下なので、間違いや非効率な点もあろうかと思いますが。
その時はご教示くださるとありがたく存じます。

# 進め方
基本的には[公式ドキュメント](https://docs.joinmastodon.org/admin/install/)通りの手順で進めていきます。
ただし、公式ドキュメント通りだけでは動作しなかったため、そこは都度補足していきます。

### 前提条件
・有効なドメインを所有している
・VPSなり自宅なりにサーバーを持っている
・DNSレコードの設定が済んでいる

#### サーバーについて
今回使ったサーバーは下記のものです
･ さくらのVPS 2GB (SSD 100GB)
･ Ubuntu 18.04 amd64 (カスタムOS)

Ubuntu 18.04をインストールしたサーバにUbuntu20.04をアップデートしてます。
使い方がわかってれば直接isoでインストールするほうが賢いとは思います。
※思いますが、今回はこの方法で進めたのでお付き合いください。

また、いちいちsudoするのも面倒なので、基本的にはrootユーザーで作業してます。

# OSのアップグレード
ルートユーザーで作業します。

```
$ sudo su -
[sudo] password for <username>:
# apt update
# apt install -y update-manager
# apt dist-upgrade
# do-release-upgrade -d
```
以後、何度か入力を求められますが Yes / OK / Continue を選択していけば大丈夫です。
`# do-release-upgrade -d`　の `-d` は忘れないようにしてください。進みません。
アップグレードが終わったらサーバーがシャットダウンされるので、起動します。

起動後、 `cat /etc/os-release` でOSのバージョンを確認します。

```
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
```
Versionが20.04になっているのを確認してOSのアップグレードは完了です。
使い慣れたエディタなんかもこのタイミングでインストールしました。
ここで公開鍵の登録やsshdの設定を変更して、sshできるようにしておきます。
調べればたくさん出てくるので、公開鍵の登録やsshdの設定の説明はここでは省きます。

# ファイアーウォールの設定
標準で `ufw` がインストールされているのでこれを使います。ちょー簡単。
まず、 `sudo su -` でrootに入り、デフォルトですべてのポートを閉じます。
その後、必要なポートを許可していくようにします。
ここで間違えるとsshできなくなったりするので気をつけてください。
sshのデフォルトは22番ですが、1024番以降の任意の番号に変更はしておいたほうが良いです。

```
$ sudo su -
[sudo] password for <username>:
# ufw default deny          ※標準で全部塞いでますが、念の為塞ぎます。
Default incoming policy changed to 'deny'
(be sure to update your rules accordingly)

# ufw allow 80              ※80番(http)ポートの開放
Rules updated
Rules updated (v6)

# ufw allow 443             ※443番(https)ポートの開放
Rules updated
Rules updated (v6)

# ufw allow <sshd port>     ※SSH用ポートの開放
Rules updated
Rules updated (v6)

# ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)?  ※ここで'y'を入力
Firewall is active and enabled on system startup
```
以上で設定は完了です。ちゃんと動いてるかを`# ufw status`で確認します。

```
# ufw status
Status: active

To                         Action      From
--                         ------      ----
80                         ALLOW       Anywhere
443                        ALLOW       Anywhere
<sshd port>                ALLOW       Anywhere
80 (v6)                    ALLOW       Anywhere (v6)
443 (v6)                   ALLOW       Anywhere (v6)
<sshd port> (v6)           ALLOW       Anywhere (v6)
```
activeで動作中。ALLOWは通過許可です。これ以外のポート開放が必要な場合は適宜追加してください。
これでファイアーウォールの設定も完了です。
ここでsshのポートを間違えるとsshでログインできなくなるので注意してください。

以上でサーバーの初期設定は完了です。

# 必要なパッケージをインストールしていく
ここから[公式ドキュメント](https://docs.joinmastodon.org/admin/install/)に沿ってMastodonのセットアップを進めていきます。
現状では`curl`がインストールされていないので、rootに入って`curl`をインストールします。
以後、基本的にrootで作業になります。

```
$ sudo su -
[sudo] password for <username>:
# apt install -y curl
```
### Node.jsのリポジトリを登録

```
# curl -sL https://deb.nodesource.com/setup_12.x | bash -
```

### Yarnのリポジトリを登録

```
# curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
# echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
# 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 libncurses-dev libffi-dev libgdbm6 libgdbm-dev \
nginx redis-server redis-tools postgresql postgresql-contrib \
certbot python3-certbot-nginx yarn libidn11-dev libicu-dev libjemalloc-dev
```
`\`は改行なので、無視して一気に入力してもOKです。
ここで、公式ドキュメントとは違う箇所があります。
公式ドキュメントはUbuntu18.04が前提なので、下記の3箇所を変更してます。
`lib-curses5-dev` -> `libncurses-dev`
`libgdbm5` -> `libgdbm6`
`python-certbot-nginx` -> `python3-certbot-nginx`

### ユーザーの作成
Mastodonを実行するユーザーをまず作成します。

```
# adduser --disabled-login mastodon
```
ここでは公式ドキュメントに習って`mastodon`でユーザーを作成してます。
作成時に名前やら電話番号やら色々聞かれますが全部空白でOKです。
作成できたら`mastodon`ユーザーに切り替えます

```
# su - mastodon
```
以後、しばらく`mastodon`ユーザーでの作業になります。

### Rubyインストール
rbenvとrbenv-buildをインストールします。

```
$ 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
```
Rubyをインストールします。

```
$ RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 2.6.6
$ rbenv global 2.6.6
```
※`RUBY_CONFIGURE_OPTS=～`のコマンド実行のあと、数分かかります。気長に待ちましょう。

Rubyのインストールが終わったら`bundler`もインストールします。

```
$ gem install bundler --no-document
```
以上でひとまず`mastodon`ユーザーでの作業は終わりです。
`exit`でrootに戻ります。

```
$ exit
```
### Rubyのバージョンアップ
2022/4/1追記
Mastodonを運営しているとRubyのバージョンアップを求められるシーンがでてきます。
自分が少し躓いたので2.6.6から3.0.3を例にここに記しておきます。
バージョンアップは`mastodon`ユーザーで作業します。
まずはインストールできるバージョンのリストを更新します。
```
$ cd .rbenv/plugins/ruby-build
$ git pull
$ cd ~
```
次に更新したリストを確認します。
```
$ rbenv install -l
2.6.9
2.7.5
3.0.3
3.1.1
jruby-9.3.4.0
mruby-3.0.0
rbx-5.0
truffleruby-22.0.0.2
truffleruby+graalvm-22.0.0.2
```
お目当ての3.0.3が出てきたのでバージョンアップします。
バージョンアップといいつつ、やることはインストールと変わりません。
```
$ RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 3.0.3
$ rbenv global 3.0.3
```
バージョンアップができているか確認します。
```
$ rbenv version
3.0.3 (set by /home/mastodon/.rbenv/version)
```
ここで旧バージョンが表示されている場合は`rbenv global 3.0.3`を忘れていると思います。
Rubyでは複数のバージョンが共存でき、切り替えて利用ができます。
インストールしただけではバージョンの切り替えがされない為、注意しましょう。

# データベースのセットアップ
公式ドキュメントではオプションとして[pgTune](https://pgtune.leopard.in.ua/#/)を使った`postgresql`のパフォーマンス最適化が提案されています。
オプションですので、スルーしてもmastodonの動作は可能です。
やっておきたい人だけ実施すればいいのですが、`postgresql`のバージョンが公式とは違うため、`config`ファイルの
場所を読み替える必要があります。
ここまで問題なく進んでいれば`postgresql 12.2`がインストールされているはずです。

```
バージョンの確認
# sudo -u postgres psql -V
psql (PostgreSQL) 12.2 (Ubuntu 12.2-4)
```
12.2のconfigは`/etc/postgresql/12/main/postgresql.conf`にあります。適宜エディタで編集してください。
編集後は`# systemctl restart postgresql`で再起動して設定を読ませましょう。

### MastodonでアクセスするPostgreSQLのユーザーとデータベースを作成します。
※以下は公式ドキュメントにはない作業も含まれています。

まず、デフォルトでの文字コードが`LATIN1`になっているので、これを`UTF-8`に変更します。

```
# systemctl stop postgresql
# pg_dropcluster -stop 12 main
# export LANGUAGE="en_US.UTF-8"
# export LANG="en_US.UTF-8"
# export LC_ALL="en_US.UTF-8"
# systemctl start postgresql
# pg_createcluster -e UTF8 -start 12 main
```
次にユーザーとデータベースを作成します。
公式ドキュメントにはユーザーの作成しかありませんが、データベースも作成しないと後々エラーが出て動きません。

```
# sudo -u postgres psql
psql (12.2 (Ubuntu 12.2-4))
Type "help" for help.

postgres=# CREATE USER mastodon CREATEDB;

postgres=# CREATE DATABASE mastodon_production WITH ENCODING ‘UTF8’ OWNER mastodon;
```
行末の`;`の付け忘れに注意してください。
これでmastodonで使うユーザーとデータベースが作成できました。文字コードも含めて確認してみます。

```
postgres=# \l
                                       List of databases
        Name         |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges
---------------------+----------+----------+-------------+-------------+-----------------------
 mastodon_production | mastodon | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 postgres            | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 template0           | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
                     |          |          |             |             | postgres=CTc/postgres
 template1           | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
                     |          |          |             |             | postgres=CTc/postgres
(4 rows)
```
Nameがデータベース名、Ownerがユーザー名、Encodingが文字コードです。ちゃんと作成できてます。
ここではデータベース名、ユーザー名ともに公式のものを使用してます。
確認ができたので`\q`でrootに戻りましょう。

```
postgres=#\q
```

# やっとこさMastodonのインストール
長かったですが、ここでやっとMastodonをサーバーにインストールします。
まずはrootから`mastodon`ユーザーへ切り替えます。

```
# su - mastodon
```
以後、しばらく`mastodon`ユーザーにて作業します。

### GitからMastodonのソースをコピーしてインストール
Githubに公開されているリポジトリからMastodonを`~/live`にコピーし、最新安定版をインストールします。

```
$ git clone https://github.com/tootsuite/mastodon.git live && cd live
$ git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)
$ bundle config deployment 'true'
$ bundle config without 'development test'
$ bundle install -j$(getconf _NPROCESSORS_ONLN)
$ yarn install --pure-lockfile
```
※記事を作成している20/5/25現在ではv3.1.4が最新のため、`$ git checkout v3.1.4`としても構いません。
※チェックアウトをしないと`master`という開発途中の不安定なバージョンになります。
　最新版は`git tag`でも確認できます。数字の一番大きな、rcのついていないものが最新安定版です。
　必ず安定版をチェックアウトするようにしましょう。

```
ex.) v3.1.0rc1　と　v3.0.1　とでは、v3.0.1　が最新安定版になります。
```

### Mastodonの構成ファイルの作成
1問1答のチャート式で回答していけば構成ファイルが自動的に作成されます。
間違えたり、この時点で不明であったりしても修正は可能の為、分かる範囲で回答していきましょう。

```
$ RAILS_ENV=production bundle exec rake mastodon:setup

Your instance is identified by its domain name. Changing it afterward will break things.
Domain name:<ドメイン名を入力>

Single user mode disables registrations and redirects the landing page to your public profile.
Do you want to enable single user mode? (y/N)
<お一人様仕様で使いますか？ y か n　を入力>

Are you using Docker to run Mastodon? (Y/n)
<DockerでMastodon動かしてますか？　ここでは n を入力>

PostgreSQL host: (/var/run/postgresql)
<データベースのホストの場所指定。ここではそのままEnter>

PostgreSQL port: (5432)
<データベースのポート番号指定。ここではそのままEnter>

Name of PostgreSQL database: (mastodon_production)
<データベース名の指定。デフォルトで進めてればそのままEnter>

Name of PostgreSQL user: (mastodon)
<PostgreSQLのユーザー名の指定。デフォルトで進めてればそのままEnter>

Password of PostgreSQL user:
<PostgreSQLユーザーのパスワード。今回は設定していないのでそのままEnter>

Redis host: (localhost)
<Redisのホストの場所指定。ここではそのままEnter>

Redis port: (6379)
<Redisのポート番号指定。ここではそのままEnter>

Redis password:
<Redisのパスワード。これも設定してないのでそのままEnter>

Do you want to store uploaded files on the cloud? (y/N)
<メディアファイル等をAmazon S3等のクラウドサービスへアップロードしますか？ y　か　n　を入力>
※ここで "y" を選択すればアクセスキー等の設定へ進みます。

Do you want to send e-mails from localhost? (y/N)
<mastodonサービス上でユーザーへ送信されるメールはローカルホストから送りますか？ y　か　n　を入力>
※通常はGmailはMailgunなどのサービスを使うので "n" になると思います。

SMTP server:
SMTP port:
SMTP username:
SMTP password:
SMTP authentication:
SMTP OpenSSL verify mode:
E-mail address to send e-mails "from":
Send a test e-mail with this configuration right now? (Y/n)
※localhostの質問で "n" と回答すると上記の質問がなされます。
　利用するメールサービスに合わせて入力してください。
　最後の質問はテストメールを送るか？です。必要に応じて回答してください。

This configuration will be written to .env.production
Save configuration? (Y/n)
<今までの設定を構成ファイルに書き込みますか？ ここは "y" でいいです>

Now that configuration is saved, the database schema must be loaded.
If the database already exists, this will erase its contents.
Prepare the database now? (Y/n)
<データベースの最適化を行いますか？ Y/nどちらでも構いません。どちらにしろ最終的には実行します>

The final step is compiling CSS/JS assets.
This may take a while and consume a lot of RAM.
Compile the assets now? (Y/n)
<アセットの作成を実行しますか？ Y/nどちらでも構いません。どちらにしろ最終的には実行します>

Do you want to create an admin user straight away? (Y/n)
<管理者アカウントを作成しますか？ Y/nどちらでも構いません。なくても動きますし、後でも作れます>
```
お疲れさまでした。これで構成ファイルの作成は終了です。
また、このチャート中に間違えてしまった場合は`~/live/.env.production`を編集することで修正が可能です。
構成ファイルの中身の詳細については[公式ドキュメント](https://docs.joinmastodon.org/admin/config/)を参照しつつ編集してください。
編集するとしたらCloudStorage関連かSMTP関連になるかと思います。

構成ファイルの作成が完了したら`mastodon`ユーザーを出てrootに戻ります。

# リバースプロキシの設定と証明書の取得
今のままではMastodonをWebに公開することはできません。（Localhostでなら動くはずです）
引き続き、Nginxの設定とLet'sEncrypt証明書の取得をやっていきます。

### NginxのConfigファイルの作成、設定
設定ファイルのテンプレートをコピーしてきます。

```
# cp /home/mastodon/live/dist/nginx.conf /etc/nginx/conf.d/mastodon.conf
※公式ドキュメントとは別の場所へコピーしています。これが正しいかどうかはわかりませんがわかりやすさ優先です。
```
これでNginx設定ファイルのテンプレのコピーができました。
エディタを使ってコピー先の`/etc/nginx/conf.d/mastodon.conf`を編集していきます。

```
# vim /etc/nginx/conf.d/mastodon.conf

※example.comをドメインに変更します (4箇所)
　また、2箇所のsslを一時的に消去します。

server {
  listen 80;
  listen [::]:80;
  server_name example.com;                                                    ※ここ
  root /home/mastodon/live/public;
  location /.well-known/acme-challenge/ { allow all; }
  location / { return 301 https://$host$request_uri; }
}                                                                                                                      
server {
  listen 443 http2;                                                           <-sslを消す
  listen [::]:443 http2;                                                      <-sslを消す
  server_name example.com;                                                    ※ここ

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  # Uncomment these lines once you acquire a certificate:
  # ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;      ※ここ
  # ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;        ※ここ

```
エディタはvim使ってますが自分の使いやすいもので大丈夫です。置換を使えば楽勝です。
編集が終えたら保存し、`systemctl reload nginx`で設定を読み込ませましょう

```
# systemctl reload nginx
```
合わせて、設定ファイルが間違っていないかの確認をします。

```
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
```
`test is successful`となっていればOKです。
上記設定ファイルで`ssl`を消しておかないとエラーがでます。
これも公式ドキュメントには説明がなく、悩みました。


次に証明書の取得をします
公式ドキュメントでは

```
# certbot --nginx -d <yourdomain>
```
となっています。私もこの方法で取得しました。
が、どうもNginxの設定も弄ってしまうらしく？取得後にウェブからドメインへアクセスできませんでした。
Nginxの設定ファイルをテンプレからコピーし直し、必要な箇所を再設定して事なきを得てます。

試してはいませんが

```
# certbot-auto certonly --webroot -w /home/mastodon/live/public/ -d <yourdomain>
```
としたほうが良いのかもしれません。これについては未検証です。

ということで、証明書の取得が無事にできたらNginxの設定ファイルを再度編集します。

```
# vim /etc/nginx/conf.d/mastodon.conf

server {
  listen 80;
  listen [::]:80;
  server_name <yourdomain>;
  root /home/mastodon/live/public;
  location /.well-known/acme-challenge/ { allow all; }
  location / { return 301 https://$host$request_uri; }
}                                                                                                                      
server {
  listen 443 ssl http2;                                                       <-sslを書き戻す
  listen [::]:443 ssl http2;                                                  <-sslを書き戻す
  server_name <yourdomain>;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  # Uncomment these lines once you acquire a certificate:
  ssl_certificate     /etc/letsencrypt/live/<yourdomain>/fullchain.pem;      ※先頭のコメントアウトを消す
  ssl_certificate_key /etc/letsencrypt/live/<yourdomain>/privkey.pem;        ※先頭のコメントアウトを消す
```
編集が済んだら、再度設定を読み込ませ、確認します。

```
# systemctl reload nginx

# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
```
これでWebからアクセスができるようになったはずです。
この時点でブラウザからアクセスすると象さんがパソコンをバンバンしてるはずです。
バンバンしていない場合は、構成ファイル作成の最後でデータベースの最適化とアセットの作成をしていないかもしれません。
その場合は下記の手順のあと、試してみてください。

```
# su - mastodon
$ cd live
$ RAILS_ENV=production bundle exec rails db:migrate　　※初回は少し時間がかかります
$ RAILS_ENV=production bundle exec rails assets:precompile　　※毎回少し時間がかかります
$ exit　　※mastodonユーザーを抜けrootに戻ります。
```
# mastodonサービスの登録と起動
これが最後の工程です。お疲れさまでした。
サーバー起動時にmastodonも自動起動するように設定します。

```
# cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/

# ls /etc/systemd/system
mastodon-sidekiq.service    mastodon-streaming.service    mastodon-web.service
※色々ファイルがあると思いますが、この3つがあることを確認。あればOK

# systemctl daemon-reload
# systemctl start mastodon-web mastodon-sidekiq mastodon-streaming
# systemctl enable mastodon-web mastodon-sidekiq mastodon-streaming
```
以上で完了です。
ブラウザでドメインへアクセスするとアカウント登録/ログイン画面が表示されているはずです。
私はここで自分のアカウントを登録し、確認メールが届くかも合わせてテストしました。
構成ファイル作成時に確認済みであれば問題ないでしょう。

最後に後から管理者アカウントの作成法だけ書いて終わります。

# 管理者アカウントの作成
tootctlを使って作成します。
前提条件として、アカウントが1つ以上（基本的に自分の）ある必要があります。

```
# su - mastodon
$ cd live
$ RAILS_ENV=production bundle exec accounts modify <youraccount> --role admin
OK
```
最後にOKと出れば完了です。ユーザー設定画面に "管理" が見えるようになってるはずです。

# さいごに
参考させていただいた下記のサイトをまとめておきます。
大変お世話になりました。
https://docs.joinmastodon.org/admin/install/
http://crows.tokyo/post-1410/
https://qiita.com/kumasun/items/870769d7db4d95cde238
https://qiita.com/n350071/items/58a60b0fc557784b0be0