経緯
普段railsでmongodbを使うことがあるのですが、sharding周りを全くわかっとらんので、
docker-composeを利用して検証。
現状の運用上の問題解決ができるか調査してみた。
(間違ったことを書いていることもあると思うので、編集リクエストや指摘してもらえると嬉しいです。)
現状の構成と課題
構成
mongodbに限らずshardingした時の運用は辛いということで、
現状shardingは利用せず、レプリカセットだけ組んで利用しています。
railsのodmとして有名なmongoidを利用しているのですが、接続情報がこんな感じで記述します。
production:
clients:
default:
database: mongodb_database
hosts:
- primary:27017
- secondary1:27017
- secondary2:27017
課題
スケールアップや、AWSの基盤メンテにぶち当たった時にレプリカセットのメンバーの入れ替えを行うのですが、
入れ替え作業を行う時などに接続情報を更新してアプリケーションのデプロイが必要になりめんどくさいし、ミスりそう。
解決策
shardingを1系統だけ利用して、mongodの接続をmongos経由の接続にすることによって、dbの接続先の情報などをアプリケーションに持たなくてよくする。
production:
clients:
default:
database: mongodb_database
hosts:
- localhost:27017
(図が全部primaryになってておかしいですが、後で差し替えときます)
mongosはmonitなどで監視再起動すればいいかと考える
検証環境
mac
docker-for-mac
railsアプリはhostマシンで動作し、ミドルウェアはdocker上で動かしています。
ディレクトリ構成
mongodb
└── docker-compose.yml
mongodb以外のミドルウェアはrailsのappの方のdocker-composeに記述しています。
今回は検証用にrails app のdocker-composeのmongodbの記述をコメントアウトし、
別のディレクトリを切ってmongodbだけ別途検証できるようにしています。
version: '2'
services:
s1:
image: mongo:3.2
ports:
- "27017:27017"
command: mongos --configdb confRep1/c1:27019,c2:27019,c3:27019
links:
- d1
- d2
- d3
- d4
- d5
- d6
- c1
- c2
- c3
s2:
image: mongo:3.2
ports:
- "27018:27017"
command: mongos --configdb confRep1/c1:27019,c2:27019,c3:27019
links:
- d1
- d2
- d3
- d4
- d5
- d6
- c1
- c2
- c3
c1:
image: mongo:3.2
command: mongod --configsvr --replSet confRep1
links:
- d1
- d2
- d3
- d4
- d5
- d6
- c2
- c3
c2:
image: mongo:3.2
command: mongod --configsvr --replSet confRep1
links:
- d1
- d2
- d3
- d4
- d5
- d6
- c3
c3:
image: mongo:3.2
command: mongod --configsvr --replSet confRep1
links:
- d1
- d2
- d3
- d4
- d5
- d6
d1:
image: mongo:3.2
command: mongod --replSet rep1
links:
- d2
- d3
- d4
- d5
- d6
d2:
image: mongo:3.2
command: mongod --replSet rep1
links:
- d3
- d4
- d5
- d6
d3:
image: mongo:3.2
command: mongod --replSet rep1
links:
- d4
- d5
- d6
d4:
image: mongo:3.2
command: mongod --replSet rep1
links:
- d5
- d6
d5:
image: mongo:3.2
command: mongod --replSet rep1
links:
- d6
d6:
image: mongo:3.2
command: mongod --replSet rep1
検証内容
d1,d2,d3でレプリカセットを組み、アプリケーションのサンプルデータを流し込む。
d4,d5,d6をレプリカセットに参加させ、d4をprimaryに昇格させる。
d1,d2,d3をレプリカセットから外し、アプリケーションの接続情報が変わっていないが問題ないことを確認
検証手順
config serverのレプリカセットが組めてないので、
mongosサーバーが荒ぶってlogを出力している
configサーバーのレプリカセットを設定する
docker exec -it mongodb_c1_1 mongo --port 27019
# mongo shell
rs.initiate()
rs.add("c2:27019")
rs.add("c3:27019")
# configサーバーはデフォルトでport:27019を使うらしい
mongodのレプリカセットを設定する
docker exec -it mongodb_d1_1 mongo
# mongo shell
rs.initiate()
rs.add("d2")
rs.add("c3")
mongosにアクセスしてshardを登録する
docker exec -it mongodb_s1_1 mongo
# mongo shell
sh.addShard("rep1/d2")
# レプリカセットに所属しいるサーバーを1台指定すればいい感じにやってくれる
railsアプリの方でサンプルデータ投入
接続情報
development:
clients:
default:
database: development
hosts:
- localhost:27017
データ投入が終わったところでmongos2の方で接続してみる
接続情報
development:
clients:
default:
database: development
hosts:
- localhost:27018 #←portを変えた
bin/spring stop
bin/rails c
ちゃんと繋がりました。
今まで別のmongosで作業していても、mongocで情報が共有されるので同じように動くということですね。
shard statusを確認するとアクティブなmongosの数も確認できます。
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("585d48811b8e027da3d3da28")
}
shards:
{ "_id" : "rep1", "host" : "rep1/8d106239ab63:27017,d2:27017,d3:27017" }
active mongoses:
"3.2.11" : 2
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" : "development", "primary" : "rep1", "partitioned" : false }
ここから、レプリカセットの中身を入れ替えても、
rails consoleのセッションを保ったまま操作ができるかを検証していきます。
docker exec -it mongodb_d1_1 mongo
# mongo shell
# 本番環境ではレプリカセットに追加する時に参照されないようなオプションをつける必要があります。
# データが追いついてないのにアプリケーションから整合性がないデータが参照されてしまうからです。
# データが追いついたことを確認して、参照できるように情報を書き換えます。今回は検証なのでめんどくさいのでやっていません。
rs.add("d4")
rs.add("d5")
rs.add("d6")
primaryサーバーを入れ替えます
# mongo shell
conf = rs.conf()
conf.members[3].priority = 2
rs.reconfig(conf)
mongod d1,d2,d3をレプリカセットから外します。
これでデータが入っていた入れ物は総入れ替えになりました。
docker exec -it mongodb_d4_1 mongo
# mongo shell
rs.remove("8d106239ab63:27017")
rs.remove("d2:27017")
rs.remove("d3:27017")
読み込み、書き込み共にうまくいきました。
最後に運用周りでshardが1つの時にconfigサーバーのバックアップは必要なのかを検証してみたいと思います。
mongos,confgサーバーをkillして構築し直した時にちゃんと参照できるのか。
(mongodサーバーのデータだけバックアップとればいい状態であれば嬉しい)
# {}の部分は他のshellでできたか自信ないです。
docker kill mongodb_s{1,2}_1
docker rm mongodb_s{1,2}_1
docker kill mongodb_c{1,2,3}_1
docker rm mongodb_c{1,2,3}_1
mongosとconfigサーバーを新しく立て直す
docker-compose up -d
configサーバーを構築しなおす
docker exec -it mongodb_c1_1 mongo --port 27019
# mongo shell
rs.initiate()
rs.add("c2:27019")
rs.add("c3:27019")
docker exec -it mongodb_s1_1 mongo
# mongo shell
sh.addShard("rep1/d4")
rails consoleで確認
読み込みも書き込みも可能。
rails serverの方でもちゃんと動作していることを確認。
# まとめ
1.mongodbでshardingを1にして使えば、アプリ側で接続情報を変えず(デプロイなし)にmongodbのメンテナンスができそう。
2.shard1にしておけば、configサーバーがぶっ壊れても作り直せばなんとかなりそう。(mongocのバックアップ不要)
(2に関しては自信がないので、やるなら自己責任でお願いします。)
docker-compose便利
ローカルで手軽にレプリカセットとかをdocker-composeを使えば検証できて便利だなと感じました。
elastic search
やredis
,memcached
のクラスター検証にもいいかもしれません。
mongodb
アップグレードの検証などにはとても向いていると思います。
(3.2=>3.4のアップグレードは手順が多そうだったので、今回の記事では見送りました。replica setのみだったら楽そうです。)
とくにフォアグラウンド実行すると各dockerの出力が観れてわかりやすいと感じました。
あと全然関係ないんですけど、macを新しくして、gifアニメキャプチャのソフトを新しくしたんですが、
GIPHY Captureってアプリがすごく良かったです。
無料で、最初と最後カットできたり、フレームレートや解像度を変更できたので、手順書とかqiitaとかに貼る用途にいいかと。