HAProxy で graceful restart する方法

haproxy には起動後に設定ファイルを読み込み直したりする機能がないので、バランス先を追加するなどの変更が無停止ではできない、と思い込んでいたのだけど実は違った、というお話。

実際、同一プロセスで読み込み直すことはできないのだけども、以下のような手法で graceful に再起動することができる。man はちゃんと読みましょう。

# haproxy -f /path/to/haproxy.conf -sf [既に動いているhaproxyのpid]

として -sf オプションに pid を渡して起動すると……

  • haproxy[2] : 起動
  • haproxy[2] : 既に動いている haproxy[1] に SIGTTOU を送信
  • haproxy[1] : SIGTTOU を受けると Listen をやめる
    • 新規接続は受け付けない
    • 既に確立している接続はそのまま維持
  • haproxy[2] : socket を Listen し、新しい接続を受け入れる状態に
  • haproxy[2] : haproxy[1] に SIGUSR1 を送信
  • haproxy[1] : SIGUSR1 を受けると、既存の接続が全て終了するのを待って停止 (graceful shutdown)

これで既存の接続を落とすことなく、新しい設定で起動した haproxy に動作を引き継ぐことができます。賢いですね。

ちなみに自分のところでは daemontools から起動していたのだけど、これだと上記の手法が使いづらい。別プロセスとして起動して、一時的には平行して 2プロセスが動作する必要があるため。

contrib から init script を貰ってきて、それから起動するように変更。

この変更も無停止でなんとか。

# svstat /service/haproxy                     # pidを調べる
# echo $HAPROXY_PID > /var/run/haproxy.pid    # pidファイル作成
# mv /service/haproxy /service/.haproxy       # daemontools から起動しないように
# svc -x /service/.haproxy
# service haproxy reload                      # reload で -sf オプション付き起動
# rm /service/.haproxy

ということで、設定変更に再起動が必要なのがちょっと不便……というのは勘違いでした。これでより安心してバリバリ使えますね!

【2015-02-10追記】
この手法、生きていたプロセスがlistenをやめてからあとのプロセスがlistenするまでの一瞬、新規の接続できない(既存接続は問題ない)タイミングがあるのですが、最近のLinux kernelでは SO_REUSEPORT を使うことで瞬断のタイミングを0にできるそうです。
haproxy の優雅な再起動 - diary.sorah