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


<<:  JavaScript学习日记 : Day6 - 函数(一)

>>:  Day18 Lab 2 - Object storage metadata

第16天 - PHP 简易登入(3)_判断身分

接续昨天的部分,今天的登入会依照你的身分进入不同的页面(以会员、管理员为例)。 首先先增加资料表的内...

Day29:今天来聊一下如何建立及管理 Azure Sentinel 威胁搜捕查询

Azure Sentinel包含功能强大的查询工具,可协助为资安人员找出并隔离公司环境内安全 性威胁...

Day 7 - 原型 (6): 预览主页

前言 今天利用之前所建的主页, 建立一个可让使用者互动的原型。 预览 利用Figma的预览功能, 即...

[DAY1]SQL的新手懒人笔记

有监於需要学的东西太多,所以做个最懒人的笔记给未来忘记语法的自己看。(大写为内建语法) 1.SELE...

day17_Windows ARM 的网站开发之旅

既然 Linux ARM 可以开发网站,那 Windows ARM 可以当日常的网站开发吗? 我们这...