upstart, pty-keeper, reptyr, socat - ターミナルアプリをリモートサーバでデーモン化する方法(Earthquakeをサーバで実行するようにした)
試行錯誤で6日かかったンゴ……普通に嵌ってしんどかった……。1 release/dayが途絶えて残念なり……。一応、毎日勤勉に取り組んでたんですけどねぇ。でも最後はかなりシンプルになって良かった。似たような方法でターミナルアプリは全て同じ方法でデーモン化して接続できるようになると思います。
例はEarthquakeです、というか、Earthquakeをデーモン化するためにいろいろ調べた。
要件
- Earthquakeをデーモン化したい
- ローカルから1コマンドで接続
- ssh認証したい
1は先日event_chainとsave_imageってEarthquakeプラグイン作ったので、サーバに常駐させときたいなぁということです。ローカルはラップトップなので閉じてることあるから。
2は基本的には常時接続なんですが、やっぱ気が向いた時にすぐ見れなきゃまずいっしょ。
3は外から誰でも接続できると嫌なんで。危ない。
ソリューション
とりあえず、ざっくり解決法書きます。解説は後で。
リモートサーバ
reptyrとsocatをインストールします。UbuntuでやってるのでOSに合わせて適度に読み替えてください。
$ sudo apt-get install reptyr $ sudo apt-get install socat
reptyrは別のpty(pseoudo tty)で動いてるプロセスを手元のptyで乗っ取るソフトウェアです。 socatはnetcatの高機能版みたいなもんで、ネットワーク用途に限らない色々なことができます。今回はコマンド実行して終了するんじゃなくて、リモートサーバへの接続を維持するために使ってます。
そして、拙作のpty-keeperをダウンロードします。
$ wget https://gist.github.com/babie/7297810/raw/ae3793ae39b3165e0c50f7ca4a05fb00921a9cdd/pty-keeper $ chmod +x pty-keeper
適当な場所に置いてね。
ptyを持たせて子プロセスを起動しながら、自分から子プロセスのIOへ読み書きができなくなっても、子プロセスが生きている限りは死なない、っつーソフトウェアが見つからなかったので作りました。
要点は、PTY.spawn
でpty付きでspawnするのと、SIGCHLD
,SIGCLD
は子プロセスが死んだ時だけじゃなくて、ptyが変更された時なんかも飛んでくるので、ホントに死んだかどうか確かめるってとこっすね。汎用的にしなければ10行です。
んで、Upstart用設定ファイル/etc/init/earthquake.conf
を作ります。
これでサーバ起動時に自動で立ち上がります。ユーザーやパスは環境に合わせて適度に改変してください。
HOME
はEarthquakeが設定ディレクトリを探すために、LANG
はEarthquakeへ文字入力するときに日本語が化けるのを防ぐのに、COLUMNS
はpty及びreadlineがデフォルト80を採用しそれ以上の入力すると強制的に行頭に戻されてつらいので、PATH
はEarthquake及びpty-keeperがRubyを探すために必要です。
--no-daemon
オプションを付けてるのは、upstartはプロセスIDを追跡する記述があって、expect fork
はfork一回、expect daemon
はfork二回という風に対応してるのですが、RubyのProcess.daemon
はupstartが追えないプロセスIDの変わり方してるみたいなんで付けざるを得ませんでした。pty-keeper自体はconsole none
でどのtty/ptyにも接続してないので問題ありません。
プロセスの起動・終了を何に任せるかはいろんな方法がありますよね。
昔ながらのstart-stop-daemon
使って/etc/init.d/earthquake
書いてたんだけど、ほら、sysvinitってrespawn(プロセスを監視して自動再起動)ないじゃないっすかー。んでその後、daemon
使ってやってたんすけど、--pty
オプションがうまく使えなかった。これデバッグ用でupstart(init)は元々pty持ってないからかな?んでググると、なんとUbuntuでは今upstart
が普通っぽいじゃないですかー。こいつはrespawnもうまく捌いてくれるしかっこいいなということでこれ使いました。
最初は直接earthquakeを呼び出すearthquake.conf書いてたんですが、デーモン化はうまくいくんですが、文字入力がダメ。なんか行単位のバッファになってるみたいなんすよね。socatのオプションでicanon=0
やraw
なんかを試したんですがうまくいきませんでした。
次にemptyを介したearthquake.confを書いたんですが、これはデーモン化も文字入力も問題なかったんですけど、earthquakeプロセスが2つできちゃう。デフォルトのEarthquake使うならこれでも良かったんですが、私が使ってるプラグインにタイムラインを監視してイベントに応じてアクションするってのがあるんすよね。具体的にはfavった画像つきツイートの画像を保存するって奴なんですが、画像が2個保存されて宜しくない。どうしてプロセス2つになるかというと、reptyrがpty乗っ取ったと同時に読み書きできる子プロセスがいなくなったemptyが自殺してupstartが再起動かけるって具合です。このempty、Ubuntuでのパッケージ名がempty-expectとなっているように、ptyを生成してコマンドを起動してそいつと対話するexpectなんですわ。なので子プロセス自体が生きていてもIOが読み書きできなくなったら速やかに死ぬようになってんですね。そもそもの目的がそれなんだからしょうがない、他の用途に使う俺が悪かった。
あ、この設定ファイル、密かにrbenv環境のupstart設定の書き方にもなってますね。
手動で立ち上げるときは
$ sudo start earthquake
です。この辺はinitctl
辺りを調べてください。
ローカルホスト
.zshrcに以下を追記
別にaliasでもいいと思うんですけど、私はwhich
ですぐ見れるようにfunctionにしてます。設定再読み込みも忘れずに。
moshじゃなくてsshなのは、moshは接続を安定させるために複数のコネクションを張るので、プロセスが複数実行されて悲しいことになります。
socatは2つのブロックの引数を取り、STDIN
がこっちの標準入力、SYSTEM
があちらでshを実行することを意味してます。
STDIN
のオプション、echo=0
は文字が二重に表示されるのを防ぐため、icanon=0
は非カノニカルモードにして行単位ではなく文字単位で書き込みするために使ってます。詳しくはTERMIOSを読んで下さい。
SYSTEM
は引数なしのコマンドを実行するならEXEC
でもいいんですが、今回は引数に加えてshの実行も必要だったのでこれにしました。オプションのpty
はリモートサーバでpty作るかどうか、stderr
はSTDERRもSTDOUTにリダイレクトしてくれるやつ、ignbrk=1
はこれまたTERMIOS関連でシグナルのC(Ctrl-c)を無視するかどうかですね。どっちにしろsocat/reptyrの終了でearthquakeも再起動するんで今回はあんま意味ないんですが。
reptyrにオプション-s
を付けてますが、これはプロセスにtty/ptyが接続されてない時に使うもので不要なんですが、オプションなしの既存のptyを乗っ取るより、オプションありの新しくpty作って繋ぎ変える方が速くて安定してるみたいなので付けました。
あかんかったわ。-sオプションつけると複数行や長文の入力に難あり。取り除いたら無事できました。遅延じゃろか。
これで、
$ earthquake
とすればリモートで動いているEarthquakeに接続しTwitterを楽しめます。
次回予告
はてなダイアリーに予約投稿がなくてつらいので何とかしたいと思います。んだけど、MacBook Proが届いて環境設定しないとアカンのでちょっと遅れるかも。