MongoDBのShardingを試してみた。その3 障害発生時の挙動について

今回はMongoDBのSharding環境にて、各サーバが停止した場合の挙動について検証してみたいと思います。

検証環境

今回の検証環境も前回と同じ環境を使います。

mongosサーバ

最初はmongosサーバです。
早速、mongosサーバを落としてみましょう。

[matsuou1@testsvr mongodb]$ kill -2 14973

次に、mongosサーバに接続してみます。

[matsuou1@testsvr mongodb]$ ./bin/mongo localhost:10000/logdb
MongoDB shell version: 1.8.0
connecting to: localhost:10000/logdb
Tue Apr 19 23:51:28 Error: couldn't connect to server localhost:10000 shell/mongo.js:81
exception: connect failed

当然と言えば当然ですが、mongosサーバへの接続に失敗します。

まとめ

想定通り、mongosサーバに接続できなくなりました。
各shardには問題なく接続でき、クエリの実行も可能ですが、複数shardをまたがるようなクエリの実行は出来なくなります。
下手な構成な場合は単一障害点となりうるので、基本的にはwebサーバやアプリサーバなど実際にアプリケーションが動作するサーバで
実行させて、プロセスの死活監視を行い、障害が発生したらプロセスの再起動を行う感じの運用になるのではないでしょうか。
mongosは、単なるルーティングプロセスでデータの管理はしないので、復旧時に特に気を使う必要はないかと思います。

shardサーバ

次は、3つあるshardサーバのうち1つを落としてみます。

shard3のプロセスIDを確認し、落としてみます。

[matsuou1@testsvr mongodb]$ kill -2 18436

試しにカウントを取ってみましたが、エラーとなります。

> db.logs10.count()
Tue Apr 19 14:45:54 uncaught exception: count failed: {
        "assertion" : "DBClientBase::findOne: transport error: localhost:10003 query: { count: \"logs10\", query: {} }",
        "assertionCode" : 10276,
        "errmsg" : "db assertion failure",
        "ok" : 0
}
> db.logs10.count()
Tue Apr 19 14:45:57 uncaught exception: error { "$err" : "socket exception", "code" : 11002 }

shard keyでアクセスし、shard3以外のshardのみにアクセスする場合は、クエリ―は実行可能

> db.logs10.find({ uid : "f26AKDmNMkIXXXXt"})
{ "_id" : ObjectId("4da7d407af13de0b7c6XXXX0"), "month" : "2010/02", "uid" : "f26AKDmNMkIXXXXt", "timestamp" : "2010/02/28 16:00:51", "path" : "/?pid=P07K&sid=E74D&uid=1", "device" : "940P" }
{ "_id" : ObjectId("4da7d407af13de0b7c6c35cd"), "month" : "2010/02", "uid" : "f26AKDmNMkIXXXXt", "timestamp" : "2010/02/28 16:01:12", "path" : "/", "device" : "940P" }
{ "_id" : ObjectId("4da7d407af13de0b7c6c369d"), "month" : "2010/02", "uid" : "f26AKDmNMkIXXXXt", "timestamp" : "2010/02/28 16:06:06", "path" : "/index.html", "device" : "940P" }
{ "_id" : ObjectId("4da7d44aaf13de0b7c7aa66d"), "month" : "2010/03", "uid" : "f26AKDmNMkIXXXXt", "timestamp" : "2010/03/31 16:51:34", "path" : "/?pid=P07K&sid=E74D&uid=1", "device" : "940P" }
障害中に実行可能なクエリのまとめ

shard環境では、targetedとglobalの2つのタイプのクエリに分けられます。
targetedは、最小限のshard(多くの場合は1つのみ)にアクセスし、globalはほぼ全てのshardにアクセスします。
つまり、このタイプの違いによって、障害中に実行可能かどうかが分かれることになります。
クエリのタイプについては、マニュアルの Operation Types を参照してください。

  • targetedクエリ

  • 実行可能なクエリの例

・shard keyを指定して、落ちているshard以外のshardの検索
・shard keyを指定して、落ちているshard以外のshardのアップデート
・インサート

  • globalクエリ

  • 実行不可能なクエリの例

・shard keyを指定して、落ちているshardのデータの検索
・shard keyを指定しない検索

