1. 副本集概述
某些情況下儿普,副本可以提供更高的讀取容量祖娘,就像客戶端可以發(fā)送讀操作到不同的服務(wù)器。在不同數(shù)據(jù)中心維護數(shù)據(jù)副本可以增加分布式應(yīng)用的數(shù)據(jù)局部性和可用性妹窖。還可以因為其它目的保存額外的副本纬朝,比如災(zāi)難恢復(fù)、報告或備份骄呼。
MongoDB 中的副本
一個副本集就是一組維護相同數(shù)據(jù)集的 mongod 實例共苛。
一個數(shù)據(jù)集包含一些數(shù)據(jù)承載節(jié)點和一個可選的仲裁節(jié)點。數(shù)據(jù)承載節(jié)點中谒麦,有且只能有一個被認為是主承載節(jié)點俄讹,而其它節(jié)點被認為是次要節(jié)點。主節(jié)點接收所有寫入操作绕德。主節(jié)點將對其數(shù)據(jù)集所做的所有更改記錄到其 oplog患膛。
次要節(jié)點復(fù)制主節(jié)點的 oplog 并將操作應(yīng)用到其數(shù)據(jù)集,就跟次要數(shù)據(jù)集反映了主數(shù)據(jù)集一樣耻蛇。如果主節(jié)點不可用踪蹬,一個合格的次要節(jié)點將被選為新的主節(jié)點胞此。
可以添加一個額外的 mongod 實例到副本集中來作為仲裁節(jié)點。仲裁節(jié)點不維護數(shù)據(jù)集跃捣。仲裁節(jié)點通過響應(yīng)副本集其它成員的心跳和選舉請求來達到維護副本集中法定成員數(shù)量的目的漱牵。因為它不需要存儲數(shù)據(jù)集,比帶有數(shù)據(jù)集的全功能副本集成員消耗更少的資源疚漆,所以它是一種提供副本集仲裁功能比較好的方式酣胀。如果你的副本集有偶數(shù)個成員,添加一個仲裁節(jié)點在主節(jié)點選舉中來獲得一個主節(jié)點的選票娶聘。仲裁節(jié)點不需要專用硬件闻镶。
仲裁節(jié)點將永遠是仲裁節(jié)點,但主節(jié)點可能變?yōu)榇我?jié)點丸升,次要節(jié)點也可能通過選舉成為主要節(jié)點铆农。
異步復(fù)制
- 次要節(jié)點異步的應(yīng)用來自主節(jié)點的操作。通過在主節(jié)點之后應(yīng)用操作狡耻,副本集可以不管一個或多個成員的失敗而繼續(xù)實現(xiàn)其功能墩剖。
自動故障切換
- 當(dāng)主節(jié)點超過十秒沒有與副本集的其它成員通信,一個合格的次要節(jié)點將舉行選舉來將它自己選舉為新的主節(jié)點夷狰。第一個次要節(jié)點舉行選舉岭皂,并在接收多數(shù)成員的投票后成為主節(jié)點。
- MongoDB 3.2 版本后引入了版本 1 的復(fù)制協(xié)議孵淘,它減少了副本集的故障切換時間并加速了多個同時存在的主節(jié)點的檢測蒲障。
- 雖然時間不同,故障轉(zhuǎn)移過程一般在一分鐘之內(nèi)完成瘫证。
讀操作
- 默認的,客戶端從主節(jié)點進行讀操作庄撮,然而可以通過指定讀偏好來將讀操作發(fā)送到次要節(jié)點背捌。次要節(jié)點的異步復(fù)制意味著從次要節(jié)點進行的讀操作可能返回沒有反映主節(jié)點上數(shù)據(jù)狀態(tài)的數(shù)據(jù)。
- MongoDB 中洞斯,客戶端可以在寫操作持久化之前看到其結(jié)果毡庆。
額外的特性
副本集提供多種支持應(yīng)用需求的選項±尤纾可以部署成員在多個數(shù)據(jù)中心的副本集么抗,或通過調(diào)整一些成員的 members[n].priority 來控制選舉主節(jié)點的結(jié)果。副本集也支持專用于報告亚铁、災(zāi)難恢復(fù)或備份功能的成員蝇刀。
副本集成員
- 主節(jié)點接收所有寫操作。
- 次要節(jié)點從主節(jié)點復(fù)制操作來保持與主節(jié)點相同的數(shù)據(jù)集徘溢。次要節(jié)點可以有特殊配置文件的附加配置吞琐。比如捆探,次要節(jié)點可以對選舉棄權(quán)或優(yōu)先級為 0。
- 副本集的推薦最低配置是帶有三個數(shù)據(jù)承載點三成員副本集:一個主節(jié)點和兩個次要節(jié)點站粟。也可以部署為兩個數(shù)據(jù)承載點的三成員副本集:一個主節(jié)點黍图,一個次要節(jié)點和一個仲裁節(jié)點。但這種模式不如三數(shù)據(jù)承載點的模式冗余性好奴烙。
- 3.0 版本的變化:副本集最多可以有 50 名成員助被,但只有 7 名投票成員。
主節(jié)點
- 主節(jié)點是副本集中接收寫操作的唯一成員切诀。MongoDB 在主節(jié)點上應(yīng)用寫操作揩环,然后將操作記錄在主節(jié)點的 oplog 上。次要節(jié)點復(fù)制 oplog 并將操作應(yīng)用到它們的數(shù)據(jù)集上趾牧。
次要節(jié)點
- 次要節(jié)點維護主節(jié)點數(shù)據(jù)集的一個副本检盼。復(fù)制數(shù)據(jù)時,次要節(jié)點在異步過程中從主節(jié)點的 oplog 應(yīng)用操作到它自己的數(shù)據(jù)集翘单。副本集可以有一個或多個次要節(jié)點吨枉。
- 次要節(jié)點可以通過配置來達到實現(xiàn)一些效果:
- 防止被選舉為主節(jié)點,以便允許它留在次要節(jié)點數(shù)據(jù)中心或作為冷備用哄芜。
- 防止應(yīng)用從中讀取貌亭,以便允許它運行需要與正常流量分離的應(yīng)用。
- 保存一個運行的歷史快照认臊,以便從某些錯誤中恢復(fù)圃庭,如誤刪數(shù)據(jù)庫。
仲裁節(jié)點
- 仲裁節(jié)點沒有數(shù)據(jù)集的副本失晴,并且不能變成主節(jié)點剧腻。副本集可以有仲裁節(jié)點來在選舉主節(jié)點中投票帐我。仲裁節(jié)點永遠有一張選票枪汪,這樣就允許副本集的投票成員數(shù)量不均等攀圈,而無需承擔(dān)一個復(fù)制數(shù)據(jù)的成員的額外開銷渤涌。
- 重要:不要在還承載副本集的主節(jié)點或次要節(jié)點的系統(tǒng)上運行仲裁節(jié)點氧敢。
副本集 oplog
- oplog:操作日志第献,一個特殊的固定集合衬吆,保持所有修改數(shù)據(jù)庫上數(shù)據(jù)的操作的滾動記錄秸滴。
- 副本集的所有成員都包含一份 oplog 的副本帖族,在
local.oplog.rs
中栈源,這允許它們維護數(shù)據(jù)庫的當(dāng)前狀態(tài)。 - 為了減輕復(fù)制的困難竖般,所有的副本集成員成員都發(fā)送心跳(ping)到所有其它成員甚垦。
- 任何成員都可以從其它成員那里導(dǎo)入 oplog。
- oplog 中的每個操作都是冪等(idempotent)的。即制轰,oplog 對目標(biāo)資料應(yīng)用不管一次或多次操作前计,都產(chǎn)生相同的結(jié)果。
oplog 大小
- 當(dāng)?shù)谝淮伍_始一個副本集成員時垃杖,MongoDB 以默認大小創(chuàng)建一個 oplog男杈。
- 如果可以預(yù)料副本集工作量的大小,可以將 oplog 設(shè)置為比默認值大些调俘。相反伶棒,如果應(yīng)用主要用來執(zhí)行讀操作和少量寫操作,一個小的 oplog 可能就足夠了彩库。
- 以下情況可能需要較大的 oplog:
- 一次更新多個文檔
- 刪除與插入量大致相同的數(shù)據(jù)時
- 大量的就地更新
oplog 狀態(tài)
- 使用
rs.printReplicationInfo()
方法來查看 oplog 狀態(tài)肤无,包括其大小和操作的時間范圍。
2. 在 docker 中創(chuàng)建副本集
準(zhǔn)備工作
安裝 docker
-
下載 mongo 鏡像骇钦,如有需求可加上版本號
docker pull mongo
概覽
- 我們要從 mongo 鏡像創(chuàng)建三個容器宛渐,都處在它們的 docker 容器網(wǎng)絡(luò)內(nèi)。
- 這三個容器將被命名為 mongo1眯搭、mongo2 和 mongo3窥翩。它們將作為副本集的三個 mongo 實例。
- 暴露它們的端口到本地機器鳞仙,以便可以從本地機器的 mongo shell 來訪問它們中的任意一個寇蚊。
- 這三個 mongo 容器中的每一個都應(yīng)該能與這個網(wǎng)絡(luò)中的所有其它容器通信。
建立網(wǎng)絡(luò)
-
創(chuàng)建一個名為 my-mongo-cluster 的網(wǎng)絡(luò):
docker network create my-mongo-cluster
-
查看當(dāng)前系統(tǒng)中的網(wǎng)絡(luò):
docker network ls
創(chuàng)建容器
運行以下命令啟動第一個容器:
docker run \
-p 30001:27017 \
--name mongo1 \
--net my-mongo-cluster \
mongo mongod --replSet my-mongo-set
- docker run: 從鏡像啟動容器
- -p 30001:27017:暴露容器中的 27017 端口棍好,映射到本機的 30001 端口
- --name mongo1:給這個容器命名為 mongo1
- --net my-mongo-cluster:將此容器添加到 my-mongo-cluster 網(wǎng)絡(luò)
- mongo:用來生成此容器的鏡像名稱
- mongod --replSet my-mongo-set:執(zhí)行 mongod 命令仗岸,以將此 mongo 實例添加到名稱為 my-mongo-se 的副本集。
啟動其余兩個容器:
docker run \
-p 30002:27017 \
--name mongo2 \
--net my-mongo-cluster \
mongo mongod --replSet my-mongo-set
docker run \
-p 30003:27017 \
--name mongo3 \
--net my-mongo-cluster \
mongo mongod --replSet my-mongo-set
配置副本集
現(xiàn)在我們需要的 mongo 實例已經(jīng)運行起來了借笙,現(xiàn)在來把它們編程副本集扒怖。
-
這條命令將在運行的容器 mongo1 中打開 mongo shell:
docker exec -it mongo1 mongo
-
在 mongo shell 中進行配置:
> db = (new Mongo('localhost:27017')).getDB('test') test > config = { "_id" : "my-mongo-set", "members" : [ { "_id" : 0, "host" : "mongo1:27017" }, { "_id" : 1, "host" : "mongo2:27017" }, { "_id" : 2, "host" : "mongo3:27017" } ] }
- 第一個 _id 鍵應(yīng)當(dāng)和 —replSet 標(biāo)簽為 mongo 實例設(shè)置的值一樣,這個例子中是 my-mongo-set业稼。
- 接下來列出了所有想放到副本集中的成員姚垃。
- 將所有 mongo 實例添加到 docker 網(wǎng)絡(luò)。在 my-mongo-cluster 網(wǎng)絡(luò)中根據(jù)每個容器的名稱各自分配到 ip 地址盼忌。
-
通過以下命令啟動副本集:
rs.initiate(config)
如果一切順利,提示符將變成這樣:
my-mongo-set:PRIMARY>
這意味著 shell 現(xiàn)在與 my-mongo-set 集群中的 PRIMARY 數(shù)據(jù)庫進行了關(guān)聯(lián)掂墓。
-
下面測試副本集是否工作谦纱,先在 primary 數(shù)據(jù)庫中插入數(shù)據(jù):
> db.mycollection.insert({name : 'sample'}) WriteResult({ "nInserted" : 1 }) > db.mycollection.find() { "_id" : ObjectId("57761827767433de37ff95ee"), "name" : "sample" }
-
然后新建一個與 secondary 數(shù)據(jù)庫的連接,并測試文檔是否在那里復(fù)制:
> db2 = (new Mongo('mongo2:27017')).getDB('test') test > db2.setSlaveOk() > db2.mycollection.find() { "_id" : ObjectId("57761827767433de37ff95ee"), "name" : "sample" }
執(zhí)行 db2.setSlaveOk() 命令來讓 shell 知道我們故意在查詢非 primary 的數(shù)據(jù)庫君编。
3. 使用 Dockerfile 構(gòu)建副本集鏡像
mongo 副本集基礎(chǔ)鏡像
-
Dockerfile
FROM ubuntu:14.04 MAINTAINER Jia chenhui ENV REFRESHED_AT 2017-0726 RUN sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6 RUN echo "deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list RUN sudo apt-get update RUN sudo apt-get install -y mongodb EXPOSE 27017 CMD []
-
構(gòu)建基礎(chǔ)鏡像
docker build -t mongo_set_base .
mongo 副本集鏡像
-
Dockerfile
FROM mongo_set_base MAINTAINER Jia chenhui ENV REFRESHED_AT 2017-07-26 RUN mkdir -p /data/db ENTRYPOINT [ "usr/bin/mongod", "--replSet", "my-mongo-set" ]
-
構(gòu)建副本集鏡像
docker build -t mongod .
-
從鏡像 mongod 啟動容器 mongo1
docker run \ -p 30001:27017 \ --name mongo1 \ --net my_mongo_cluster \ mongod
-
順利的話會看到提示
[rsStart] replSet can't get local.system.replset config from self or any seed (EMPTYCONFIG)
這是因為還沒有配置副本集跨嘉,繼續(xù)往下執(zhí)行
mongo 副本集次要節(jié)點
從鏡像 mongod 啟動容器,這里啟動兩個容器 mongo2 和 mongo3吃嘿,作為副本集次要節(jié)點
docker run \
-p 30002:27017 \
--name mongo2 \
--net my_mongo_cluster \
mongod
docker run \
-p 30003:27017 \
--name mongo3 \
--net my_mongo_cluster \
mongod
配置副本集 config
-
以交互模式進入主節(jié)點的 shell(可以連接任一容器)
docker exec -it mongo1 mongo
-
執(zhí)行以下命令
db = (new Mongo('localhost:27017')).getDB('test') config = { "_id" : "my-mongo-set", "members" : [ { "_id" : 0, "host" : "mongo1:27017" }, { "_id" : 1, "host" : "mongo2:27017" }, { "_id" : 2, "host" : "mongo3:27017" } ] } rs.initiate(config)
-
如果一切正常祠乃,會出現(xiàn)以下提示梦重,表示副本集已創(chuàng)建成功,目前在主節(jié)點中
my-mongo-set:PRIMARY>
測試副本集
-
插入一條文檔
db.mycollection.insert({name : 'sample'})
-
查看是否成功插入
db.mycollection.find()
-
另開一個 terminal 連接次要節(jié)點亮瓷,查詢文檔是否與主節(jié)點插入的文檔一致
docker exec -it mongo2 mongo
db2 = (new Mongo('mongo2:27017')).getDB('test') db2.setSlaveOk() db2.mycollection.find()
到這里就完成了一個主節(jié)點兩個次要節(jié)點副本集的創(chuàng)建琴拧。
4. 使用 docker-compose 構(gòu)建副本集服務(wù)
Dockerfile
FROM mongo:3.4.1
MAINTAINER Jia Chenhui
ENV REFRESHED_AT 2017-07-27
ADD . /replicaset
WORKDIR /replicaset
RUN mkdir -p /replicaset/data/db
EXPOSE 27017
ENTRYPOINT [ "mongod", "--replSet", "my-mongo-set" ]
compose.yml
version: '2'
services:
primary:
build: .
image: mongoset:primary
ports:
- "30001:27017"
volumes:
- .:/replicaset
networks:
- my_mongo_cluster
replica1:
build: .
image: mongoset:replica1
ports:
- "30002:27017"
volumes:
- .:/replicaset
networks:
- my_mongo_cluster
replica2:
build: .
image: mongoset:replica2
ports:
- "30003:27017"
volumes:
- .:/replicaset
networks:
- my_mongo_cluster
networks:
my_mongo_cluster:
創(chuàng)建服務(wù)
docker-compose up
進入 mongo shell 配置并初始化副本集
config = {
"_id" : "my-mongo-set",
"members" : [
{
"_id" : 0,
"host" : "compose_primary_1:27017"
},
{
"_id" : 1,
"host" : "compose_replica1_1:27017"
},
{
"_id" : 2,
"host" : "compose_replica2_1:27017"
}
]
}
rs.initiate(config)