我们在本书中都使用了go-demo服务。它可以帮助我们更好地理解Swarm是如何工作的。除此之外,我们对服务进行了多次扩容。这很容易做到,因为它是无状态的。可以创建任意数量的副本,而不必担心数据。数据存放在其他地方。
go-demo服务将其状态外部化到MongoDB。如果你有注意的话,我们从来没有缩放过数据库。原因很简单,不能使用简单的docker service scale命令来缩放MongoDB。
Docker Flow Proxy是从底层开始设计的,它在数据复制之前利用Swarm网络来查找其他的实例,与此不同,MongoDB是网络不可知的。它不能自动发现它的副本。让事情变得更复杂的是,只有一个实例可以是主节点,这意味着只有一个实例可以接收写请求。这就意味着我们不能使用Swarm来缩放Mongo。我们需要一种不同的方法。让我们试着配置三个MongoDB,通过创建一个副本集来使用数据复制。现在从手动过程开始,以了解可能面临的问题以及可能采用的解决方案。稍后,一旦我们达到一个令人满意的结果,就会尝试自动化该过程。
首先登录一个管理节点:
Mongo副本集的所有成员都能够相互通信,因此将创建与之前的go-demo相同的网络:
如果要创建带有三个副本的服务,Swarm将为该服务创建一个单一的网络端点,并在所有实例之间对请求做负载均衡。这种方法的问题在于MongoDB的配置。属于副本集的每个DB都需要一个固定的地址。
我们将创建三个服务,而不是创建一个服务的三个副本:
执行的命令创建了服务go-demo-db-rs1、go-demo-db-rs2和godemo-db-rs3。它们都属于go-demo网络,这样它们就可以自由地进行通信。该命令为所有服务指定了mongod—replSet "rs0",使它们都属于同一个名为rs0的Mongo副本集。请不要把Swarm副本和Mongo副本集混为一谈。虽然它们的目标相似,但背后的逻辑却截然不同。
我们应该等到所有服务都运行起来:
输出的相关部分如下(为简洁起见,移除了ID):
现在应该配置Mongo的副本集。将通过再创建一个mongo服务来做到这一点:
我们让服务成为全局的,以确保它运行在我们所在的同一节点上。这使得这个过程比试图找出它所运行的节点的IP更容易。它属于同一个go-demo网络,这样它就可以访问其他DB服务。
我们不希望在此服务中运行Mongo服务器。go-demo-db-util的目的是给我们提供一个Mongo客户端,可以使用它连接到其他数据库并配置之。因此,我们使用很长的休眠时间代替了默认的mongod命令。
要登录go-demo-db-util服务的一个容器,就需要找到它的ID:
现在已经有了在同一服务器上运行的go-demo-db-util副本的ID,就可以在容器中输入:
下一步是执行一个命令,该命令将初始化Mongo的副本集:
可以使用本地运行在godem-db-rs1中的服务器上的mongo客户端发出命令。它使用ID rs0来初始化副本集,并指定之前创建的三个服务应该是它的成员。多亏Docker Swarm网络,我们不需要知道IP,只要指定服务的名称就足够了。
响应如下:
不应只相信这一响应。让我们看一下配置:
我们向运行在go-demo-db-rs1中的远程服务器发出了另一个命令,它获取了副本集的配置。部分输出如下:
从以上代码中可以看到,副本集有三个成员(为简洁起见,删除了两个成员)。
让我们再向运行在go-demo-db-rs1中的远程Mongo发送一个命令。这一次,我们将检查副本集的状态:
部分输出如下:
为简洁起见,删除了关于两个副本的信息。
从以上代码中可以看到所有的Mongo副本都在运行。go-demo-db-rs1服务充当主节点,而其他两个服务是副节点。
设置Mongo副本集意味着数据将被复制到它的所有成员。其中一个始终是主节点,其余的则是副节点。使用当前的配置,我们只能在主节点上进行数据读/写。副本集可以配置为允许所有服务器进行读访问。写操作总是被限制在主节点上。
现在生成一些样本数据:
我们进入了运行在go-demo-db-rs1上的远程Mongo。
输出如下:
从提示符中可以看到,我们位于主数据库服务器中。
现在在数据库test中创建一些记录:(www.xing528.com)
前面的命令从数据库test中检索所有记录。
输出如下:
现在已经配置了副本集和一些样本记录,可以模拟其中一个服务器的故障并观察结果:
我们退出了MongoDB和go-demo-db-util服务副本,然后找到了go-demo-db-rs1(Mongo副本集的主节点)的IP,并列出了服务器上运行的所有容器。
输出如下(为简洁起见,移除了ID和STATUS列):
现在,可以找到go-demo-db-rs1服务副本的ID,并通过删除它来模拟故障:
让我们看一看go-demo-db-rs1任务:
Swarm发现其中一个副本发生了故障,并重新调度其运行。稍后将运行一个新的实例。
service ps命令的输出如下(为简洁起见,移除了ID):
再次进入go-demo-db-util服务副本,并输出Mongo副本集的状态:
输出的相关部分如下:
从以上代码中可以看到,go-demo-db-rs2成为了主要的Mongo副本。所发生情况的简化流程如下。
● Mongo副本go-demo-db-rs1发生故障。
● 其余成员注意到它的缺失,并将go-demo-db-rs2提升到PRIMARY状态。
● 同时,Swarm对故障的服务副本进行了重新调度。
● 主Mongo副本集注意到go-dem-db-rs1服务器重新联机,并作为副节点加入Mongo副本集。
● 新创建的go-demo-db-rs1将它的数据与Mongo副本集的其他成员之一进行同步。
所有这些能工作的关键要素之一是Docker网络。当重新调度的服务副本重新在线时,它可以保持相同的地址go-demo-db-rs1,并且不需要更改Mongo副本集的配置。
如果使用虚拟机,在AWS的情况下,Auto Scaling Group用来托管Mongo,当节点发生故障时,将创建一个新的节点。但是,新节点将收到新的IP,如果不修改配置,将无法加入Mongo副本集。不使用容器,可以在AWS中实现同样的目标,但是没有哪种方法会像Docker Swarm及其网络那样简单和优雅。
创建的样本数据发生了什么变化?请记住,我们将数据写入主Mongo副本go-demo-db-rs1,并随后将其删除。我们没有使用REX-Ray或任何其他方案来持久化数据。
让我们进入新的主Mongo副本:
在你的集群中,新的主服务器可能是go-demo-db-rs3。如果是这样,则请修改上面的命令。
接下来,指定要使用test数据库并检索所有数据:
输出如下:
即使没有配置数据持久性,所有的数据也都还在。
Mongo副本集的主要目的是提供容错功能。如果数据库有故障,那么其他成员将接管。对数据(状态)的任何更改都在副本集的所有成员之间进行复制。
这是否意味着我们不需要将状态保存在外部驱动器上?这取决于场景。如果所操作的数据是巨大的,那么可能会使用某种形式的磁盘持久性来加快同步过程。在任何其他情况下,使用卷都是一种浪费,因为大多数数据库都是为提供数据复制和同步而设计的。
当前的方案运行良好,我们应该寻找一种更自动化(也更简单)的方法来设置它。
现在将退出MongoDB和go-demo-db-util服务副本,删除所有DB服务,然后重新开始:
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。