nginx
passenger
mastodon

Phusion PassengerでMastodonを運用する

More than 1 year has passed since last update.

Mastodonを「お一人様インスタンス1」で運用して気になることの一つに、「もっと消費メモリを圧縮できないかな……」ということがあると思います。

そこでPhusion Passenger2

Mastodonを立ち上げる際の選択肢として言及はされているものの、その方法自体は見掛けない気がするこのPassengerに載せ替えてみたところ、どうやら良い感じになりました。

以下は私が実際に運用している「お一人様インスタンス」で、負荷に応じてPassengerが適当にWorkerを増減させてくれている様子(が見える消費メモリの推移)です。

Mastodon with Passenger

メモリ1GBの手軽なVPSでも心の余裕がうまれるグラフです:-)

この構成では結局Mastodonだけでサーバを占有している状態ですが、他のアプリケーションなどと同居させる場合に威力を発揮しそうです。

この記事では、Production guideに従った環境、すなわちDockerを利用していない、mastodon-web及びmastodon-streamingをPassenger(+nginx)に置き換える方法を扱います。

Mastodonの立ち上げ方そのものについては、先達が沢山いらっしゃるのでそちらが参考になると思います。


前提条件



  • Production guide(a3097c9)に準拠する形でCentOS7.3上に構築3、利用可能な状態のMastodon(v1.3.3)を出発点にしています

  • 以降の手順のコマンドは、全てmastodonユーザとして実行します


    • 設定ファイルの編集コマンド等は省略しています




既にPassengerの準備ができている場合

以下の項目がお役に立つかも知れません


Passengerインストール

Installing Passenger + Nginx on Red Hat 7 / CentOS 7 (with RPM) - Passenger Libraryといった手順がありますが、この方法では異なるバージョンのRubyが不必要にインストールされてしまったり、ChromeでHTTP/2が有効にならなかったりします。

Production guideに従うとrbenvを持ったmastodonユーザーがいるはずですので、今回はこれを利用してGemインストール+ビルド、という手段をとることにします。


gemインストール

gemを利用してpassengerをインストールします。

cd ~/live

gem install passenger --no-ri --no-rdoc


ソースコード準備

ビルドに利用するnginx、opensslのソースコードを用意します。


  • nginxのバージョンは、Passenger5.1.3から1.10.3が推奨されていますので、それ以降が良いと思います

  • HTTP/2(ALPN)に対応するには、opensslのバージョンは1.0.2以上が必要です

cd /usr/local/src

curl -L -O https://nginx.org/download/nginx-1.12.0.tar.gz
sudo tar xofz nginx-1.12.0.tar.gz
curl -L -O https://www.openssl.org/source/openssl-1.1.0e.tar.gz
sudo tar xofz openssl-1.1.0e.tar.gz


ビルド用パッケージ準備

libcurl-develをインストールします。

他のディストリビューションの場合、読み替えて同等のパッケージを導入してください。

sudo yum install -y libcurl-devel


ビルド

以下のコマンドを実行してビルドします。

この環境では単純にsudoしてgemインストールしたコマンドを利用することはできないため、冗長な形になっています。

また、環境に応じて次の箇所を適宜修正した上で実行してください。



  • versions/2.4.1 rbenvでインストールしたRubyのバージョン


  • passenger-5.1.3 gemでインストールしたPassengerのバージョン


  • --prefix=/opt/nginx これからビルドするnginxのインストール先


  • --nginx-source-dir nginxのソースコードの場所


  • --with-openssl opensslのソースコードの場所

sudo PATH=/home/mastodon/.rbenv/versions/2.4.1/bin:$PATH \

/home/mastodon/.rbenv/versions/2.4.1/bin/ruby \
/home/mastodon/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/passenger-5.1.3/bin/passenger-install-nginx-module --auto \
--prefix=/opt/nginx \
--nginx-source-dir=/usr/local/src/nginx-1.12.0 \
--extra-configure-flags="--with-ipv6 --with-openssl='/usr/local/src/openssl-1.1.0e'"

それなりに時間が掛かるので待ちます。

正常終了するとNginx with Passenger support was successfully installed.と表示されます。

インストール完了


設定ファイルを準備

既存のnginxから設定を適宜移行します。

上記の手順でnginxをインストールした場合、デフォルトで読み込まれる設定ファイルがconfディレクトリ下になっていることに注意してください。

