iT邦幫忙

2021 iThome 鐵人賽

DAY 18
0
Software Development

MongoDB披荊斬棘之路系列 第 18

DAY18 MongoDB Replication 實戰

DAY18 MongoDB Replication 實戰

本篇我們要使用 Dcoker compose 方式實作 MongoDB replication,又快速又方便,如果不太熟的可以去參考之前文章 DAY2 安裝與使用 MongoDB


上一篇文章有提到(怎麼又是之前文章啊?這樣代表我的文章有連貫性啊) MongoDB replication 至少需要有三個實體。

首先我們建立一個 yml 檔案 docker-compose.yml,開始寫我們計劃中的設定。

  1. 替這次 replication 取個名字叫做 ith2021-rs

  2. 三個節點名稱與port分別為

    • mongo_node1 : 27666
    • mongo_node2 : 27667
    • mongo_node3 : 27668
  3. 再拿之前的範例來修改就完成了!

version: '3'
services:
    mongo_node1:
        container_name: mongo_node1
        image: mongo
        ports:
            - 27666:27666
        command: mongod --port 27666 --bind_ip_all --replSet ith2021-rs
    mongo_node2:
        container_name: mongo_node2
        image: mongo
        ports:
            - 27667:27667
        command: mongod --port 27667 --bind_ip_all --replSet ith2021-rs
    mongo_node3:
        container_name: mongo_node3
        image: mongo
        ports:
            - 27668:27668
        command: mongod --port 27668 --bind_ip_all --replSet ith2021-rs

我們可以看到這次啟動的指令多了 --replSet ith2021-rs,就是指定 replica set 的名稱。

設定 replica set

透過上面的 yml 啟動後確實可以看到三個實體,但它們三個還不認識彼此,這時候就必須進行 replica set 初始化的動作,這時候直接執行會出現...

> rs.initiate()
{
	"info2" : "no configuration specified. Using a default configuration for the set",
	"me" : "17dca5869ff9:27666",
	"ok" : 1,
	"$clusterTime" : {
		"clusterTime" : Timestamp(1631433581, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	},
	"operationTime" : Timestamp(1631433581, 1)
}

它會告訴你沒有任何設定檔,所以用預設值來設定。這時候就很麻煩,需手動敲指一個一個節點加進去 replica set。

懶惰如我,我們把這段用指令方式放進 docker compose 的 healthcheck 方式自動執行,這樣就可以省掉一個動作了。(注意啊~這是本機方便快速測試用才會這樣做,部署到其他環境還是要抽出到正確的職責位置)

完整的 yml

version: '3'
services:
    mongo_node1:
        container_name: mongo_node1
        image: mongo
        ports:
            - 27666:27666
        command: mongod --port 27666 --bind_ip_all --replSet ith2021-rs
    mongo_node2:
        container_name: mongo_node2
        image: mongo
        ports:
            - 27667:27667
        command: mongod --port 27667 --bind_ip_all --replSet ith2021-rs
    mongo_node3:
        container_name: mongo_node3
        image: mongo
        ports:
            - 27668:27668
        command: mongod --port 27668 --bind_ip_all --replSet ith2021-rs
        healthcheck:
            test: ["CMD","mongo","--host","mongo_node3","--port","27668",
                "--eval", 'rs.initiate( { _id : "ith2021-rs", members:
                [{ _id: 0, host: "mongo_node1:27666" },
                 { _id: 1, host: "mongo_node2:27667" },
                 { _id: 2, host: "mongo_node3:27668" }]})']
            interval: 10s

啟動後隨便連入一台 MongoDB 看看,再次輸入 rs.initiate() 會出現什麼...

➜  source git:(master) ✗ mongo --host 127.0.0.1 --port 27666
MongoDB shell version v4.0.3
connecting to: mongodb://127.0.0.1:27666/

//中略

