daemontools とか supervisor とか pm2 とか forever とか foreman とか systemd で同じコマンドを複数のプロセスでサービスにする

これまでスクリプトをデーモン化するために daemontools をよく使っていたのですが、同じコマンドを複数プロセス起動させたいときに煩雑というか、そもそもこのやりかたあってんの? って思ったので、代替になりそうなものをいくつか試しました。

例として、w1.shw2.sh の 2 つのサービスを、w1.sh は 2 プロセス、w2.sh は 3 プロセス起動したいものとします。

daemontools

  • http://cr.yp.to/daemontools.html
  • 定番
  • 下記の SRPM から入れるとインストールが簡単
  • 1サービス=1プロセスが基本
    • 複数プロセスを起動したければその数だけサービスを定義する必要がある
    • もしくはサービスとして起動したプロセスでさらにプロセスマネージャーみたいにするか
# インストール
sudo yum -y install rpm-build
wget http://mirrors.qmailtoaster.com/daemontools-toaster-0.76-1.3.6.src.rpm
rpmbuild --rebuild daemontools-toaster-0.76-1.3.6.src.rpm
sudo rpm -ivh ~/rpmbuild/RPMS/x86_64/daemontools-toaster-0.76-1.3.6.x86_64.rpm

# systemd のユニットファイル
sudo tee /etc/systemd/system/daemontools.service <<EOS
[Unit]
Description = daemontools
After = network.target

[Service]
ExecStart = /command/svscanboot
Restart = always
Type = simple

[Install]
WantedBy = multi-user.target
EOS

# daemontools を起動
sudo systemctl daemon-reload
sudo systemctl start daemontools
sudo systemctl status daemontools
sudo systemctl enable daemontools

# サービスの設定
sudo mkdir /service/.w1-1
sudo mkdir /service/.w1-2
sudo mkdir /service/.w2-1
sudo mkdir /service/.w2-2
sudo mkdir /service/.w2-3

sudo ln -sf /vagrant/w1.sh /service/.w1-1/run
sudo ln -sf /vagrant/w1.sh /service/.w1-2/run
sudo ln -sf /vagrant/w2.sh /service/.w2-1/run
sudo ln -sf /vagrant/w2.sh /service/.w2-2/run
sudo ln -sf /vagrant/w2.sh /service/.w2-3/run

sudo touch /service/.w1-1/down
sudo touch /service/.w1-2/down
sudo touch /service/.w2-1/down
sudo touch /service/.w2-2/down
sudo touch /service/.w2-3/down

sudo ln -sf .w1-1 /service/w1-1
sudo ln -sf .w1-2 /service/w1-2
sudo ln -sf .w2-1 /service/w2-1
sudo ln -sf .w2-2 /service/w2-2
sudo ln -sf .w2-3 /service/w2-3

