Edited at

Mastodon を CentOS にインストールする (Docker未使用)


Mastodon

Mastodon はイエナ大学出身の24歳のドイツ人 Eugen Rochko によって2016年10月に公開された Rails アプリケーションで、フロントエンドには ReactRedux が使用されている。

Docker を利用せずに CentOS 7 に Mastodon をインストールする手順をまとめた。当該手順は Mastodon 1.1.2 時点の内容のため、最新版では当初と異なる部分があるので注意すること。


ドキュメント

Mastodon 関連情報まとめ」を参照。


検証環境


  • VirtualBox 5.1.18


    • CentOS 7.3.1611 (minimal)


      • Node.js 6.10.2 (nodesource)


        • Yarn 0.23.2 (yarn)



      • PostgreSQL 9.6.3 (pgdg96)

      • Redis 3.2.3 (epel)

      • rbenv 1.1.0 (GitHub)


        • Ruby 2.4.1 (rbenv)


          • Bundler 1.14.6 (gem)





      • nginx 1.12.0 (nginx)

      • ImageMagick 6.7.8.9 (base)

      • FFmpeg 2.8.11 (rpmfusion-free-updates)






環境構築

yum clean all && yum -y update && reboot


ImageMagick

yum install ImageMagick

ImageMagick は画像処理に利用される。

remi リポジトリで 6.9.8.3 が、公式サイトで 7.0.5-4 の rpm パッケージが入手可能だが、最新である必要はないと思われるのでメンテナンスのしやすい base リポジトリの 6.7.8.9 で充分だろう。


FFmpeg

yum install https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm

yum install ffmpeg

FFmpeg は動画処理に利用される。

rpmfusion-free-updates リポジトリから 2.8.11 が、nux-dextop リポジトリから 2.6.8 がインストールできる。

また John Van Sickle のサイトから最新バージョンの 3.3 などのスタティックビルドも入手可能。


Redis

yum -y install epel-release && yum install redis

systemctl start redis && systemctl enable $_

Redis はデータ構造サーバーを実装する NoSQL データベース。一般的に全てのデータはメモリ上に格納される。1

Sidekiq で要求される。

remi リポジトリが最新安定版の 3.2.8 を、 RHSCL が rh-redis32 パッケージで 3.2.4 を提供しているが、epel の 3.2.3 でも充分だろう。

ちなみに remi-test リポジトリでベータ版最新の 4.0.0 RC2 が試せる。


PostgreSQL

yum install https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm

