本篇我們要使用 Dcoker compose
方式實作 MongoDB replication,又快速又方便,如果不太熟的可以去參考之前文章 DAY2 安裝與使用 MongoDB。
上一篇文章有提到(怎麼又是之前文章啊?這樣代表我的文章有連貫性啊) MongoDB replication 至少需要有三個實體。
首先我們建立一個 yml 檔案 docker-compose.yml
,開始寫我們計劃中的設定。
替這次 replication 取個名字叫做 ith2021-rs
三個節點名稱與port分別為
再拿之前的範例來修改就完成了!
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 的名稱。
透過上面的 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
節點是哪一台。
指令是 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