ith2021-rs:SECONDARY> rs.initiate()
{
	"ok" : 0,
	"errmsg" : "already initialized",
	"code" : 23,
	"codeName" : "AlreadyInitialized",
	"$clusterTime" : {
		"clusterTime" : Timestamp(1631434164, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	},
	"operationTime" : Timestamp(1631434164, 1)
}
ith2021-rs:SECONDARY>

會告訴你已經初始化完成了。


驗證資料同步

要如何驗證資料同步呢?最簡單方式就是寫入一筆資料,確認次節點有沒有同步就可以了。還記得我們提到僅有主節點能夠執行資料修改內容,於是我們先隨便連進一台,接著查看 Primary 節點是哪一台。

尋找 Primary

指令是 rs.status() 不過內容太多了,我們只需要查看這個 rs 內的會員即可。

rs.status().members

不過內容細節還是很多,我就不在這貼出來佔版面,可以使用一點 js 語法來撈出我們要看的內容。

ith2021-rs:SECONDARY> rs.status().members.forEach(x=>print(`${x.name}/${x.stateStr}`))
mongo_node1:27666/SECONDARY
mongo_node2:27667/SECONDARY
mongo_node3:27668/PRIMARY

在主節點寫入資料

➜  source git:(master) ✗ mongo --host 127.0.0.1 --port 27668
MongoDB shell version v4.0.3
connecting to: mongodb://127.0.0.1:27668/

// 中略

ith2021-rs:PRIMARY> use testdb
switched to db testdb

ith2021-rs:PRIMARY> db.testcol.insert({field:'iThome 2021 ironman BEST!'})
WriteResult({ "nInserted" : 1 })

ith2021-rs:PRIMARY> exit
bye

次節點查看

剛剛已經在主節點寫入資料了,讓我們來隨便連到一個次節點看看有沒有這筆資料囉

➜  source git:(master) ✗ mongo --host 127.0.0.1 --port 27666
MongoDB shell version v4.0.3
connecting to: mongodb://127.0.0.1:27666/

// 中略

ith2021-rs:SECONDARY> use testdb
switched to db testdb
ith2021-rs:SECONDARY> db.testcol.find()
Error: error: {
	"topologyVersion" : {
		"processId" : ObjectId("613db58d9e315ff6c1c2fe94"),
		"counter" : NumberLong(4)
	},
	"ok" : 0,
	"errmsg" : "not master and slaveOk=false",
	"code" : 13435,
	"codeName" : "NotPrimaryNoSecondaryOk",
	"$clusterTime" : {
		"clusterTime" : Timestamp(1631434784, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	},
	"operationTime" : Timestamp(1631434784, 1)
}
ith2021-rs:SECONDARY>

沒想到居然跳錯誤,但是也不用太緊張,這是因為 secondary 預設是關閉查詢的,我們只需要打開即可。

ith2021-rs:SECONDARY> rs.slaveOk()

ith2021-rs:SECONDARY> db.testcol.find()
{ "_id" : ObjectId("613db79ea937743f1dc6af82"), "field" : "iThome 2021 ironman BEST!" }
ith2021-rs:SECONDARY>

就看到這筆資料囉~代表資料真的是有自動同步到次節點。

我想說的是這指令 rs.slaveOk().. ok 你個頭
其實這指令已經被改為 rs.secondaryOk() 
在 MongoDB 裡面逐漸把 Master/Slave 改為 Primary/Secondary 了
不知道什麼時候會完全移除這些指令就是了,所以請記得改用 rs.secondaryOk()

建立 replica set 介紹就差不多到這邊了,架設測試用的環境不會遇到太大的困難,後面要加入密碼和key會稍微麻煩一點,不過不在這次的範圍內,也許之後再找時間來發教學文了。

後面開始會講一點維運的東西,跟 oplog 息息相關。


本系列文章會同步發表於我個人的部落格 Pie Note


上一篇
DAY17 MongoDB Replication 觀念
下一篇
DAY19 MongoDB Oplog 是什麼?邁向高手之路
系列文
MongoDB披荊斬棘之路30

尚未有邦友留言

立即登入留言