メドレー開発本部の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" "http://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」などのプロダクトを提供しています。これらのサービスの拡大を受けて、その成長を支えるエンジニア・デザイナーを募集しています。
メドレーで一緒に医療体験を変えるプロダクト作りに関わりたい方のご連絡お待ちしております。