fstaidというHAデーモンを作った

fstaidというHAデーモンを作りました。

github.com

HeartbeatとかCorosyncとかPacemakerの代替を考えて作ったソフトウェアです。

なぜ作ったのか?

それほど多くはないんですがたまに「先方の許可するIPアドレスは1つしかないから、フェイルオーバー時にElasticIPを付け替える感じで」という案件がたまにあるんですよ。 そうすると「Corosync/Pacemakerでクラスタ組むか。だるいな…」ということになって、Heartbeat/Corosync/Pacemakerを使いたくない欲が高まった結果、作成されました。 KeepalivedSerfを使ってクラスタを組むこともできるんですが、どうもやりたいことに対して、オーバーキル感とか明後日の方向感がぬぐえない感じでいました。

話は変わってMHAです。 MHAいいですよね。安定した動作もレプリケーションの付け替えもすばらしいんですが

  • 監視対象外の第三者サーバによるモニタリング
  • セカンダリチェック
  • CLIツール群による設定・環境まわりのチェック
  • フェイルオーバー完了後の起動ロック

などなど、「ああ、わかってる…」感がすごく好きでした。 fstaidにはMHAにインスパイアされたこれらの設計を取り入れた(つもり)です。

サーバ構成イメージ

使い方

以下のような感じで起動します。

fstaid -config fstaid.toml

設定ファイルは以下の通り。

[global]
port = 8080
interval = 1
maxattempts = 3
#lockdir = "/tmp"
#log = "/var/log/fstaid.log"
#mode = "debug"

[handler]
command = "/usr/libexec/fstaid/handler.rb"
timeout = 300

[primary]
command = "curl -s -f server-01"
timeout = 3

[secondary]
command = "curl -s -f -x server-02:8080 server-01"
timeout = 3

[self]
command = "curl -s -f 169.254.169.254/latest/meta-data/instance-id"
timeout = 3

[[user]]
userid = "foo"
password = "bar"

動作

  1. 2〜4までを一定時間ごとに繰り返す
  2. self-checkを実行→失敗したら即終了
  3. maxattemptsまでprimary-checkを実行。すべて失敗したら4に進む
  4. secondary-checkを実行。失敗したら5に進む
  5. 監視対象を死亡と見なして、ハンドラスクリプトを実行
  6. フェイルオーバーが完了。ロックファイルを作成してfstaidは終了 (ロックファイルを削除しないと、起動できない)

self-checkの必要性が微妙な感じなんですが、今のところ

  • self-checkでsecondary-checkの踏み台サーバとの疎通を確認
  • secondary-checkで踏み台サーバ経由で監視対象の動作を確認(ssh、http proxy等々)
  • primary-checkで監視対象の動作を直接確認

とするといいんじゃないかと考えております。

ハンドラ

ハンドラはこんな感じです。

#!/usr/bin/env ruby
# The handler is called when both the primary check and the secondary check fail

primary_exit_code   =  ARGV[0].to_i
primary_timeout     = (ARGV[1] == 'true')
secondary_exit_code =  ARGV[2].to_i
secondary_timeout   = (ARGV[3] == 'true')


def failover
  # to fail over
end

if primary_exit_code != 0
  if secondary_timeout
    # Nothing to do if the secondary-check times out
    exit 1
  end

  failover
elsif primary_timeout
  # Nothing to do if the primary-check times out
  exit 2
end

Rubyで書いてますが、実行できればシェルスクリプトでもなんでも。

  • ARGV[0]: primary-checkの終了コード
  • ARGV[1]: primary-checkがタイムアウトしたか否か
  • ARGV[2]: secondary-checkの終了コード
  • ARGV[3]: secondary-checkがタイムアウトしたか否か

ここに、ElasticIPの付け替えとか、ENIの付け替えとかを仕込むとよいかと。

その他

curl localhost:8080/failをたたくことで、手動でフェイルオーバーを実行することもできます。

まとめ

今のところ、まだそのような案件が降ってきていないので、まだ実戦投入はできていないのですが、つらみが出てきそうなら積極的に使っていこうかと考えています。 あるいは、まだ知らない便利ソリューションがあるとしたら是非知りたいところですが…