使用 docker-compose 部署 MongoDB 副本集

1. 副本集概述

某些情況下儿普,副本可以提供更高的讀取容量祖娘,就像客戶端可以發(fā)送讀操作到不同的服務(wù)器。在不同數(shù)據(jù)中心維護數(shù)據(jù)副本可以增加分布式應(yīng)用的數(shù)據(jù)局部性和可用性妹窖。還可以因為其它目的保存額外的副本纬朝,比如災(zāi)難恢復(fù)、報告或備份骄呼。

MongoDB 中的副本

  1. 一個副本集就是一組維護相同數(shù)據(jù)集的 mongod 實例共苛。

  2. 一個數(shù)據(jù)集包含一些數(shù)據(jù)承載節(jié)點和一個可選的仲裁節(jié)點。數(shù)據(jù)承載節(jié)點中谒麦,有且只能有一個被認為是主承載節(jié)點俄讹,而其它節(jié)點被認為是次要節(jié)點。主節(jié)點接收所有寫入操作绕德。主節(jié)點將對其數(shù)據(jù)集所做的所有更改記錄到其 oplog患膛。

  3. 次要節(jié)點復(fù)制主節(jié)點的 oplog 并將操作應(yīng)用到其數(shù)據(jù)集,就跟次要數(shù)據(jù)集反映了主數(shù)據(jù)集一樣耻蛇。如果主節(jié)點不可用踪蹬,一個合格的次要節(jié)點將被選為新的主節(jié)點胞此。

  4. 可以添加一個額外的 mongod 實例到副本集中來作為仲裁節(jié)點。仲裁節(jié)點不維護數(shù)據(jù)集跃捣。仲裁節(jié)點通過響應(yīng)副本集其它成員的心跳和選舉請求來達到維護副本集中法定成員數(shù)量的目的漱牵。因為它不需要存儲數(shù)據(jù)集,比帶有數(shù)據(jù)集的全功能副本集成員消耗更少的資源疚漆,所以它是一種提供副本集仲裁功能比較好的方式酣胀。如果你的副本集有偶數(shù)個成員,添加一個仲裁節(jié)點在主節(jié)點選舉中來獲得一個主節(jié)點的選票娶聘。仲裁節(jié)點不需要專用硬件闻镶。

  5. 仲裁節(jié)點將永遠是仲裁節(jié)點,但主節(jié)點可能變?yōu)榇我?jié)點丸升,次要節(jié)點也可能通過選舉成為主要節(jié)點铆农。

異步復(fù)制

  1. 次要節(jié)點異步的應(yīng)用來自主節(jié)點的操作。通過在主節(jié)點之后應(yīng)用操作狡耻,副本集可以不管一個或多個成員的失敗而繼續(xù)實現(xiàn)其功能墩剖。

自動故障切換

  1. 當(dāng)主節(jié)點超過十秒沒有與副本集的其它成員通信,一個合格的次要節(jié)點將舉行選舉來將它自己選舉為新的主節(jié)點夷狰。第一個次要節(jié)點舉行選舉岭皂,并在接收多數(shù)成員的投票后成為主節(jié)點。
  2. MongoDB 3.2 版本后引入了版本 1 的復(fù)制協(xié)議孵淘,它減少了副本集的故障切換時間并加速了多個同時存在的主節(jié)點的檢測蒲障。
  3. 雖然時間不同,故障轉(zhuǎn)移過程一般在一分鐘之內(nèi)完成瘫证。

讀操作

  1. 默認的,客戶端從主節(jié)點進行讀操作庄撮,然而可以通過指定讀偏好來將讀操作發(fā)送到次要節(jié)點背捌。次要節(jié)點的異步復(fù)制意味著從次要節(jié)點進行的讀操作可能返回沒有反映主節(jié)點上數(shù)據(jù)狀態(tài)的數(shù)據(jù)。
  2. MongoDB 中洞斯,客戶端可以在寫操作持久化之前看到其結(jié)果毡庆。

額外的特性

副本集提供多種支持應(yīng)用需求的選項±尤纾可以部署成員在多個數(shù)據(jù)中心的副本集么抗,或通過調(diào)整一些成員的 members[n].priority 來控制選舉主節(jié)點的結(jié)果。副本集也支持專用于報告亚铁、災(zāi)難恢復(fù)或備份功能的成員蝇刀。

