事故背景
線上環境有一個MongoDB副本集,由於是部署在客戶那邊本地機房,客戶誤操作把部署副本集的另外2個節點的 VM 給刪除了(並且VM已經無法恢復了)。所幸的是還有一個節點存活,登錄節點后發現這個節點是 SECONDARY,所以可能會有一部分數據丟失,而且此時已經無法對應用提供讀寫服務。此時只能停服維護,並對集群進行恢復。
基於以上問題,下面對副本集恢復操作步鄹進行了記錄。
處理思路
- 對mongodb數據進行備份(防止恢復集群時出現意外導致數據丟失)。
- 把僅存的 SECONDARY 節點提升為 PRIMARY,刪除集群中另外2個不存活的節點,然后重新配置MongoDB副本集。
- 新部署2個MongoDB節點,並加入到集群中。
- 等待 PRIMARY 節點數據同步到另外2個新節點后,進行數據驗證,結束生產環境維護。
注意:
由於原先的集群中只存有 SECONDARY 節點,PRIMARY 節點已經丟失,所以存在部署數據沒同步到 SECONDARY 的可能。但由於PRIMARY節點的VM已經被刪,這部分未同步的數據的丟失在所難免,想恢復這部分數據只能根據自己的業務、代碼邏輯設定才有補上丟失的數據的可能性。
集群恢復
1、在SECONDARY節點刪除掛掉的primary節點
1.1 查看當前副本集配置
rs1:SECONDARY> rs.conf()
輸出內容:
rs1:SECONDARY> use admin
switched to db admin
rs1:SECONDARY> rs_conf = rs.config() { "_id" : "rs1", "version" : 7, "protocolVersion" : NumberLong(1), "members" : [ { "_id" : 0, "host" : "192.168.30.207:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "192.168.30.213:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "192.168.30.214:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("5f5094994a4d5004eae73e2f") } }
1.2 刪除集群成員
- 比如要刪除members中 host 為
192.168.30.213:27017的成員,通過rs.conf()找到成員的_id
{
"_id" : 1, "host" : "192.168.30.213:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 },
- 刪除
_id為1的成員
splice的第一個參數表示要刪除的數組元素的下標
0 表示集群中成員節點的
"_id"1 表示刪除的個數
rs1:SECONDARY> rs_conf = rs.conf()
rs1:SECONDARY> rs_conf.members.splice(0,1)
輸出內容:
rs1:SECONDARY> rs_conf.members.splice(1,1)
[
{
"_id" : 1, "host" : "192.168.30.213:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ]
依照此方法刪除副本集中不存活的節點。
注意:
有一點需要注意,由於已經刪除了
_id為1的成員,所以后面的成員的_id號都會減小1,與數組中元素的下標相同。
2、重新配置MongoDB副本集
2.1 重置集群配置
rs_conf 就是上面修改后的配置,加force參數是因為 SECONDARY 默認沒有執行此命令的權限
rs1:SECONDARY> rs.reconfig(rs_conf, {"force":true})
返回內容:
rs1:SECONDARY> rs.reconfig(rs_conf, {"force":true}) { "ok" : 1, "operationTime" : Timestamp(1619586716, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619588924, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs1:PRIMARY>
2.2 查看集群狀態
rs1:PRIMARY> rs.status()
返回內容:
{
"set" : "rs1", "date" : ISODate("2021-04-28T05:51:03.672Z"), "myState" : 1, "term" : NumberLong(17), "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1619589055, 1), "t" : NumberLong(17) }, "readConcernMajorityOpTime" : { "ts" : Timestamp(1619589055, 1), "t" : NumberLong(17) }, "appliedOpTime" : { "ts" : Timestamp(1619589055, 1), "t" : NumberLong(17) }, "durableOpTime" : { "ts" : Timestamp(1619589055, 1), "t" : NumberLong(17) } }, "members" : [ { "_id" : 0, "name" : "192.168.30.207:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 7482, "optime" : { "ts" : Timestamp(1619589055, 1), "t" : NumberLong(17) }, "optimeDate" : ISODate("2021-04-28T05:50:55Z"), "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "electionTime" : Timestamp(1619588924, 1), "electionDate" : ISODate("2021-04-28T05:48:44Z"), "configVersion" : 124340, "self" : true, "lastHeartbeatMessage" : "" } ], "ok" : 1, "operationTime" : Timestamp(1619589055, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619589055, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
此時發現,這個 SECONDARY 節點已經提升為 PRIMARY,並且集群狀態中,也就只有我們當前一個節點。
接下來就可以向副本集中添加新的MongoDB節點了。
3、添加新的MongoDB節點
這里省略新節點的部署過程,具體可以參考[《MongoDB 單節點升級為副本集高可用集群》](MongoDB 單節點升級為副本集高可用集群 - HEBIN博客 (wanhebin.com))文章中MongoDB節點部署的步鄹。
注意:
向mongodb副本集添加實例后,PRIMARY節點數據能夠自動同步到新添加的SECONDARY節點,無需人工干預。
3.1 增加實例
登錄PRIMARY節點,添加MongoDB實例。
新添加的實例優先級權重默認為1,如需調整,建議等數據同步完成后進行權重更改。
rs1:PRIMARY> use admin
rs1:PRIMARY> rs.add('192.168.30.213:27017') rs1:PRIMARY> rs.add('192.168.30.214:27017')
添加節點的返回結果如下:
rs1:PRIMARY> rs.add('192.168.30.213:27017') { "ok" : 1, "operationTime" : Timestamp(1619581966, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619581966, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs1:PRIMARY> rs.add('192.168.30.214:27017') { "ok" : 1, "operationTime" : Timestamp(1619581975, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619581975, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
3.2 刪除實例
如果添加錯節點時,可以通過 rs.remove() 來刪除錯誤的節點(因為此時當前實例已經是 PRIMARY 了,所以不需要用 1.2 中方法剔除節點)。
從mongodb副本集中移除實例,不可移除primary
rs1:PRIMARY> use admin
rs1:PRIMARY> rs.remove('192.168.30.214:27017')
返回內容:
rs1:PRIMARY> rs.remove('192.168.30.213:27017') { "ok" : 1, "operationTime" : Timestamp(1619581713, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619581713, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs1:PRIMARY> rs.remove('192.168.30.214:27017') { "ok" : 1, "operationTime" : Timestamp(1619581777, 2), "$clusterTime" : { "clusterTime" : Timestamp(1619581777, 2), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
注意:
副本集經過添加刪除后順序會亂,可以根據需要設置權重來調整。
4、調整節點權重
如果想在集群宕機恢復后,還想讓某一節點始終保持為 PRIMARY,可以把此節點的權重設置成最大。
4.1 設置權重
找到對應節點在副本集中成員_id,進行權重設置。
這里以成員0為例,其host為192.168.30.207:27017
rs1:PRIMARY> rs_conf = rs.config()
rs1:PRIMARY> rs_conf.members[0].priority=10
4.2 生效配置
rs1:PRIMARY> rs.reconfig(rs_conf)
返回結果:
rs1:PRIMARY> rs.reconfig(rs_conf)
{
"ok" : 1, "operationTime" : Timestamp(1619591404, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619591404, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
4.3 驗證權重配置
- 查詢成員0的權重
rs1:PRIMARY> rs.config()
返回內容:
{
"_id" : 0, "host" : "192.168.30.207:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 10, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }
- 模擬宕機恢復后的集群狀態
關閉三個節點的mongodb服務,再無序恢復,然后連接進節點192.168.30.207:27017,成員0依然還是PRIMARY。(為了必然偶然性,可以進行多次測試)
https://www.wanhebin.com/database/mongodb/1026.html
