PR TIMESデザイナー&エンジニアブログ BREAK TIMES

PR TIMES Developer Blog(デザイナー&エンジニアによる開発者ブログ)

PR TIMES Developer Blog
デザイナー&エンジニアによる開発者ブログ

mongodb3.2でシャーディングを複数台で実践してみた

あけましておめでとうございます!!
チーフエンジニアの落合です!

それでは、新年1発目のネタですが・・・・MongoDB3.2です!
現在、PR TIMESで扱っているサービスに、MongoDBを導入検討を行っております。

導入する事を前提に、複数台のサーバーを用いて、
シャーディング構成(レプリカセットは無し)を作成しましたので、
本日は、構築手順を報告したいと思います。

前提

まずは、以下の通りに3台のVPSを開発環境として用意しています。
今回は、さくらインターネットVPSを使用し、スイッチを使ってプライベートネットワークを構築しています。

サーバー名* プライベートIP* OS*
mongo01 192.168.0.1 Centos6.5
mongo02 192.168.0.2 Centos6.5
mongo03 192.168.0.3 Centos6.5

インストール手順

MongoDBのインストール手順は、https://docs.mongodb.org/manual/tutorial/install-mongodb-on-red-hat/:mongodb公式サイトを参考にしながら3台全てに実施しています。
まずは、yumリポジトリを追加します。

$ vim /etc/yum.repos.d/mongodb-org-3.2.repo
[mongodb-org-3.2]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.2/x86_64/
gpgcheck=0
enabled=0

次に、この登録したリポジトリを使用してインストールを実施します。

$ yum -y install --enablerepo=mongodb-org-3.2  mongodb-org

準備が完了しましたので、早速シャーディングを構築していきたいと思います。

mongo01での設定

今回は、mongo01にconfigサーバーとmongosサーバー、シャードサーバーを同居させています。
本番の場合は、別々にするべきですが、今回はシャーディングを複数台で作成するのが目的なので、今回は割愛します。
また、テスト用にchunkSizeは1(MB)にしています。
デフォルトは、シャーディングしない状態だと200MBになっており、シャーディングしている状態だと64MBになります。
チャンクサイズが小さすぎると、データの移動が多くなりすぎる状態になり、不具合につながるようです。

$ mkdir -p /mongo/data/node0
$ mkdir /mongo/data/config
$ mkdir /mongo/log
$ mongod --shardsvr --port 30000 --dbpath /mongo/data/node0 --logpath /mongo/log/node0.log --fork
# Configサーバーの起動
$ mongod --configsvr --port 20001 --dbpath /mongo/data/config --logpath /mongo/log/config.log --fork 
# Mongosサーバの起動
$ mongos --configdb 192.168.0.1:20001 --port 20000 --logpath /mongo/log/mongos.log --chunkSize 1 --fork

ここでハマりポイント!

色んなサイトを見ていると、どこのサイトでもmongosを起動する際にconfigdbをlocalhost:20001と指定していますが、
全てlocalhostで行っている場合は、いいのですが、IPが変わる場合は、IPで指定しないと、シャーディングを登録する時に、
以下のようなエラーが発生します。

"can't use localhost as a shard since all shards need to communicate. 
either use all shards and configdbs in localhost or all in actual IPs host: localhost:xxxxx isLocalHost:0"

意味がわからず、何回もやり直して気づきました・・・。
ちゃんと、エラーメッセージを読まないとダメですね(笑)

mongo02での設定

さて、こちらのサーバーの役割は、シャードサーバだけです。
以下のように設定しています。

# ssh mongo02
$ mkdir -p /mongo/data/node1 && mkdir /mongo/log
$ mongod --shardsvr --port 30001 --dbpath /mongo/data/node1 --logpath /mongo/log/node1.log --fork

# ssh mongo03
$ mkdir -p /mongo/data/node2 && mkdir /mongo/log
$ mongod --shardsvr --port 30002 --dbpath /mongo/data/node2 --logpath /mongo/log/node2.log --fork

シャーディングを実行する。

シャーディングを行うには、まず、DBを作成する必要があります。

$ mongo 192.168.0.2:20000/admin
mongos> use logdb 

テストデータを追加した上で、シャードするキーをindexしておきます。