一通り移行したところで、設定に誤りが無いことを確認しておきます。

sudo /opt/nginx/sbin/nginx -t


出力結果

nginx: the configuration file /opt/nginx/conf/nginx.conf syntax is ok

nginx: configuration file /opt/nginx/conf/nginx.conf test is successful


systemd対応

NGINX systemd service file | NGINXを参考にファイルを作成し、今回インストールしたnginxをsystemctlで操作できるようにします。

環境に応じて、PIDFileの場所及び/opt/nginxを適宜修正してください。

また、PrivateTmptrueとすると、そのままでは「Passenger Instance Registry」を取得することが難しくなり後々困るため、ここではfalseにしておきます。


/etc/systemd/system/nginx-passenger.service

[Unit]

Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/opt/nginx/sbin/nginx -t
ExecStart=/opt/nginx/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=false

[Install]
WantedBy=multi-user.target



Passengerで動作させるためのアプリケーション準備


babel-nodenode

Passengerではbabel-node相当の機能が無さそうなため、事前にビルドする必要があります。

package.jsonscripts内にbuildを追記します。


/home/mastodon/live/package.json

{

...
"scripts": {
...
"start": "babel-node ./streaming/index.js --presets es2015,stage-2",
"build": "babel streaming -d build --presets es2015,stage-2",
"storybook": "start-storybook -p 9001 -c storybook",
...
}
...
}

コマンドを実行し、ビルドします。

cd ~/live

npm run build

build/index.jsが出力されます。


.env.production

試した限りでは、どうしてもうまく.env.productionの内容を反映させられませんでした。

やむを得ませんので、build/index.jsを修正し.env.productionの絶対パスを記述して対応します。


/home/mastodon/live/build/index.js修正前

_dotenv2.default.config({

path: env === 'production' ? '.env.production' : '.env'
});


/home/mastodon/live/build/index.js修正後

_dotenv2.default.config({

path: env === 'production' ? '/home/mastodon/live/.env.production' : '.env'
});


Passenger設定

詳細はリファレンスを読みましょう。

ここでは、各ディレクティブ毎に最低限必要な設定を記述していきます。

各設定ファイルは環境に応じて適宜読み替えてください。


httpディレクティブ


/opt/nginx/conf/nginx.conf

http {

passenger_root /home/mastodon/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/passenger-5.1.3;
passenger_ruby /home/mastodon/.rbenv/versions/2.4.1/bin/ruby;
passenger_log_file /var/log/nginx/passenger.log;
passenger_max_pool_size 4;
passenger_max_instances_per_app 3;
}


passenger_root passenger_ruby

インストール時に自動的に追記されているはずです。

設定ファイルを他のnginxなどから移行してきた場合、これらを追記します。


passenger_log_file

Mastodonアプリケーションのログが出力される先になります。

設定しない場合、これらはnginxのerror_logに出力されるため、設定しておいた方が良いと思います。


passenger_max_pool_size passenger_max_instances_per_app

それぞれ、サーバ全体で許可するアプリケーションWorkerインスタンスの最大数、及び各アプリケーション毎のWorkerインスタンスの最大数です。

ここがチューニングの要ですが、ひとまず適当な値を入れておきます。

(後述の動作状態の確認を参照)

max_pool_sizeのデフォルト値は6ですが、メモリが1GBしか無いような場合超えてしまいますので下げておきます。

max_instances_per_appは、max_pool_size以下で各アプリケーションにうまく割り振られる値を検討します。

今回は、streaming用は1つあれば十分ですので、max_pool_size - 1としておきます。


serverディレクティブ - web


passenger_enabled passenger_app_group_name

port443をlistenしているserverディレクティブ内に追記します。

root /home/mastodon/live/public;の上あたりが間違いが無いと思います。


/opt/nginx/conf.d/Mastodon用.conf

server {

passenger_enabled on;
passenger_app_group_name web;
root /home/mastodon/live/public;
}


リバースプロキシ設定を削除

pumaに対するリバースプロキシ設定は不要となるため、削除またはコメントアウトします。

Production guideの設定例では以下の部分が該当します。


削除対象

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ディレクティブ - streaming

nodeに対するリバースプロキシ設定は不要となるため、削除またはコメントアウトします。

Production guideの設定例では以下の部分が該当します。


削除対象

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;
}


新しい設定は以下の通りです。


/opt/nginx/conf.d/Mastodon用.conf

