読者です 読者をやめる 読者になる 読者になる

続 カッコの付け方

AWSを始めとしたクラウドコンピューティング全般と、唯一神emacsにおける()の付け方についてだらだら書きます

lsyncdで双方向同期するなら、delete='running' がいい

Linux lsyncd

lsyncdを使って、2台のサーバ上のファイルを双方向からの同期を検証していた時に、予期せぬファイル削除が発生しました。その謎と対処方法について迫ります。

lsyncdで双方向同期とは?

lsyncdは自分の特定のディレクトリを見張り、変更(ファイルの追加、変更、削除等)のイベントが発生した場合、それをトリガーに他のサーバへ伝搬したりすることができます。しかし、lsyncdはあくまでも自サーバのファイル変更を捕まえるだけなので、相手のサーバに起こった変更はわかりません。

そこで、例として2台のサーバでお互いにlsyncdをかけることにより、2台が全く同じ状況を作ります。下図のような構成です。(絵はAWSですが、別にAWSに限った話ではないです。)

f:id:iga-ninja:20150228225202p:plain

lsyncインストール

CentOS 6系の場合は、下記を参考にインストールできます。

CentOS6.5でlsyncdを動かす - Qiita

このエントリにも記載があるように、initスクリプトがミスっていますので、修正が必要です。/etc/lsyncd.confに設定ファイルを置く場合は、ここの修正は不要ですが、上記を参考にアレンジしましょう。

設定ファイル

lsyncd は 2.1 系で大きく設定ファイルが変更されています。 2.0系の設定ファイルはまず動かないと思います。ここはマニュアル読むしか無いです(日本語の2.1系の情報は少ないと言われています。)

Manual to Lsyncd 2.1.x · axkibe/lsyncd Wiki · GitHub

とりあえず、設定例を載せておきます。

settings {
   logfile = "/var/log/lsyncd/lsyncd.log",
   statusFile = "/var/log/lsyncd/lsyncd-status.log",
   statusInterval = 20
}

sync {
    default.rsync,
    delay = 0,
    source="/tmp/lsync-test",
    target="[user]@[相手ホスト]:/tmp/lsync-test/",
    rsync = {
        rsh = "/usr/bin/ssh -i [ssh-key] -o StrictHostKeyChecking=no"
    }
}

/tmp/lsync-test にファイルを書いたり消したりして、動作を確認しましょう。問題無いことが分かります。(循環が発生しそうですが、大丈夫でした)

障害時を想定し、片系を止める

ためしに図の サーバA を止めてみます。すると、LBはすべてのリクエストをサーバBに流れます。サーバBに対して書き込まれたファイルは、lsyncd によりサーバAに伝搬しようとしますが、Aは死んでいるので受け取れません。その時lsyncdはどうなるのか。

答えは、リトライを繰り返すです。BはAが死んでいても、思いを伝えようと、何度も何度もリトライします。健気です、切ないです。。

f:id:iga-ninja:20150228225252p:plain

ですが、この動作を利用すれば、このままAが復帰した暁には、Bの思いはAに届きそうです。やったね!サーバB!

無慈悲な現実、サーバAが復帰すると、サーバBのファイルが消された!

なんと無残な!この事象の発生メカニズムはこうです。

  1. Aが死んだ
  2. Bにリクエストが全部行く、これにより AとBに差分が生まれた
  3. BはAに同期を通うとするが、Aは死んでいるので受け取れない
  4. Aが復帰した
  5. B->Aの同期が届く前に A->B の同期が発生した場合、 2.で生じた差分が、DeleteとなってBに伝搬する!

f:id:iga-ninja:20150228225319p:plain

5については、B->Aの同期が早く届く場合もあるので、100%ではありませんが、実際に確認することは出来ました。これを何とかしましょう。

delete='running'を使おう!

Lsyncd 2.1.x ‖ Layer 4 Config ‖ Default Behavior · axkibe/lsyncd Wiki · GitHub

上記にデフォルトの動作として delete=true のかわりに delete="running" とすれば、スタートアップ時の削除は抑制できるよーと書いてあります。早速適応して、試すとうまく行きました。

settings {
   logfile = "/var/log/lsyncd/lsyncd.log",
   statusFile = "/var/log/lsyncd/lsyncd-status.log",
   statusInterval = 20
}

sync {
    default.rsync,
    delay = 0,
    source="/tmp/lsync-test",
    target="[user]@[相手ホスト]:/tmp/lsync-test/",
    delete="running",
    rsync = {
        rsh = "/usr/bin/ssh -i [ssh-key] -o StrictHostKeyChecking=no"
    }
}

めでたしめでたし。

と・・・油断させといて・・・

実はこれで終わりではないです。確かにファイル喪失という最悪の事態は回避できますが、今度はB単独時に削除されたファイルを復帰させてしまう可能性があります。

  1. A,B 同期が取れてる状態で、x.txt というファイルがあった
  2. A死亡
  3. Bでx.txtを削除
  4. A復帰
  5. A が x.txt をBに伝搬!

これを抑制するには init = false を指定しましょう、これもマニュアルに書いてあり、扱いは要注意とのことです。