mongos> for(var i=1; i<=100000; i++) db.logs.insert({"uid":i, "value":Math.floor(Math.random()*100000+1)})
mongos> db.logs.ensureIndex( { uid : 1 } ); 

次に、シャードを追加します。

mongos> use admin
mongos> sh.addShard("192.168.0.1:30000")
mongos> sh.addShard("192.168.0.2:30001")
mongos> sh.addShard("192.168.0.3:30002")

この状態でシャーディングの状態を確認すると以下の状態になります。

mongos> sh.status()
 --- Sharding Status ---
  sharding version: {
    "_id" : 1,
    "minCompatibleVersion" : 5,
    "currentVersion" : 6,
    "clusterId" : ObjectId("56722a2022ab7dfdaabcb289")
}
  shards:
    {  "_id" : "shard0000",  "host" : "192.168.0.1:30000" }
    {  "_id" : "shard0001",  "host" : "192.168.0.2:30001" }
    {  "_id" : "shard0002",  "host" : "192.168.0.3:30002" }
  active mongoses:
    "3.2.0" : 1
  balancer:
    Currently enabled:  yes
    Currently running:  no
    Failed balancer rounds in last 5 attempts:  0
    Migration Results for the last 24 hours:
        No recent migrations
  databases:
    {  "_id" : "logdb",  "primary" : "shard0000",  "partitioned" : false }

シャードのノードを追加しただけなので、実際にlogdbのシャーディングは実施されていません。
シャーディングするには、以下のコマンドを実行します。

mongos> sh.enableSharding("logdb")
mongos> sh.shardCollection("logdb.logs" , { uid : 1 })
{ "collectionsharded" : "logdb.logs", "ok" : 1 }

まず、DBに対してシャーディングを有効にします。
次に、コレクションに対して、どのキーで水平分割をするか設定を行います。
※この際、コレクションのindexがない場合、indexを作ってくださいというエラーが発生します。
ステータスを確認すると、以下の状態になっていると思います。

mongos> sh.status()
 --- Sharding Status ---
  sharding version: {
    "_id" : 1,
    "minCompatibleVersion" : 5,
    "currentVersion" : 6,
    "clusterId" : ObjectId("56722a2022ab7dfdaabcb289")
}
  shards:
    {  "_id" : "shard0000",  "host" : "192.168.0.1:30000" }
    {  "_id" : "shard0001",  "host" : "192.168.0.2:30001" }
    {  "_id" : "shard0002",  "host" : "192.168.0.3:30002" }
  active mongoses:
    "3.2.0" : 1
  balancer:
    Currently enabled:  yes
    Currently running:  no
    Failed balancer rounds in last 5 attempts:  0
    Migration Results for the last 24 hours:
        6 : Success
  databases:
    {  "_id" : "logdb",  "primary" : "shard0000",  "partitioned" : true }
        logdb.logs
            shard key: { "uid" : 1 }
            unique: false
            balancing: true
            chunks:
                shard0000   4
                shard0001   3
                shard0002   3
            { "uid" : { "$minKey" : 1 } } -->> { "uid" : 10486 } on : shard0001 Timestamp(2, 0)
            { "uid" : 10486 } -->> { "uid" : 20972 } on : shard0002 Timestamp(3, 0)
            { "uid" : 20972 } -->> { "uid" : 31458 } on : shard0001 Timestamp(4, 0)
            { "uid" : 31458 } -->> { "uid" : 41944 } on : shard0002 Timestamp(5, 0)
            { "uid" : 41944 } -->> { "uid" : 52430 } on : shard0001 Timestamp(6, 0)
            { "uid" : 52430 } -->> { "uid" : 62916 } on : shard0002 Timestamp(7, 0)
            { "uid" : 62916 } -->> { "uid" : 73402 } on : shard0000 Timestamp(7, 1)
            { "uid" : 73402 } -->> { "uid" : 83888 } on : shard0000 Timestamp(1, 7)
            { "uid" : 83888 } -->> { "uid" : 94374 } on : shard0000 Timestamp(1, 8)
            { "uid" : 94374 } -->> { "uid" : { "$maxKey" : 1 } } on : shard0000 Timestamp(1, 9)