location /api/v1/streaming {

root /home/mastodon/live;
passenger_enabled on;
passenger_app_group_name streaming;
passenger_app_type node;
passenger_startup_file /home/mastodon/live/build/index.js;
tcp_nodelay on;
}

一通り記述が済みましたら、設定に誤りが無いことを確認しておきます。

sudo /opt/nginx/sbin/nginx -t


出力結果

nginx: the configuration file /opt/nginx/conf/nginx.conf syntax is ok

nginx: configuration file /opt/nginx/conf/nginx.conf test is successful


既存のサービス類を停止

この時点から、元の環境ではアクセスできなくなります。

元々試用していたnginxを停止します。

sudo systemctl stop nginx

sudo systemctl disable nginx

mastodon-web及びmastodon-streamingを停止します。

sudo systemctl stop mastodon-web

sudo systemctl stop mastodon-streaming
sudo systemctl disable mastodon-web
sudo systemctl disable mastodon-streaming


Passenger + nginxを起動

systemd対応で設定した名前を使用して、起動します。

start/restart/reloadなど、nginxの場合と同様に実行できます。

sudo systemctl enable nginx-passenger

sudo systemctl start nginx-passenger

ブラウザからMastodonにアクセスします。

エラー無く利用できれば、作業完了です。お疲れさまでした:-)


動作状態の確認

passenger-statusコマンドを利用すると、Passengerの動作状態を確認できます。

cd ~/live

passenger-status


出力結果

Version : 5.1.3

Date : 2017-05-07 07:49:31 +0900
Instance: dqfV6I6e (nginx/1.12.0 Phusion_Passenger/5.1.3)

----------- General information -----------
Max pool size : 4
App groups : 2
Processes : 2
Requests in top-level queue : 0

----------- Application groups -----------
streaming:
App root: /home/mastodon
Requests in queue: 0
* PID: 27481 Sessions: 2 Processed: 4 Uptime: 1h 2m 27s
CPU: 0% Memory : 21M Last used: 30m 26s a

web:
App root: /home/mastodon/live
Requests in queue: 0
* PID: 28733 Sessions: 0 Processed: 3 Uptime: 30m 26s
CPU: 0% Memory : 120M Last used: 23m 8s ago


必要に応じて、PassengerによりwebのWorkerインスタンスが増減します。


出力結果2

Version : 5.1.3

Date : 2017-05-07 07:51:07 +0900
Instance: dqfV6I6e (nginx/1.12.0 Phusion_Passenger/5.1.3)

----------- General information -----------
Max pool size : 4
App groups : 2
Processes : 4
Requests in top-level queue : 0

----------- Application groups -----------
streaming:
App root: /home/mastodon
Requests in queue: 0
* PID: 27481 Sessions: 2 Processed: 9 Uptime: 1h 4m 3s
CPU: 0% Memory : 21M Last used: 2s ago

web:
App root: /home/mastodon/live
Requests in queue: 0
* PID: 28733 Sessions: 0 Processed: 10 Uptime: 32m 2s
CPU: 0% Memory : 120M Last used: 3s ago
* PID: 29839 Sessions: 0 Processed: 1 Uptime: 6s
CPU: 2% Memory : 46M Last used: 3s ago
* PID: 29849 Sessions: 0 Processed: 0 Uptime: 3s
CPU: 0% Memory : 4M Last used: 3s ago


ここでは詳細は割愛しますが、この状態を見ながら環境に合わせて設定をチューニングすることになると思います。


バージョンアップ

基本はProduction guideの通りですが、以下の作業が追加となります。


streaming/index.json

このファイルに修正が入った場合、ビルド作業及びビルド後ファイルへの修正が必要です。


アプリケーション再起動

リバースプロキシで運用していたときと同様、アプリケーションはメモリ上で保持されているため再起動するまで更新は反映されません。

次のコマンドで、Passengerが読み込んでいるMastodonを再起動できます。

cd ~/live

passenger-config restart-app /home/mastodon/live


元に戻したくなった……

元のnginxを削除していない場合、今回インストールしたPassenger+nginxを停止し、再びsystemctlで設定すれば元に戻るはずです。

Passengerが不要になった場合、以下の手順で削除できます。

ファイルパスは適宜読み替えてください。

sudo rm /etc/systemd/system/nginx-passenger.service

sudo rm -rf /opt/nginx
cd ~/live
gem uninstall passenger
git checkout package.json
rm -rf build