2021 iThome 鐵人賽 - DAY18 MongoDB Replication 實戰

2021 iThome 鐵人賽 MongoDB披荊斬棘之路


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. 再拿之前的範例來修改就完成了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 初始化的動作,這時候直接執行會出現…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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() 會出現什麼…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
➜  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 語法來撈出我們要看的內容。

1
2
3
4
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

在主節點寫入資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜  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

次節點查看

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
➜  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 預設是關閉查詢的,我們只需要打開即可。

1
2
3
4
5
ith2021-rs:SECONDARY> rs.slaveOk()

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

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

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

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

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

  • 作者: MingYi Chou
  • 版權聲明: 轉載不用問,但請註明出處!本網誌均採用 BY-NC-SA 許可協議。