シャーディングが実施され、チャンクが10個あり、各サーバーに分かれているのがわかるかと思います。

実行中の状態でシャードを追加したらどうなる?

出来るだとうと思って、実験してみました。
まずはシャードサーバにmongodを複数起動します。

# ssh cli02
$ mkdir -p /mongo/data/node3
$ mongod --shardsvr --port 30003 --dbpath /mongo/data/node3 --logpath /mongo/log/node3.log --fork
# ssh cli03
$ mkdir -p /mongo/data/node4
$ mongod --shardsvr --port 30004 --dbpath /mongo/data/node4 --logpath /mongo/log/node4.log --fork

次にmongosにログインして、シャードを追加してみます。

$ mongo 192.168.0.1:20000/admin
mongos> sh.addShard("192.168.0.2:30003")
mongos> sh.addShard("192.168.0.3:30004")

さて、この状態で、チャンクの状態を確認したいと思います。

mongos> sh.status()
 --- Sharding Status ---
  sharding version: {
    "_id" : 1,
    "minCompatibleVersion" : 5,
    "currentVersion" : 6,
    "clusterId" : ObjectId("56722a2022ab7dfdaabcb289")
}
  shards:
    {  "_id" : "shard0000",  "host" : "192.168.0.1:30000" }
    {  "_id" : "shard0001",  "host" : "192.168.0.2:30001" }
    {  "_id" : "shard0002",  "host" : "192.168.0.3:30002" }
    {  "_id" : "shard0003",  "host" : "192.168.0.2:30003" }
    {  "_id" : "shard0004",  "host" : "192.168.0.3:30004" }
  active mongoses:
    "3.2.0" : 1
  balancer:
    Currently enabled:  yes
    Currently running:  no
    Failed balancer rounds in last 5 attempts:  0
    Migration Results for the last 24 hours:
        10 : Success
  databases:
    {  "_id" : "logdb",  "primary" : "shard0000",  "partitioned" : true }
        logdb.logs
            shard key: { "uid" : 1 }
            unique: false
            balancing: true
            chunks:
                shard0000   2
                shard0001   2
                shard0002   2
                shard0003   2
                shard0004   2
            { "uid" : { "$minKey" : 1 } } -->> { "uid" : 10486 } on : shard0004 Timestamp(10, 0)
            { "uid" : 10486 } -->> { "uid" : 20972 } on : shard0004 Timestamp(11, 0)
            { "uid" : 20972 } -->> { "uid" : 31458 } on : shard0001 Timestamp(10, 1)
            { "uid" : 31458 } -->> { "uid" : 41944 } on : shard0002 Timestamp(11, 1)
            { "uid" : 41944 } -->> { "uid" : 52430 } on : shard0001 Timestamp(6, 0)
            { "uid" : 52430 } -->> { "uid" : 62916 } on : shard0002 Timestamp(7, 0)
            { "uid" : 62916 } -->> { "uid" : 73402 } on : shard0003 Timestamp(8, 0)
            { "uid" : 73402 } -->> { "uid" : 83888 } on : shard0003 Timestamp(9, 0)
            { "uid" : 83888 } -->> { "uid" : 94374 } on : shard0000 Timestamp(9, 1)
            { "uid" : 94374 } -->> { "uid" : { "$maxKey" : 1 } } on : shard0000 Timestamp(1, 9)

自動的に、チャンクの移動を行い、バランスを取ってくれているのがわかると思います。
こういった機能が、簡単に行えるのも、mongodbの魅力ですよね!
今回はレプリカセットを作成しないでテストした結果をまとめてみました。

また、このデータをPHPから取得して、扱う為に、PHPのマニュアルを漁っていると・・・・。
mongodbは現在「MongoDB driver classes」というライブラリで、取り扱うのが推奨されており、
このページも、まだ翻訳どころか、マニュアルも完成していない状態です。

このドライバーライブラリも扱ってみたので、次回更新時には、MongoDB driver classesを使ったCRUDをレポートしたいと思います。

今年こそは、もっとブログの更新頻度をあげて、継続的にPRTIMESの文化やエンジニアの活動を、積極的に発信できればと思っています。
本年も、BREAKTIMESをよろしくお願いいたします。

以上、落合でした!