まとめ

shard障害が発生すると、障害shardを参照するクエリは実行エラーとなるが、問題ないshardへのshard keyを使用したクエリは実行可能。
アプリへの影響が大きいので、基本的にはReplica set を構成し、冗長化を行うべきです。

configサーバ

最後に、configサーバが落ちた場合についてです。
先ほどと同じように、configサーバを落としてみます。

[matsuou1@testsvr mongodb]$ kill -2 27480

この状態で、以下の操作を行ってみます。

  • shardの追加、削除

まずは、shard4を新たに起動します。

[matsuou1@testsvr mongodb]$ ./bin/mongod --shardsvr --port 10005 --dbpath /tmp/mongodb/shard4 --logpath /tmp/mongodb/log/shard4.log &

adminサーバより、addshardコマンドを実行します。

[matsuou1@testsvr mongodb]$ ./bin/mongo localhost:10000/admin
MongoDB shell version: 1.8.0
connecting to: localhost:10000/admin
> db.runCommand( { addshard : "localhost:10005" } );
Wed Apr 20 00:48:51 uncaught exception: error { "$err" : "socket exception", "code" : 11002 }

想定通りにエラーとなります。クラスタメタデータを管理するサーバが落ちているので当然ですよね。

  • 大量データロード

次に大量のデータをロードし、chunkがどのようになるか確認します。

1行のみ入れてあるcollectionに対し、100万行程のアクセスログをインポートしてみます。

logdb.logs12 chunks:
				shard0000	1
			{ "timestamp" : { $minKey : 1 } } -->> { "timestamp" : { $maxKey : 1 } } on : shard0000 { "t" : 1000, "i" : 0 }

chunkの分割や移動は行われなくなるため、色々大変となるところの検証を行うつもりでいたのですが、
shardやmongosが大量のExceptionを吐いて、mongoimportが激遅に。。。

shardのログ

Wed Apr 20 01:24:23 [initandlisten] connection accepted from 127.0.0.1:46419 #53871
Wed Apr 20 01:24:23 [conn53871] end connection 127.0.0.1:46419
Wed Apr 20 01:24:23 [initandlisten] connection accepted from 127.0.0.1:46421 #53872
Wed Apr 20 01:24:23 [conn53846] end connection 127.0.0.1:46369

mongosのログ

Wed Apr 20 01:25:57 [conn2] DBException in process: socket exception
Wed Apr 20 01:25:57 [conn2] ~ScopedDBConnection: _conn != null
Wed Apr 20 01:25:57 [conn2] DBException in process: socket exception
Wed Apr 20 01:25:57 [conn2] ~ScopedDBConnection: _conn != null
Wed Apr 20 01:25:57 [conn2] DBException in process: socket exception

わざわざ、configサーバを落とした状態でmongoimportする人はなかなかいないと思いますが、エラー吐きまくりな状態になるのでやめておいたほうが無難です。時間があったら、もう少し調査&最新版で検証してみます。

まとめ

configサーバ障害が発生すると、システムのメタデータはリードオンリーになるため、shardの追加、削除などは出来なくなります。
システムは機能し続けますが、chunkの分割や移動は行われなくなるため、このタイミングで大量のデータロードを行うとmigrateできないために、アンバランスなshardができてしまう可能性があります。(マニュアルより。。。)

また、configサーバが落ちている場合は、クラスタメタデータが必要なためmongosサーバが起動できません。
configサーバと同時にmongosサーバも落ちた場合は、先にconfigサーバ復旧後にmongosサーバを復旧させる必要があります。

マニュアルでは、ダメージは大きくないよと記載されていますが、意外なところで思わぬバグ等を踏む可能性もあるので、可能な限り早く復旧しましょう。

全体まとめ

今回は、sharding環境構成する3つのサーバに対し、それぞれ障害が発生した場合にどういう現象が発生するか検証してみました。
障害発生時に焦るよりは、障害発生時にどのような状況になるのかを事前に把握し、速やかに復旧させたいですね。
障害が発生しない、または、きちんと冗長構成を取っておくのが良いのは、間違いないのですが。

色々、踏み込めてない気がするので、時間があれば後で追記するかもしれません。