副本集成員

  1. 主節(jié)點接收所有寫操作。
  2. 次要節(jié)點從主節(jié)點復(fù)制操作來保持與主節(jié)點相同的數(shù)據(jù)集徘溢。次要節(jié)點可以有特殊配置文件的附加配置吞琐。比如捆探,次要節(jié)點可以對選舉棄權(quán)或優(yōu)先級為 0。
  3. 副本集的推薦最低配置是帶有三個數(shù)據(jù)承載點三成員副本集:一個主節(jié)點和兩個次要節(jié)點站粟。也可以部署為兩個數(shù)據(jù)承載點的三成員副本集:一個主節(jié)點黍图,一個次要節(jié)點和一個仲裁節(jié)點。但這種模式不如三數(shù)據(jù)承載點的模式冗余性好奴烙。
  4. 3.0 版本的變化:副本集最多可以有 50 名成員助被,但只有 7 名投票成員。

主節(jié)點

  1. 主節(jié)點是副本集中接收寫操作的唯一成員切诀。MongoDB 在主節(jié)點上應(yīng)用寫操作揩环,然后將操作記錄在主節(jié)點的 oplog 上。次要節(jié)點復(fù)制 oplog 并將操作應(yīng)用到它們的數(shù)據(jù)集上趾牧。

次要節(jié)點

  1. 次要節(jié)點維護主節(jié)點數(shù)據(jù)集的一個副本检盼。復(fù)制數(shù)據(jù)時,次要節(jié)點在異步過程中從主節(jié)點的 oplog 應(yīng)用操作到它自己的數(shù)據(jù)集翘单。副本集可以有一個或多個次要節(jié)點吨枉。
  2. 次要節(jié)點可以通過配置來達到實現(xiàn)一些效果:
    • 防止被選舉為主節(jié)點,以便允許它留在次要節(jié)點數(shù)據(jù)中心或作為冷備用哄芜。
    • 防止應(yīng)用從中讀取貌亭,以便允許它運行需要與正常流量分離的應(yīng)用。
    • 保存一個運行的歷史快照认臊,以便從某些錯誤中恢復(fù)圃庭,如誤刪數(shù)據(jù)庫。

仲裁節(jié)點

  1. 仲裁節(jié)點沒有數(shù)據(jù)集的副本失晴,并且不能變成主節(jié)點剧腻。副本集可以有仲裁節(jié)點來在選舉主節(jié)點中投票帐我。仲裁節(jié)點永遠有一張選票枪汪,這樣就允許副本集的投票成員數(shù)量不均等攀圈,而無需承擔(dān)一個復(fù)制數(shù)據(jù)的成員的額外開銷渤涌。
  2. 重要:不要在還承載副本集的主節(jié)點或次要節(jié)點的系統(tǒng)上運行仲裁節(jié)點氧敢。

副本集 oplog

  1. oplog:操作日志第献,一個特殊的固定集合衬吆,保持所有修改數(shù)據(jù)庫上數(shù)據(jù)的操作的滾動記錄秸滴。
  2. 副本集的所有成員都包含一份 oplog 的副本帖族,在 local.oplog.rs 中栈源,這允許它們維護數(shù)據(jù)庫的當(dāng)前狀態(tài)。
  3. 為了減輕復(fù)制的困難竖般,所有的副本集成員成員都發(fā)送心跳(ping)到所有其它成員甚垦。
  4. 任何成員都可以從其它成員那里導(dǎo)入 oplog。
  5. oplog 中的每個操作都是冪等(idempotent)的。即制轰,oplog 對目標(biāo)資料應(yīng)用不管一次或多次操作前计,都產(chǎn)生相同的結(jié)果。

oplog 大小

  1. 當(dāng)?shù)谝淮伍_始一個副本集成員時垃杖,MongoDB 以默認大小創(chuàng)建一個 oplog男杈。
  2. 如果可以預(yù)料副本集工作量的大小,可以將 oplog 設(shè)置為比默認值大些调俘。相反伶棒,如果應(yīng)用主要用來執(zhí)行讀操作和少量寫操作,一個小的 oplog 可能就足夠了彩库。
  3. 以下情況可能需要較大的 oplog:
    • 一次更新多個文檔
    • 刪除與插入量大致相同的數(shù)據(jù)時
    • 大量的就地更新

oplog 狀態(tài)

  1. 使用 rs.printReplicationInfo() 方法來查看 oplog 狀態(tài)肤无,包括其大小和操作的時間范圍。

2. 在 docker 中創(chuàng)建副本集

準(zhǔn)備工作

  1. 安裝 docker

  2. 下載 mongo 鏡像骇钦,如有需求可加上版本號

    docker pull mongo
    

概覽

  1. 我們要從 mongo 鏡像創(chuàng)建三個容器宛渐,都處在它們的 docker 容器網(wǎng)絡(luò)內(nèi)。
  2. 這三個容器將被命名為 mongo1眯搭、mongo2 和 mongo3窥翩。它們將作為副本集的三個 mongo 實例。
  3. 暴露它們的端口到本地機器鳞仙,以便可以從本地機器的 mongo shell 來訪問它們中的任意一個寇蚊。
  4. 這三個 mongo 容器中的每一個都應(yīng)該能與這個網(wǎng)絡(luò)中的所有其它容器通信。

