/* カラム名を変更しています 20110524 */
@kamipo さんが正座して待っているのを思いだした。
GreenBucketsで、ノードがダウンした時の動作と復旧方法です。GreenBuckets自体の動作実績はないのであくまで想定です。ただ、mixiの画像クラスタの構成をまねているので復旧方法もほぼ同じかもです
まず、障害が起きて、復旧するまでの間を次の3段階にわけて対応を考えます
- 障害が発生し、アラート検知、運用者が対応するまで
- 運用者が対応を行い、一時復旧
- データの整合性がとれ、完全復旧
■ 障害が発生し、アラート検知、運用者が対応するまで
さて、HDDが破損するなどしてサーバがダウンした場合、運用者が対応を行うまで、GreenBucketsは障害の影響をなるべく表に出さないよう、動作します
上の2つの図はノード1がダウンした状態を示しています。オブジェクトを取得する際(左上)に、ノード1にアクセスしても正常にレスポンスが得られないので、同じグループの次のノードへアクセスします。(もちろんmurmurhashを使ってsortした順です) オブジェクトを保存する際(右上)は、ノード1へPUTが失敗するので、ノードを切り替えて保存します。もしこの際グループ1の他のノードにオブジェクトを保存していた場合は、JobQueueへ削除を依頼します。
下は3つめのノードがダウンしていた場合です、GreenBucketsはオンラインで2つのコピーを作成し、残りをJobQueueに依頼しますが、JobQueueでコピーに失敗した場合にもグループを変更してコピーし直します。コピーが正しくできたところで、MySQLにグループ変更を記録します。
このように、GreenBucketsはノードに障害があった場合でも、ノード数が足りていれば機能的には問題がなく動くようになっています。しかし、Retryを行うためにレスポンスが遅くなる可能性があるため、障害検知後、運用者が問題の起きたノードをサービスから切り離す必要があります。(監視ツールから自動で切り離すこともできるかもしれません)
■ 運用者が対応を行い、一時復旧するまで
運用者による対応が2つ目の段階です。
GreenBucketsのノード管理テーブルは以下のようになっています
mysql> select * from nodes;
+----+-----+--------------------+--------+-------+--------+
| id | gid | node | online | fresh | remote |
+----+-----+--------------------+--------+-------+--------+
| 1 | 1 | http://127.0.0.11/ | 1 | 1 | 0 |
| 2 | 1 | http://127.0.0.12/ | 1 | 1 | 0 |
| 3 | 1 | http://127.0.0.13/ | 1 | 1 | 0 |
| 4 | 2 | http://127.0.0.14/ | 1 | 1 | 0 |
| 5 | 2 | http://127.0.0.15/ | 1 | 1 | 0 |
| 6 | 2 | http://127.0.0.16/ | 1 | 1 | 0 |
| 7 | 3 | http://127.0.0.17/ | 1 | 1 | 0 |
| 8 | 3 | http://127.0.0.18/ | 1 | 1 | 0 |
| 9 | 3 | http://127.0.0.19/ | 1 | 1 | 0 |
+----+-----+--------------------+--------+-------+--------+
各ノードには、onlineとfreshという2つのフラグがあります(remoteは別の機会に)。これを用いてノードが有効かどうかを設定します。onlineがついていれば、読み出し可能であり、onlineとfreshの両方がついていれば新規オブジェクトの追加ができることを示しています。
ノードが落ちている場合、そのノードのonlineを0にすれば、該当ノードオブジェクトへの取得リクエストは停止されます。また、保存もできなくなるので、レプリカ数の3を満たさなくなり、ダウンしたノードが属するグループへオブジェクト追加は行われなくなります
mysql> BEGIN;
mysql> UPDATE nodes SET online = 0 WHERE id = 3;
+----+-----+--------------------+--------+-------+--------+
| id | gid | node | online | fresh | remote |
+----+-----+--------------------+--------+-------+--------+
| 3 | 1 | http://127.0.0.13/ | 0 | 1 | 0 |
+----+-----+--------------------+--------+-------+--------+
mysql> COMMIT;
これで、ノードダウンへの一時対応は完了です。もし夜間に携帯電話に起こされたのであれば、再びベッドに戻って寝てしまっても大丈夫です。レプリカ数が3以上の場合はデータは2重化されているので、データ復旧の対応は次の営業日で問題がないはずです。
■ データの整合性がとれ、完全復旧するまで
最後にデータの復旧です。これには運用者がなれたツールを使います。
ダウンしたノードの代わりとなる新規のサーバを用意し、同じグループのノードからデータを単純にコピーします。各グループのノードには同じデータが格納されているので、これだけで復旧できます。
コピーする際はtar と ssh を組み合わせるのが個人的には好みです
127.0.0.13 $ tar cf - | ssh 新サーバ 'cd /path/to/htdocs && tar xf -'
ssh の代わりに、 nc を使うのもいいかもしれませんね。数百GBから数TBのコピーには数日掛かるかもしれませんので、screen等を使ってterminalが落ちても問題がないようにしておくのがおすすめです。
コピーが終わったら MySQL のノード管理テーブルをアップデートして完全復旧です
mysql> BEGIN;
mysql> UPDATE nodes SET node='http://新サーバ/', online = 1 WHERE id = 3;
+----+-----+--------------------+--------+-------+--------+
| id | gid | node | online | fresh | remote |
+----+-----+--------------------+--------+-------+--------+
| 3 | 1 | http://新サーバ/ | 1 | 1 | 0 |
+----+-----+--------------------+--------+-------+--------+
mysql> COMMIT;
ディスクバッファがない状態でノードを戻すと、負荷が急上昇することが考えられます。その際は、freshを落とした上で(新しいオブジェクトがこない)、負荷をみつつonlineをつけたり、外したりを繰り返してみると対応できるかもしれません。ここはなんか考えたいところ。
最後にオブジェクトの削除について
GreenBucketsのオブジェクトの削除については、わりと緩やかに作ってあります。物理ファイルの削除は必ずJobQueueを経由しますが、ノードにDELETEアクセスし、もしなんらかの理由で失敗してもやりなおすことはありません。MySQLのデータが消されていること、削除されたMySQL上にしかないランダムな数値を使ってファイル名をhash化していることから、物理ファイルが残っていても、ほぼ到達することはできないからです。この仕様が許されない場所ではGreenBucketsはお勧めできないかもしれません。
ノードのremoteの値についてはまたいつか書きたいと思います。