runit が便利なので、使い方を紹介した話〜メドレー TechLunch〜
メドレー開発本部の nakatani です。
開発本部で定期的に開催している勉強会「TechLunch」で、runitという unix のプロセススーパバイザについてお話しました。 その内容について紹介させていただきます。
runit 自体は特に目新しい技術ではなく(Linux の busybox に収められていたりする枯れた技術です)、大して難しい話題でもありません。
ただ、個人的には便利に使っている手放せないツールであり、もしスーパバイザというものの存在を知らずに使わずにいる人がいると勿体無いなあという思いから、TechLunch のテーマとして取り上げた次第です。
runit とはなんなのか
プロセスをデーモンとして立ち上げて、プロセスが死んでも再度起動し続けてくれるツール郡です。C 言語で開発されています。
Linux などの unix ではたいてい標準で init, Upstart, Systemd, launchd などのスーパバイザが組み込まれています。 runit はそれらと同じような位置づけのものです。
qmail の作者であるdjbが作ったdaemontoolsの後継のプロダクトです。
runit があると何が便利なのか
■ マシンが起動しているかぎり、プロセスを動作させ続けることができる
マシンを立ち上げたあとに、起動コマンドを叩いたり、プロセスが落ちたときに再起動をする必要がありません。 また、フォアグラウンドで動作するプロセスを起動した後に、端末を切り離す操作をする必要もありません。
ただ、これは他のスーパバイザでも同じことが実現できます。
■ その場しのぎで作ったスクリプトを、ほぼそのままデーモン化できる
Shell, Ruby, Perl, Haskell どのような言語で作ったスクリプトであっても、 シェルなどで実行可能なファイルがあれば、それをそのままデーモンとして実行することができます。
■ スクリプトの標準出力をそのままログファイルとして扱うことができる
svlogd というプログラムが、デーモンの標準出力をログ化し、ローテーションなどの面倒も見てくれます。 自作のデーモンが思った通りに動かない際のデバッグが容易です。
macOS への導入方法
macOS に導入するための手順を記載します。詳しくはスライドや runit のドキュメントなどを理解して使うようにしてください。
Xcode や Homebrew を macOS に導入していることが前提です。
/service
を root の runit ディレクトリとして設定する手順
$ brew install runit # macports feels better.
$ sudo mkdir /service
$ sudo cat <<EOF |sudo tee /Library/LaunchDaemons/runit.plist
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"https://www.apple.com/DTDs/PropertyList-1.0.dtd" >
<plist version='1.0'>
<dict>
<key>Label</key><string>runit</string>
<key>ProgramArguments</key>
<array>
<string>sh</string><string>-c</string>
<string>PATH="/usr/local/sbin:/usr/local/bin:$PATH"
exec '/usr/local/bin/runsvdir' '/service'
</string>
<string>;</string>
<string>--pid=exec</string>
</array>
<key>Debug</key><false/><key>Disabled</key><true/><key>KeepAlive</key><true/>
</dict>
</plist>
EOF
$ launchctl load -w >/Library/LaunchDaemons/runit.plist
$ launchctl list | grep runit
自作スクリプトをデーモンにする手順
$ cd /service/
$ sudo mkdir -p hello/log # log を同時につくると runsvdir が log の準備もする
$ cd hello
$ sudo cat <<EOF |sudo tee run
#!/usr/bin/env ruby
# 自作スクリプト
while true
puts("hello ruby #{Time.now.to_i % 100}");
STDOUT.flush();
sleep(1);
end
EOF
$ sudo cat <<EOF |sudo tee log/run
#!/bin/sh
exec svlogd -ttt .
EOF
$ sudo chmod 755 run log/run
$ tail -F log/current # ログが見られる。
$ sudo sv st . # daemon の状態を確認する。
run: .: (pid 35517) 46s; run: log: (pid 34727) 456s
$ sudo sv st /service/hello/ # ディレクトリの指定方法は自由。
$ sudo sv t . # TERM シグナルを daemon に送る
$ sudo sv st .
run: .: (pid 35589) 1s; run: log: (pid 34727) 470s # 起動時間が 1s になってる。
まとめ
結局のところ、「使えばわかるし使わないと便利さがよくわからない」というのが正直なところです。 そのため、TechLunch においては、使うための手順を時間をかけて解説をするようにしました。
みなさんも興味があれば、ぜひ導入して使ってみてください。
僕自身、開発マシンである macbook pro に runit を入れて、開発環境の Ruby や MongoDB, Elasticsearch, Nginx などのサーバ群、 定期的に動かしたいちょっとしたスクリプトなどを runit で管理しています。
異なる設定のサーバ群を一つのマシンに同居させる場合も、設定ファイルを分けて別ポートで立ち上げたりしています。
以前のプロジェクトでは本番環境を runit で構築したこともありますし、今のプロジェクトでも、たまったゴミデータを削除し続けるスクリプトを runit で対応してそのまま放置(放置しても OK なくらいメンテナンスフリー)していたりします。
最近はクラウドやコンテナ技術が活況であり、環境を抽象化しようという流れがあります。しかしながら、そもそもプロセスや UNIX OS 自体が環境を抽象化するための技術群です。そういった基本的な技術と仲良くすることで、物事がシンプルになることがあるのではないかと考えたりしながら、日々開発に取り組んでいます。
お知らせ
メドレーでは、医療介護の求人サイト「ジョブメドレー」、医師たちがつくるオンライン医療事典「MEDLEY」、口コミで探せる介護施設の検索サイト「介護のほんね」、オンライン診療アプリ「CLINICS」などのプロダクトを提供しています。これらのサービスの拡大を受けて、その成長を支えるエンジニア・デザイナーを募集しています。
https://www.medley.jp/recruit/creative.html
メドレーで一緒に医療体験を変えるプロダクト作りに関わりたい方のご連絡お待ちしております。