建立網(wǎng)絡(luò)

  1. 創(chuàng)建一個名為 my-mongo-cluster 的網(wǎng)絡(luò):

    docker network create my-mongo-cluster
    
  2. 查看當(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
  1. docker run: 從鏡像啟動容器
  2. -p 30001:27017:暴露容器中的 27017 端口棍好,映射到本機的 30001 端口
  3. --name mongo1:給這個容器命名為 mongo1
  4. --net my-mongo-cluster:將此容器添加到 my-mongo-cluster 網(wǎng)絡(luò)
  5. mongo:用來生成此容器的鏡像名稱
  6. 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)在來把它們編程副本集扒怖。

  1. 這條命令將在運行的容器 mongo1 中打開 mongo shell:

    docker exec -it mongo1 mongo
    
  2. 在 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 地址盼忌。
  3. 通過以下命令啟動副本集:

    rs.initiate(config)
    

    如果一切順利,提示符將變成這樣:

    my-mongo-set:PRIMARY>
    

    這意味著 shell 現(xiàn)在與 my-mongo-set 集群中的 PRIMARY 數(shù)據(jù)庫進行了關(guān)聯(lián)掂墓。

  4. 下面測試副本集是否工作谦纱,先在 primary 數(shù)據(jù)庫中插入數(shù)據(jù):

    > db.mycollection.insert({name : 'sample'})
    WriteResult({ "nInserted" : 1 })
    > db.mycollection.find()
    { "_id" : ObjectId("57761827767433de37ff95ee"), "name" : "sample" }
    
  5. 然后新建一個與 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ǔ)鏡像

  1. 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 []
    
  2. 構(gòu)建基礎(chǔ)鏡像

    docker build -t mongo_set_base .
    

mongo 副本集鏡像

  1. 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" ] 
    
  2. 構(gòu)建副本集鏡像

    docker build -t mongod .
    
  3. 從鏡像 mongod 啟動容器 mongo1

    docker run \
    -p 30001:27017 \
    --name mongo1 \
    --net my_mongo_cluster \
    mongod
    
  4. 順利的話會看到提示

    [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

  1. 以交互模式進入主節(jié)點的 shell(可以連接任一容器)

    docker exec -it mongo1 mongo
    
  2. 執(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)
    
  3. 如果一切正常祠乃,會出現(xiàn)以下提示梦重,表示副本集已創(chuàng)建成功,目前在主節(jié)點中

    my-mongo-set:PRIMARY>
    

測試副本集

  1. 插入一條文檔

    db.mycollection.insert({name : 'sample'})
    
  2. 查看是否成功插入

    db.mycollection.find()
    
  3. 另開一個 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)

使用 mgo 已經(jīng)可以正常插入和查詢數(shù)據(jù)

在 mongo shell 中可以參照 “2. 在 docker 中創(chuàng)建副本集” 相關(guān)步驟

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嘱支,隨后出現(xiàn)的幾起案子蚓胸,更是在濱河造成了極大的恐慌,老刑警劉巖除师,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沛膳,死亡現(xiàn)場離奇詭異,居然都是意外死亡汛聚,警方通過查閱死者的電腦和手機锹安,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倚舀,“玉大人叹哭,你說我怎么就攤上這事∶榻埃” “怎么了话速?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長芯侥。 經(jīng)常有香客問我泊交,道長,這世上最難降的妖魔是什么柱查? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任廓俭,我火速辦了婚禮,結(jié)果婚禮上唉工,老公的妹妹穿的比我還像新娘研乒。我一直安慰自己,他們只是感情好淋硝,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布雹熬。 她就那樣靜靜地躺著,像睡著了一般谣膳。 火紅的嫁衣襯著肌膚如雪竿报。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天继谚,我揣著相機與錄音烈菌,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛芽世,可吹牛的內(nèi)容都是我干的挚赊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼济瓢,長吁一口氣:“原來是場噩夢啊……” “哼荠割!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起葬荷,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤涨共,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后宠漩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體举反,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年扒吁,在試婚紗的時候發(fā)現(xiàn)自己被綠了火鼻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡雕崩,死狀恐怖魁索,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盼铁,我是刑警寧澤粗蔚,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站饶火,受9級特大地震影響鹏控,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肤寝,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一当辐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鲤看,春花似錦缘揪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至慷吊,卻和暖如春呻征,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背罢浇。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嚷闭。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓攒岛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胞锰。 傳聞我的和親對象是個殘疾皇子灾锯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容