如何在 Ubuntu 20.04 上配置 MongoDB 复制集

此教程的早期版本是由 Justin Ellingwood 撰写的。

介绍

MongoDB是一个以文档为导向的数据库,用于许多现代Web应用程序。它被归类为NoSQL数据库,因为它不依赖于传统的基于表的关系数据库结构。相反,它使用具有动态方案的JSON类型的文档。这意味着,与关系数据库不同,MongoDB在将数据添加到数据库之前不需要预先定义的方案。

在与数据库工作时,有时有助于拥有多个数据副本,这在一个数据库服务器失败的情况下提供冗余性,可以提高数据库的可用性和可扩展性,以及减少读取延迟。

本教程简要概述了复制如何在MongoDB中工作,并概述了如何配置和启动一个复制集,其中有三个成员。

<$>[注] :请注意,本指南中描述的程序旨在向您展示如何快速设置和运行复制品。完成本教程后,您将有一个功能齐全的复制品集,但不会有任何安全功能启用。

对于使用复制的生产部署,MongoDB 文档建议(https://docs.mongodb.com/manual/tutorial/deploy-sharded-cluster-with-keyfile-access-control/index.html# keyfile-security)使用 x.509 身份验证,并将 keyfiles 描述为仅限于最低限度的安全形式,这些形式最适合测试或开发环境

如果您打算使用您的复制片集进行测试或开发,我们强烈鼓励您遵循我们关于 Ubuntu 20.04 上的 MongoDB 复制片集如何配置 Keyfile Authentication 的教程(https://andsky.com/tech/tutorials/how-to-configure-keyfile-authentication-for-mongodb-replica-sets-on-ubuntu-20-04)。

前提条件

要完成本指南,您将需要:

  • 三个服务器,每个运行Ubuntu 20.04. 所有三个服务器都应该有一个非根管理用户和一个与UFW配置的防火墙. 要设置这个,请遵循我们的 Ubuntu 20.04初始服务器安装指南
  • MongoDB安装在您的Ubuntu服务器。

请注意,为了澄清,本指南将提到三个服务器为 mongo0 , ** mongo1** 和 ** mongo2** . 显示在 ** mongo0** 上执行的命令或文件更改的任何示例都将具有蓝色背景,如下:

1[environment second]

mongo1 上执行的命令和文件更改将具有粉红色背景:

1[environment third]

mongo2 上的示例操作将具有绿色背景:

1[environment fourth]

最后,必须运行的命令或必须在 每个 服务器上进行的文件更改将具有标准的黑色背景,如下:

了解 MongoDB 复制集

正如介绍中所提到的,MongoDB 通过一个被称为 replica sets 的实现来处理复制。 每个运行的 MongoDB 实例被称为其中的一个 _members。

主要成员是与复制件集的交易的主要访问点,是唯一可以接受写作操作的成员。每个复制件集一次只能有一个主要成员,因为复制是通过复制主要的 oplog(简称操作日志)并重复在次要件的各自数据集上记录的更改。

默认情况下,应用程序只会查询主要成员的阅读和写作操作。您可以配置您的设置以便从一个或多个次要成员中读取,但由于数据是无同步传输的,从次要节点读取可以导致旧数据被服务。

区分 MongoDB 复制集与其他复制实现的一个特征是它们的自动故障转移机制. 如果主要成员变得不可用,则在次要节点之间发生自动选举过程,以选择新的主要集合。

但是,如果二级会员组包含相等数量的节点,这可能会导致无法选择新的初级会员,因为投票陷入僵局。这将需要将第三类会员纳入复制集:一个 arbiter。 仲裁员是复制集的可选成员,在这种情况下投票以确保组能够做出决定。 但是,请注意,仲裁员没有数据集的副本,他们被禁止成为复制集的主要成员。

可能有时,您不希望所有副本都遵循副本组的标准规则,MongoDB 允许您将副本组的副本组配置为以下非标准角色:

*** 优先事项0 复制成员** : 在某些情况下,某些固定成员当选担任主要职务可能会对您的申请业绩产生不利影响。 例如,如果将数据复制到远程数据中心或某个二级成员的硬件上,不足以使其作为数据集的主要接入点发挥作用,那么将0作为优先级可以确保该成员不会成为主要成员,但可以继续复制数据。

  • ** 隐藏复制成员** :有些情况要求您保持一组成员对客户的可访问性和可见性,同时隐藏背景成员,这些背景成员有不同的目的,不应该用于读取操作. 例如,你可能需要一名次要成员作为分析工作的基础,分析工作将受益于最新的数据集,但将对工作成员造成压力。 通过使这个成员被隐藏,它不会干扰复制集的一般操作. 隐藏成员必须被确定为`0'的优先事项,以避免成为主要成员,但他们可以在选举中投票。
  • ** 延迟复制成员** : 通过设定一个次要成员的延迟选项,您可以控制次要的等待时间,以完成从初级的oplog复制的每个动作. 如果您想防止意外删除或从破坏行动中恢复,这样做是有用的。 例如,如果将二次操作延迟了半天,它不会立即在自己的一组数据上进行意外操作,并可用于回放更改. 被拖延的成员不能成为主要成员,但可以在选举中投票。 在大多数情况下,它们也应当被隐藏起来来防止应用过程读取过时的数据.

步骤 1 – 配置 DNS 分辨率

在步骤 4 中初始化您的复制组时,您需要提供一个地址,每个复制组成员可以由组中的其他两个成员访问。 MongoDB 文档建议在配置复制组时不要使用 IP 地址,因为 IP 地址可能会意外改变。

其中一种方法是 配置每个复制成员的子域。虽然配置子域对于生产环境或其他长期解决方案是理想的,但本教程将概述如何通过编辑每个服务器的各自主机文件来配置 DNS 分辨率。

「主機」是一個特殊的檔案,允許您將人類可讀的主機名稱分配給數字 IP 地址,這意味著如果您的任何伺服器的 IP 地址發生變化,您只需要在三個伺服器上更新「主機」檔案,而不是重新設定複製集。

在Linux和其他类似Unix的系统中,主机存储在/etc/目录中.在您的三个服务器中,您可以使用您偏好的文本编辑器编辑文件。

1sudo nano /etc/hosts

在配置 localhost 的前几行后,为复制组的每个成员添加一个条目. 这些条目以 IP 地址的形式,然后是您选择的可人读的名称,如本示例所示:

1[label /etc/hosts]
2IP_address any_hostname

您可以配置您的服务器以使用您想要的任何主机名称,但使每个主机名称具有描述性可能是有用的。

  • mongo0.replset.member
  • mongo1.replset.member
  • mongo2.replset.member

使用这些主机名称,您的/etc/hosts文件看起来类似于以下突出的行:

1[label /etc/hosts]
2. . .
3127.0.0.1 localhost
4
5203.0.113.0 mongo0.replset.member
6203.0.113.1 mongo1.replset.member
7203.0.113.2 mongo2.replset.member
8. . .

<$>[info] 如果你不知道你的服务器的IP地址,你可以在每个服务器上运行以下‘curl’命令来获取它们。 `icanhazip.com’是一个显示任何计算机的IP地址的网站。

1curl -4 icanhazip.com

如果您正在使用 DigitalOcean Droplets,您也可以在 控制面板中找到服务器的 IP 地址。

您在这里添加的新行应该在您集中的三个主机上相同。 将文件保存并关闭在您的每个服务器上. 如果您使用nano来编辑这些文件,请按CTRL + X,Y,然后按ENTER

在您的每个服务器上编辑、保存和关闭主机文件后,您将完成为您的复制组配置DNS分辨率,您现在可以继续更新每个服务器的防火墙规则,以允许它们相互通信。

步骤 2 — 使用 UFW 更新每个服务器的防火墙配置

假设您遵循了 初始服务器设置指南的先决条件,您将在您安装 MongoDB 的每个服务器上设置防火墙,并允许访问OpenSSH UFW 配置文件。

但是,这些防火墙还会阻止每个服务器上的 MongoDB 实例相互通信,从而阻止您启动复制集,以便更正此问题,您需要添加新的防火墙规则,以允许每个服务器访问其他两个服务器上的端口,其中 MongoDB 正在收听连接。

mongo0 上,运行以下ufw命令,允许 ** mongo1** 在 ** mongo0** 上访问端口 27017:

1[environment second]
2sudo ufw allow from mongo1_server_ip to any port 27017

请务必更改mogno1_server_ip,以反映您的 mongo1 服务器的实际 IP 地址。 请注意,ufw命令不会与hosts文件中配置的主机名称一起工作,所以请务必在这个命令和下列命令中使用您的服务器的实际 IP 地址。

然后添加另一个防火墙规则,让 mongo2 访问相同的端口:

1[environment second]
2sudo ufw allow from mongo2_server_ip to any port 27017

接下来,更新您的其他两个服务器的防火墙规则,在 mongo1 上运行以下命令,确保更改 IP 地址以反映 ** mongo0** 和 ** mongo2** 的地址:

1[environment third]
2sudo ufw allow from mongo0_server_ip to any port 27017
3sudo ufw allow from mongo2_server_ip to any port 27017

最后,在 mongo2 上运行这两个命令. 再次,请确保您为每个服务器输入正确的IP地址:

1[environment fourth]
2sudo ufw allow from mongo0_server_ip to any port 27017
3sudo ufw allow from mongo1_server_ip to any port 27017

添加这些 UFW 规则后,您的三台 MongoDB 服务器将被允许访问其他两个服务器上的 MongoDB 使用的端口. 但是,您将无法测试这一点,因为每个服务器上的 Mongo 实例目前正在阻止任何外部连接。

步骤 3 — 在每个服务器的 MongoDB 配置文件中启用复制

在这一点上,你已经编辑了你的服务器的/etc/hosts文件,以配置主机名称,这些名称将解决每个服务器的IP地址。你还打开了每个服务器的防火墙,允许其他两个服务器访问默认的MongoDB端口,27107。

此步骤概述了如何通过编辑 MongoDB 的配置文件, /etc/mongod.conf. 您必须在每个服务器上完成此步骤中的每个程序,但为了演示目的,我们将在示例中使用 mongo0

mongo0 上,在您喜爱的文本编辑器中打开 MongoDB 配置文件:

1[environment second]
2sudo nano /etc/mongod.conf

即使您已打开每个服务器的防火墙,以允许其他服务器访问端口 27017,MongoDB 目前已绑定到 127.0.0.1,即本地路由网络接口,这意味着MongoDB 只能接受来自其安装的服务器的连接。

要允许远程连接,您必须将 MongoDB 连接到您的服务器的公开可路由的 IP 地址,除了127.0.0.1

查找网络接口部分,默认情况下将看起来像这样:

1[label /etc/mongod.conf]
2[environment second]
3. . .
4# network interfaces
5net:
6  port: 27017
7  bindIp: 127.0.0.1
8. . .

添加一个字符串以 bindIp: 开头的行,然后是 mongo0 的主机名或公共 IP 地址。

1[label /etc/mongod.conf]
2[environment second]
3. . .
4# network interfaces
5net:
6  port: 27017
7  bindIp: 127.0.0.1,mongo0.replset.member
8. . .

接下来,找到读到# replication:的行到文件底部,它将看起来像这样:

1[label /etc/mongod.conf]
2[environment second]
3. . .
4#replication:
5. . .

然后在这个行下面添加一个replSetName指令,然后添加一个名称,MongoDB 将用来识别复制组:

1[label /etc/mongod.conf]
2[environment second]
3. . .
4replication:
5  replSetName: "rs0"
6. . .

在本示例中,replSetName指令的值为rs0。你可以在这里提供任何你想要的名称,但使用一个描述性的名称可能是有帮助的. 但是,请记住,每个服务器的mongod.conf文件必须在replSetName指令之后具有相同的名称,以便每个MongoDB实例成为相同复制组的成员。

請注意,在「replSetName」指令之前有兩個空白,並且名稱被包裝在引文標記(")中,這兩個空白都是必要的,以便適當閱讀此配置。

更新文件的这两个部分,netreplication后,它们将看起来像这样:

 1[label /etc/mongod.conf]
 2[environment second]
 3. . .
 4# network interfaces
 5net:
 6  port: 27017
 7  bindIp: 127.0.0.1,mongo0.replset.member
 8. . .
 9replication:
10  replSetName: "rs0"
11. . .

然后在 mongo1 和 ** mongo2** 上对 /etc/mongod.conf 文件进行相同的更改。

 1[label /etc/mongod.conf]
 2[environment third]
 3. . .
 4# network interfaces
 5net:
 6  port: 27017
 7  bindIp: 127.0.0.1,mongo1.replset.member
 8. . .
 9replication:
10  replSetName: "rs0"
11. . .

以下是这些部分在 mongo2 的配置文件中的样子:

 1[label /etc/mongod.conf]
 2[environment fourth]
 3. . .
 4# network interfaces
 5net:
 6  port: 27017
 7  bindIp: 127.0.0.1,mongo2.replset.member
 8. . .
 9replication:
10  replSetName: "rs0"
11. . .

再次重申,您在每个服务器的bindIp 指令中添加的 IP 地址或主机名称必须是您正在编辑的mongod.conf 文件的服务器。

在对每个服务器的mongod.conf文件进行这些更改后,保存并关闭每个文件,然后通过发出以下命令重新启动每个服务器上的mongod服务:

1sudo systemctl restart mongod

通过此,您已为每个服务器的 MongoDB 实例启用复制。

<$>[注] **注:此时,您可以使用nc命令来测试您在步骤 2 中添加的防火墙规则是否正确。

以下示例nc命令包括-z选项,该选项限制了实用程序仅在目标服务器上扫描听取代门而不向其发送任何数据。从 前提安装教程中回忆说,MongoDB作为服务代门运行,使此选项用于测试连接性。

此示例命令 nc 显示了从 mongo0 到达 ** mongo1** 的尝试:

1[environment second]
2nc -zv mongo1.replset.member 27017

以下输出表示 mongo0 可以在 MongoDB 使用的端口达到 ** mongo1** :

1[secondary_label Output]
2[environment second]
3Connection to mongo1.replset.member 27017 port [tcp/*] succeeded!

您可以通过在每个服务器上重复这个命令来测试每个服务器之间的连接,并指定相应的主机名称或 IP 地址。

编辑每个服务器的mongod.conf文件以启用复制并重新启动mongod服务后,您准备启动复制集并将每个Mongo实例添加为会员。

步骤 4 – 启动复制集并添加会员

现在,您已经配置了三个 MongoDB 安装,您可以打开一个 MongoDB 壳来启动复制,并将每个安装添加为会员。

为了演示目的,本步骤中的示例将使用 mongo0 上的 MongoDB 实例来启动复制集,但是,您可以从任何已配置适当的 mongod.conf 文件的服务器启动复制。

mongo0 上,打开 MongoDB 壳:

1[environment second]
2mongo

从提示,您可以通过运行rs.initiate()方法启动mongo壳中的复制集,但是,运行此方法本身只会启动您运行该方法的机器的复制,然后您需要通过发行每个成员的rs.add()方法来添加您的其他Mongo实例。

请记住,MongoDB 将其数据存储在 JSON 类似的结构中,称为 documents. 因为您已经在每个服务器上编辑了 mongod.conf 文件,以便为复制配置三个 Mongo 实例,您可以代替在 rs.initiate 方法中包含每个成员的配置细节的文档。

要做到这一点,请通过键入下列方式开始一个 rs.initiate() 方法,然后按一下 ENTER:

1[environment second]
2rs.initiate(

Mongo 将不会将 rs.initiate 方法注册为完整,直到您输入一个关闭组合。

与 JSON 中的对象一样,MongoDB 中的文档始于和结束于弯曲的弯曲({和 `}')。 若要开始添加复制集的配置文档,请输入一个打开弯曲的弯曲:

1[environment second]
2{

MongoDB 文档由任何数量的 field-and-value 对组成,这些对以 field:value 的形式组成。本文档的第一个 field-and-value 对必须是一个 _id: 字段,该字段提供一个名称来识别复制组;这个字段的值必须与您在您的 mongod.conf 文件中设置的 replSetName 指令相同,这在我们的示例中是 `"rs0" 。

输入此字段和值对,然后按ENTER,开始一个新行:

1[environment second]
2_id: "rs0",

接下来,添加一个会员:字段,而不是一个单一的值,但是,按照这个会员:字段,一个包含多个文档的数组,每一个代表一个复制组成员添加。

添加会员:字段,然后添加一个开立的方块,以开始数组,然后按ENTER来移动到下一行:

1[environment second]
2members: [

现在添加一个包含两个字段和值对的文档以代表复制集的第一个成员,分开一个字段。 该文档的第一个字段是另一个 _id: 字段,它接受用于内部识别成员的整数。 第二个字段是 host: 字段,该字段必须是包含一个主机名称的字符串,该字段将解决到可以访问成员 Mongo 实例的地址:

1[environment second]
2{ _id: 0, host: "mongo0.replset.member" },

<$>[注] ** 注意** :如果您的 Mongo 实例运行在 MongoDB 的默认端口以外的任何一个端口上 - 27017 - 您必须跟随主机的名称,然后是端口号,如本示例所示:

1[environment second]
2{ _id: 0, host: "mongo0.replset.member:27018" },

美元

输入第一个文档后,为您的复制组的其他成员输入额外的文档. 请确保将每个文档用一个字节分开:

1[environment second]
2{ _id: 1, host: "mongo1.replset.member" },
3{ _id: 2, host: "mongo2.replset.member" }

接下来,通过输入一个关闭的平方支架来结束数组:

1[environment second]
2]

最后,用一个关闭弯曲的轴承结束配置文档,然后用一个关闭弯曲来关闭方法:

1[environment second]
2})

总体而言,rs.initiate()方法将看起来如下:

 1[environment second]
 2> rs.initiate(
 3... {
 4... _id: "rs0",
 5... members: [
 6... { _id: 0, host: "mongo0.replset.member" },
 7... { _id: 1, host: "mongo1.replset.member" },
 8... { _id: 2, host: "mongo2.replset.member" }
 9... ]
10... })

假设您正确输入了所有细节,一旦在键入关闭栏后按下ENTER,该方法将运行并启动复制集. 如果该方法在输出中返回OK:1`,则意味着复制集已正确启动:

 1[secondary_label Output]
 2[environment second]
 3{
 4    "ok" : 1,
 5    "$clusterTime" : {
 6        "clusterTime" : Timestamp(1612389071, 1),
 7        "signature" : {
 8            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
 9            "keyId" : NumberLong(0)
10        }
11    },
12    "operationTime" : Timestamp(1612389071, 1)
13}

如果复制集按预期启动,你会注意到MongoDB客户端的提示将从一个大于字符(>)的字符改变到以下:

1[environment second]

MongoDB 配备了一些内置的方法,您可以使用这些方法来管理和获取有关您的复制品集的信息,其中 rs.help() 方法特别有用,因为它会返回这些复制品集方法的列表和它们的描述:

1[environment second]
2rs.help()
 1[secondary_label Output]
 2[environment second]
 3    rs.status()                                { replSetGetStatus : 1 } checks repl set status
 4    rs.initiate()                              { replSetInitiate : null } initiates set with default settings
 5    rs.initiate(cfg)                           { replSetInitiate : cfg } initiates set with configuration cfg
 6    rs.conf()                                  get the current configuration object from local.system.replset
 7    rs.reconfig(cfg)                           updates the configuration of a running replica set with cfg (disconnects)
 8    rs.add(hostportstr)                        add a new member to the set with default attributes (disconnects)
 9    rs.add(membercfgobj)                       add a new member to the set with extra attributes (disconnects)
10    rs.addArb(hostportstr)                     add a new member which is arbiterOnly:true (disconnects)
11    rs.stepDown([stepdownSecs, catchUpSecs])   step down as primary (disconnects)
12    rs.syncFrom(hostportstr)                   make a secondary sync from the given member
13    rs.freeze(secs)                            make a node ineligible to become primary for the time specified
14    rs.remove(hostportstr)                     remove a host from the replica set (disconnects)
15    rs.secondaryOk()                               allow queries on secondary nodes
16
17    rs.printReplicationInfo()                  check oplog size and time range
18    rs.printSecondaryReplicationInfo()             check replica set members and replication lag
19    db.isMaster()                              check who is primary
20    db.hello()                              check who is primary
21
22    reconfiguration helpers disconnect from the database so the shell will display
23    an error, even if the command succeeds.

在运行rs.help()或其他方法后,您可能会看到客户端提示再次更改为下列方法:

1[environment second]

这意味着您连接的 MongoDB 实例被选为主要组成员。

请注意,如果您有额外的节点,您希望在未来将其添加到复制组中,您可以使用rs.add()方法来配置它们,就像您在之前的步骤中对当前复制组成员一样:

1[environment second]
2rs.add( "mongo3.replset.member" )

现在您可以通过按CTRL + C或运行exit命令来关闭 MongoDB 客户端:

1[environment second]
2exit

您的复制件集现在正在运行,您可以开始将其与应用程序集成。

<$>[警告] 警告 :当您打开 MongoDB 提示以启动复制集时,您可能会注意到一个警告消息如下:

1. . .
2        2021-02-03T21:45:48.379+00:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
3. . .

此消息表明您尚未为您的数据库启用访问控制。

MongoDB 使用基于角色的访问控制 (RBAC) 来管理访问 MongoDB 系统. 用户被授予一个或多个角色,决定用户访问数据库资源和操作。

由于您的任何 MongoDB 实例都未启用访问控制功能,因此在复制集中的任何三台服务器都可以访问该服务器上的 Mongo 实例。

删除此警告并将一层安全性添加到您的复制品集的一种方法是通过配置 _keyfile 身份验证。 然而,正如介绍中提到的,MongoDB 文档描述了(https://docs.mongodb.com/manual/tutorial/deploy-replica-set-with-keyfile-access-control/# keyfile-security)将其视为仅限于最低限度的安全形式,这些形式最适合测试或开发环境

请注意,对于生产部署,MongoDB 文档建议使用 x.509 证书进行内部会员身份验证。获取和配置 x.509 证书的过程伴随着一系列的警告和决策,必须根据具体情况做出决定,这超出了本教程的范围。

如果您打算使用您的复制片集进行测试或开发,我们强烈鼓励您遵循我们关于 Ubuntu 20.04 上的 MongoDB 复制片集如何配置 Keyfile Authentication 的教程(https://andsky.com/tech/tutorials/how-to-configure-keyfile-authentication-for-mongodb-replica-sets-on-ubuntu-20-04)。

结论

数据库复制已被广泛应用于提高性能、可用性和数据安全性的策略,到目前为止,建议在生产环境中使用的任何数据库都具有某种形式的复制功能。 复制也具有多功能性,可以在数据架构中扮演许多不同的角色,例如报告或灾难恢复。

如果您想了解更多关于 MongoDB 的信息,我们鼓励您查看 我们的 MongoDB 教程全集

Published At
Categories with 技术
comments powered by Disqus