# サービスを開始する
sudo svc -u /service/*

# サービスの一覧表示
sudo svstat /service/*

# サービスの再起動
sudo svc -t /service/*

# サービスを停止する
sudo svc -d /service/*

# サービスを無効にする
sudo svc -d /service/* && sudo svc -x /service/* && sudo rm /service/*

supervisor

  • http://supervisord.org/
  • 定番
  • epel から yum でインストールできる
  • 設定ファイルの numprocs でプロセス数を指定可能
# インストール
sudo yum -y install supervisor

# supervisor 起動
sudo systemctl start supervisord.service
sudo systemctl status supervisord.service
sudo systemctl enable supervisord.service

# サービスの設定
sudo tee /etc/supervisord.d/app.ini <<EOS
[program:w1]
command=/vagrant/w1.sh
process_name=%(program_name)s-%(process_num)d
numprocs=2
autostart=false

[program:w2]
command=/vagrant/w2.sh
process_name=%(program_name)s-%(process_num)d
numprocs=3
autostart=false
EOS

# 反映
sudo supervisorctl update

# サービスの開始
sudo supervisorctl start all

# サービスの再起動
sudo supervisorctl restart all

# サービスの停止
sudo supervisorctl stop all

pm2

  • http://pm2.keymetrics.io/
  • 基本的には Node.js のアプリ用
    • だけど Node.js 以外にも使える
  • 設定ファイルの instances でプロセス数を指定可能
  • モニタとかデプロイ?とか多機能?
    • 使わなさそう
  • 一般ユーザーで pm2 コマンド実行するとデーモンが立ち上がってしまう
    • ~/.pm2 が pm2 のデータディレクトリになっているため
# インストール
sudo yum install nodejs npm
sudo npm install -g pm2

# systemd のユニットとして登録
sudo pm2 startup systemd

# サービスの設定
cat <<'EOS'> app.json
[
  {
    "name"      : "w1",
    "script"    : "w1.sh",
    "exec_mode" : "fork_mode",
    "instances"  : "2"
  },
  {
    "name"      : "w2",
    "script"    : "w2.sh",
    "exec_mode" : "fork_mode",
    "instances"  : "3"
  }
]
EOS

# サービスの登録と開始
sudo pm2 start app.json

# サービスの一覧表示
sudo pm2 list

# サービスの停止
sudo pm2 stop all

# サービスの開始
sudo pm2 start all

# サービスの再起動
sudo pm2 restart all

# サービスの削除
sudo pm2 delete all

forever

  • https://github.com/foreverjs/forever
  • 基本的には Node.js のアプリ用
    • だけど Node.js 以外にも使える
  • 1サービス=1プロセスが基本
    • 複数プロセスを起動したければその数だけサービスを定義する必要がある
    • もしくはサービスとして起動したプロセスでさらにプロセスマネージャーみたいにするか
# インストール
sudo yum install nodejs npm
sudo npm install -g forever

# サービスの設定
cat <<'EOS' > forever.json
[
  {
    "command": "/bin/bash",
    "script": "w1.sh"
  },
  {
    "command": "/bin/bash",
    "script": "w1.sh"
  },
  {
    "command": "/bin/bash",
    "script": "w2.sh"
  },
  {
    "command": "/bin/bash",
    "script": "w2.sh"
  },
  {
    "command": "/bin/bash",
    "script": "w2.sh"
  }
]
EOS

# サービスの開始
sudo forever start forever.json

# サービスの一覧表示
sudo forever list

# サービスの再起動
sudo forever restartall

# サービスの停止
sudo forever stopall

foreman

  • https://ddollar.github.io/foreman/
  • 他の類似ツールの設定ファイルをエクスポートできる
    • supervisord とか upstart とか systemd とか
  • コマンドラインオプションでプロセス数を指定可能
  • respawn はしない
    • 複数プロセスのどれかが死ぬと全部死ぬ
# インストール
sudo yum -y install ruby rubygems
sudo gem install foreman

# サービスの設定
cat <<EOS> Procfile
w1: ./w1.sh
w2: ./w2.sh
EOS

# サービスの開始(Ctrl+C で終了)
foreman start -c -m w1=2,w2=3

# いろいろなサービス管理ツールの設定にエクスポート
foreman export -c w1=2,w2=3 supervisord ./supervisord
foreman export -c w1=2,w2=3 systemd ./systemd
foreman export -c w1=2,w2=3 upstart ./upstart

systemd

  • systemd でもテンプレートユニットを使えば同じコマンドを複数プロセス起動できそう
sudo tee /etc/systemd/system/app.target <<EOS
[Unit]
StopWhenUnneeded = true
Wants = \
  w1.target \
  w2.target

[Install]
WantedBy = multi-user.target
EOS

sudo tee /etc/systemd/system/w1.target <<EOS
[Unit]
StopWhenUnneeded=true
PartOf = app.target
Wants = \
  w1@1.service \
  w1@2.service
EOS

sudo tee /etc/systemd/system/w2.target <<EOS
[Unit]
StopWhenUnneeded=true
PartOf = app.target
Wants = \
  w2@1.service \
  w2@2.service \
  w2@3.service
EOS

sudo tee /etc/systemd/system/w1@.service <<EOS
[Unit]
Description = w1
After=network.target
PartOf = w1.target

[Service]
ExecStart = /vagrant/w1.sh
Restart = always
Type = simple

[Install]
WantedBy = multi-user.target
EOS

sudo tee /etc/systemd/system/w2@.service <<EOS
[Unit]
Description = w2
After=network.target
PartOf = w2.target

[Service]
ExecStart = /vagrant/w2.sh
Restart = always
Type = simple

[Install]
WantedBy = multi-user.target
EOS

sudo systemctl daemon-reload

# サービスの開始
sudo systemctl start app.target

# サービスの一覧
sudo systemctl list-units "w[12]@*"

# w1 だけ再起動
sudo systemctl restart w1.target

# サービスを停止
sudo systemctl stop app.target

まとめ

  • daemontools はたくさん設定するのが辛い
  • supervisor は良さそう
  • pm2 と forever は Node.js って感じある
    • Node.js 以外でも使えるけどあんまり使われてなさそうな、雰囲気を感じる
  • foreman は respawn しないのが辛そう
    • systemd が foreman を respawn するにせよ
    • 1 プロセス死んだだけで全部が再起動されるのは過剰では?
    • そもそも他とくらべてツールの系統が違う
      • foreman はシステムに 1 つのサービスマネージャーってわけではない
      • 他はシステムに 1 つのサービスマネージャーって感じ
      • 次のような複数の foreman を起動する構成を考えるとこれはこれでありかも?
        • systemd
          • foreman (serviceA) -> serviceA
          • foreman (serviceB) -> serviceB
      • 他のツールだと次のようになって systemd から見ると 1 つのサービスに見える
        • systemd
          • supervisor
            • serviceA
            • serviceB
  • systemd でできるなら systemd でもいいんじゃないか?
    • テンプレートユニットや PartOf や Wants を使えばなんとかなりそう
    • プロセスの数だけ Wants に羅列する必要があるのは辛いか

ほかのツールみたいなおもしろ機能なないけれども(WebUI とか)、どうせ systemd は居るわけなので、systemd でやるのがよいだろうか。