yum install yum-utils
yum-config-manager --enablerepo=pgdg96
yum install postgresql96-{contrib,devel,server}
ln -s /usr/pgsql-9.6/bin/* /usr/local/bin/
postgresql96-setup initdb
systemctl start postgresql-9.6 && systemctl enable $_

base リポジトリだと 9.2 がインストールされるので、パフォーマンスが影響するか不明だが公式リポジトリから最新安定版の 9.6 をインストール。


DB ユーザーの作成

sudo su - postgres -c 'psql -c "CREATE USER mastodon CREATEDB;"'


pg_stat_statements の有効化

sed -i.org '/shared_preload_libraries/ s/^#//' /var/lib/pgsql/9.6/data/postgresql.conf

sed -i "/shared_preload_libraries/ s/''/'pg_stat_statements'/" /var/lib/pgsql/9.6/data/postgresql.conf
sed -i "/shared_preload_libraries/a pg_stat_statements.track = all" /var/lib/pgsql/9.6/data/postgresql.conf
systemctl restart postgresql-9.6

デフォルトでは PgHeroQuery stats must be enabled for slow queries と warning されるので pg_stat_statements モジュールをロードする。この際に postgresql96-contrib パッケージに含まれる pg_stat_statements.so が必要。

詳細は PgHero のガイド Query Stats に説明がある。


nginx

cat << "_EOF_" > /etc/yum.repos.d/nginx.repo

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1
_EOF_
yum install nginx

nginx は Web サーバー。他に Apache HTTP Serverlighttpd などがある。

epel リポジトリで 1.10.2 がインストールできるが、公式リポジトリがあるのでそちらで最新安定版の 1.12.0 をインストールする。


Node.js

curl -sL https://rpm.nodesource.com/setup_6.x | bash

yum install nodejs

Node.js® は、Chrome の V8 JavaScript エンジン で動作する JavaScript 環境。

公式では 4.x を提示しているが、LTS 最新安定版の 6.10.2 をインストールしても今のところ問題は見当たらない。

7.x や 8.x もあるようだが Nightly Build のようだ。


Yarn

curl -sL https://dl.yarnpkg.com/rpm/yarn.repo -o /etc/yum.repos.d/yarn.repo

yum install yarn

Yarn は npm に代わる Node パッケージマネージャー。

package.json に記述されたパッケージおよびそれに依存関係のあるパッケージを自動インストールする。

公式リポジトリから最新安定版の 0.23.2 がパッケージインストール可能。


Ruby

yum install bzip2 gcc-c++ git {openssl,readline,zlib}-devel

useradd mastodon
su - mastodon
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src && cd ~
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile && source ~/.bash_profile
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
rbenv install 2.4.1 && rbenv global $_ && rbenv rehash

Ruby のパッケージインストールは RHSCL の rh-ruby23 パッケージで Ruby 2.3.1 までしかインストールできないので、公式ドキュメントに従って Ruby のバージョン管理ツール rbenv で最新安定版の Ruby 2.4.1 をインストールする。他に RVM がある。

CPU にもよると思うが rbenv install は7分程度かかった。

本番環境で rbenv 使うのどうなの的な意見も見受けられるが、Ruby に詳しくないので具体的にどのようなデメリットがあるのかはよく分からない。


Bundler

gem install bundler

Bundler は Gem パッケージマネージャー。

Gemfile ファイルに記述されたパッケージおよびそれと依存関係のあるパッケージを自動インストールする。


Mastodon


インストール

cd ~ && git clone https://github.com/tootsuite/mastodon.git live && cd live

git checkout $(git tag | tail -n 1)
bundle install --deployment --without development test
yarn install

時間帯にもよると思うが bundle install は4~6分程度、yarn install は2分程度かかった。

公式ドキュメントを確認すると git checkout が追加されている。


設定

cp .env.production.sample .env.production

sed -i '/^REDIS_HOST=/ s/redis/localhost/' .env.production
sed -i '/^DB_HOST=/ s/db//' .env.production
sed -i '/^DB_USER=/ s/postgres/mastodon/' .env.production
sed -i '/^DB_NAME=/ s/postgres/mastodon/' .env.production
sed -i '/^LOCAL_DOMAIN=/ s/example.com/192.168.56.101:3000/' .env.production
sed -i '/^LOCAL_HTTPS=/ s/true/false/' .env.production
sed -i "/^PAPERCLIP_SECRET=$/ s/$/`rake secret`/" .env.production
sed -i "/^SECRET_KEY_BASE=$/ s/$/`rake secret`/" .env.production
sed -i "/^OTP_SECRET=$/ s/$/`rake secret`/" .env.production

pg_hba.conf がデフォルトの状態だと DB_HOSTlocalhost127.0.0.1 を指定すると PG::ConnectionBad: FATAL: ユーザ"mastodon"のIdent認証に失敗しました というエラーが出て DB に接続できない。空欄にするか pg_hba.conf を書き換える必要がある。

LOCAL_DOMAIN および LOCAL_HTTPS はいったんローカル確認のために一時的に変更。


セットアップ

bundle exec rails db:setup RAILS_ENV=production

bundle exec rails assets:precompile RAILS_ENV=production


サービス化


mastodon-web (Puma)

cat << "_EOF_" > /etc/systemd/system/mastodon-web.service

[Unit]
Description=mastodon-web
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="PORT=3000"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec puma -C config/puma.rb
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_

Puma は Ruby アプリケーションサーバー。

他に Unicorn, Rainbows!, Passenger, Raptor (Passenger 5), TorqueBox, Thin などがあるらしい。


mastodon-sidekiq (Sidekiq)

cat << "_EOF_" > /etc/systemd/system/mastodon-sidekiq.service

[Unit]
Description=mastodon-sidekiq
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=5"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_

Sidekiq は Rails アプリで非同期処理を行うためのライブラリ。2

他に ResqueDelayed::Job などがあるらしい。


mastodon-streaming

cat << "_EOF_" > /etc/systemd/system/mastodon-streaming.service

[Unit]
Description=mastodon-streaming
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="NODE_ENV=production"
Environment="PORT=4000"
ExecStart=/usr/bin/npm run start
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_


サービスの起動と自動起動設定

systemctl daemon-reload && \

systemctl start mastodon-{web,sidekiq,streaming} && \
systemctl enable $_


SELinux

setenforce 0

SELinux は Linux のカーネルに強制アクセス制御 (MAC) 機能を付加するモジュール。3

とりあえず検証ということで一時無効に。

ConoHa の CentOS 7 テンプレートではデフォルトで無効になっていた。

コメント欄での情報提供4によると m.kagucho.net で利用されている SELinux のポリシーが Gist で公開されているとのこと。


firewalld

firewall-cmd --permanent --add-service={http,https} && firewall-cmd --reload

firewalld はファイアーウォールデーモン。RHEL 7 から iptables の代わりにデフォルトでインストールされるようになった。5


Query stats の有効化

sudo su - postgres -c 'psql -d mastodon -c "CREATE extension pg_stat_statements;"'

sudo su - postgres -c 'psql -d mastodon -c "SELECT pg_stat_statements_reset();"'


動作確認

firewall-cmd --add-port=3000/tcp

3000番ポートを一時的に開けて http://192.168.56.101:3000/ などで動作しているか確認。

firewall-cmd --reload

リロードして3000番ポートを閉じる。 6


管理者設定

RAILS_ENV=production bundle exec rails mastodon:make_admin USERNAME=admin

RAILS_ENV=production bundle exec rails mastodon:confirm_email USER_EMAIL=admin@example.com

mastodon:make_adminUSERNAME は任意のアカウント名を指定する。

mastodon:confirm_email でメール送信なしにアカウントの有効化が行える。


Let's Encrypt

yum install certbot

certbot certonly --standalone -d example.com


設定

cat << "_EOF_" > /etc/nginx/conf.d/mastodon.conf

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

server {
listen 80;
listen [::]:80;
server_name example.com;
return 301 https://$host$request_uri;
}

server {
listen 443 ssl;
listen [::]:443 ssl;
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;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
ssl_stapling on;
ssl_stapling_verify on;

keepalive_timeout 70;
sendfile on;
client_max_body_size 0;

root /home/mastodon/live/public;

gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
# add_header Content-Security-Policy "default-src 'none'; style-src 'self'; script-src 'self' https://example.com; img-src 'self' https://example.com; connect-src 'self' wss://example.com; font-src 'self'; frame-ancestors 'none'; media-src 'self';";

location / {
try_files $uri @proxy;
}

location @proxy {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Proxy "";
proxy_pass_header Server;

proxy_pass http://127.0.0.1:3000;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

tcp_nodelay on;
}

location /api/v1/streaming {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Proxy "";

proxy_pass http://localhost:4000;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

tcp_nodelay on;
}

error_page 500 501 502 503 504 /500.html;
}
_EOF_

systemctl start nginx && systemctl enable $_


Cronjobs

cat << "_EOF_" | crontab -

RAILS_ENV=production
@daily cd /home/mastodon/live && /home/mastodon/.rbenv/shims/bundle exec rake mastodon:daily > /dev/null
_EOF_

Cronjobs が mastodon:daily 1つに統合された模様。


メール設定

VALUE-DOMAIN での Mailgun の DNS 設定」などを参考に。


セキュリティ対策

Linuxセキュリティ監査ツール Lynis」がお手軽なので参考に。


参照情報