Rails
MongoDB
mongoid
docker-compose

聖なる夜にMongodbDBと戯れるwith docker-compose

More than 1 year has passed since last update.


経緯

普段railsでmongodbを使うことがあるのですが、sharding周りを全くわかっとらんので、

docker-composeを利用して検証。

現状の運用上の問題解決ができるか調査してみた。

(間違ったことを書いていることもあると思うので、編集リクエストや指摘してもらえると嬉しいです。)


現状の構成と課題


構成

mongodbに限らずshardingした時の運用は辛いということで、

現状shardingは利用せず、レプリカセットだけ組んで利用しています。

railsのodmとして有名なmongoidを利用しているのですが、接続情報がこんな感じで記述します。


config/mongoid.yml

production:

clients:
default:
database: mongodb_database
hosts:
- primary:27017
- secondary1:27017
- secondary2:27017

image


課題

スケールアップや、AWSの基盤メンテにぶち当たった時にレプリカセットのメンバーの入れ替えを行うのですが、

入れ替え作業を行う時などに接続情報を更新してアプリケーションのデプロイが必要になりめんどくさいし、ミスりそう。


解決策

shardingを1系統だけ利用して、mongodの接続をmongos経由の接続にすることによって、dbの接続先の情報などをアプリケーションに持たなくてよくする。


config/mongoid.yml

production:

clients:
default:
database: mongodb_database
hosts:
- localhost:27017

(図が全部primaryになってておかしいですが、後で差し替えときます:bow:)

IMG_4668.JPG

mongosはmonitなどで監視再起動すればいいかと考える


検証環境

mac

docker-for-mac

railsアプリはhostマシンで動作し、ミドルウェアはdocker上で動かしています。


ディレクトリ構成

mongodb

└── docker-compose.yml

mongodb以外のミドルウェアはrailsのappの方のdocker-composeに記述しています。

今回は検証用にrails app のdocker-composeのmongodbの記述をコメントアウトし、

別のディレクトリを切ってmongodbだけ別途検証できるようにしています。


docker-compose.yml

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をレプリカセットから外し、アプリケーションの接続情報が変わっていないが問題ないことを確認


検証手順

docker-compose up

docker-compose-up.gif

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を使うらしい

config1.gif

config2.gif

mongodのレプリカセットを設定する


docker exec -it mongodb_d1_1 mongo

# mongo shell
rs.initiate()
rs.add("d2")
rs.add("c3")

mongod.gif

mongosにアクセスしてshardを登録する


docker exec -it mongodb_s1_1 mongo

# mongo shell
sh.addShard("rep1/d2")
# レプリカセットに所属しいるサーバーを1台指定すればいい感じにやってくれる

mongos.gif

railsアプリの方でサンプルデータ投入

接続情報


config/mongoid.yml

development:

clients:
default:
database: development
hosts:
- localhost:27017

データ投入が終わったところでmongos2の方で接続してみる

接続情報


config/mongoid.yml

development:

clients:
default:
database: development
hosts:
- localhost:27018 #←portを変えた


bin/spring stop
bin/rails c

rails_c_1.gif

ちゃんと繋がりました。

今まで別の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")

mongod2.gif

primaryサーバーを入れ替えます

# mongo shell

conf = rs.conf()
conf.members[3].priority = 2
rs.reconfig(conf)

mongod3.gif

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")

mongod4.gif

rails consoleの状況はどうか

rails-c2.gif

読み込み、書き込み共にうまくいきました。

最後に運用周りでshardが1つの時にconfigサーバーのバックアップは必要なのかを検証してみたいと思います。

mongos,confgサーバーをkillして構築し直した時にちゃんと参照できるのか。

(mongodサーバーのデータだけバックアップとればいい状態であれば嬉しい)


zsh

# {}の部分は他の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

d-kill1.gif

d-kill2.gif

mongosとconfigサーバーを新しく立て直す

docker-compose up -d

dc-up-d.gif

configサーバーを構築しなおす

docker exec -it mongodb_c1_1 mongo --port 27019

# mongo shell
rs.initiate()
rs.add("c2:27019")
rs.add("c3:27019")

mongos3.gif

mongos4.gif


docker exec -it mongodb_s1_1 mongo

# mongo shell
sh.addShard("rep1/d4")

mongos5.gif

rails consoleで確認

rails-c3.gif

読み込みも書き込みも可能。

rails serverの方でもちゃんと動作していることを確認。


 まとめ

1.mongodbでshardingを1にして使えば、アプリ側で接続情報を変えず(デプロイなし)にmongodbのメンテナンスができそう。

2.shard1にしておけば、configサーバーがぶっ壊れても作り直せばなんとかなりそう。(mongocのバックアップ不要)

(2に関しては自信がないので、やるなら自己責任でお願いします。)

docker-compose便利

ローカルで手軽にレプリカセットとかをdocker-composeを使えば検証できて便利だなと感じました。

elastic searchredis,memcachedのクラスター検証にもいいかもしれません。

mongodbアップグレードの検証などにはとても向いていると思います。

(3.2=>3.4のアップグレードは手順が多そうだったので、今回の記事では見送りました。replica setのみだったら楽そうです。)

とくにフォアグラウンド実行すると各dockerの出力が観れてわかりやすいと感じました。

あと全然関係ないんですけど、macを新しくして、gifアニメキャプチャのソフトを新しくしたんですが、

GIPHY Captureってアプリがすごく良かったです。

無料で、最初と最後カットできたり、フレームレートや解像度を変更できたので、手順書とかqiitaとかに貼る用途にいいかと。

image