Go で書いたサーバーを管理するには circus が便利

Go を使うとサーバーとアプリケーションの境界が無くなり、アプリケーションサーバーを書けるようになります。 それは良いことなのですが、アプリケーションを書く人が、従来サーバーを書く人が設計していた機能を理解して実現できないと、運用できないサーバーができあがる結果になってしまいます。

例えば Apache は、 master, worker プロセスが分離していて、設定変更を反映させるときなどは新しい worker を作ってから古い worker を殺すことで、サービスを一瞬も止めずに worker を再起動していました。これを graceful restart と呼びます。

Go で 1024 以下のポートを Listen するアプリを作る で触れたとおり、 Go はプロセス管理システムを作るのには少し向いていない面がありますし、せっかくアプリケーションプログラマーが簡単にサーバーを書けるようになったのにアプリを書くたびに面倒なことはしたくありません。

そこで、 daemontools や supervisord のようなプロセス管理ツールの一つである circus がお勧めです。 Circus はもともと、プロセス管理ツールが master プロセスを管理して、 master プロセスが worker プロセスを管理する、という2重構造を簡素化して、直接 worker プロセスを管理できるように設計されています。 (参考: supervisord と circus の構成の比較)

Circus は、前の記事でサンプルコードを紹介した「socket を bind, listen したうえで、 setuid し、子プロセスに fd を渡す」といった機能を最初から提供している上に、 reload コマンドは新しいプロセスを起動してから古いプロセスを終了するという動作をします。(再起動するのではなく SIGHUP を送る設定もあります) プログラムを再起動しても bind, listen している socket はずっと circus が持ち続けているので、プログラム側は circus から受け取った fd を使うだけで graceful restart が実現できるのです。

前の記事のサンプルを、 circus で動かすには次のように書きます。

[circus]
statsd = 1

[watcher:echo-go]
cmd = ./server_go -fd $(circus.sockets.echo)
use_sockets = True
uid = ユーザー名
gid = グループ名

[socket:echo]
host = 0.0.0.0
port = 80
このブログに乗せているコードは引用を除き CC0 1.0 で提供します。