Mastodon
Mastodon はイエナ大学出身の24歳のドイツ人 Eugen Rochko によって2016年10月に公開された Rails アプリケーションで、フロントエンドには React と Redux が使用されている。
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)
- Ruby 2.4.1 (rbenv)
- nginx 1.12.0 (nginx)
- ImageMagick 6.7.8.9 (base)
- FFmpeg 2.8.11 (rpmfusion-free-updates)
- Node.js 6.10.2 (nodesource)
- CentOS 7.3.1611 (minimal)
環境構築
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
デフォルトでは PgHero に Query 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 Server や lighttpd などがある。
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_HOST
に localhost
や 127.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
他に Resque や Delayed::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_admin
の USERNAME
は任意のアカウント名を指定する。
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」がお手軽なので参考に。
参照情報
-
http://dev.classmethod.jp/server-side/ruby-on-rails-sidekiq/ ↩
-
http://qiita.com/bezeklik/items/1a8530d530613acd665c#comment-4f330b87df7725888f04 ↩
-
https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Using_Firewalls.html#sec-Comparison_of_Firewalld_to_system-config-firewall_and_iptables ↩
-
Chrome だとセッションが維持されるのか、ポートを閉じて再読み込みしても表示される場合がある。 ↩