在Swarm集群内缩放服务很容易,不是吗?只需执行docker service scale <SERVICE_NAME>=<NUMBER_OF_INSTANCES>,该服务马上就会运行多个副本。
之前的陈述只是部分正确,更准确的措辞是“在Swarm集群内缩放无状态服务很容易”。
缩放无状态服务很容易的原因在于不需要考虑状态,一个实例不管运行多长时间都是一样的。一个新创建的实例与一个运行了一周的实例没什么区别。由于状态不会随着时间的改变而改变,我们可以在任何时候创建新的副本,并且它们都完全相同。
但是,这个世界并不是无状态的。状态是我们行业中不可避免的一部分。一旦有信息被创建出来,它就需要存储在某个地方。我们存储数据的地方必须是有状态的,它有一个随时间变化的状态。如果想缩放这样一个有状态的服务,至少有以下两件事情需要考虑。
(1)如何将一个实例的状态变化传播给其他实例?
(2)如何创建一个有状态服务的副本(一个新实例),并确保状态也被复制?
通常将无状态和有状态的服务组合成一个逻辑实体。后端服务可能是无状态的,并依赖数据库服务作为外部数据存储。这样,每个服务都可以有不同的生命周期,其关注点也有清晰的界限。
首先,我必须说明,没有什么银弹可以让有状态服务可缩放和容错。在整本书中,我给出的例子也许适用于你的实际情况,也可能不适用。数据库是有状态服务的一个显而易见且非常典型的例子。虽然有一些常见的模式,但几乎每种数据库都提供了不同的数据复制机制。这本身就足以说明我们无法得到一个适用于所有情况的明确答案。我们将在本书后面探讨一下MongoDB的可伸缩性,也会看到一个Jenkins的例子,它使用文件系统来处理自身状态。(www.xing528.com)
我们要处理的第一种情况属于其他类型,我们会讨论将状态存储在配置文件中的服务的可伸缩性。为了让情况更复杂些,这里的配置是动态的,在服务的整个生命周期中它会随着时间的改变而改变。我们也会探讨缩放HAProxy的方法。
如果使用官方的HAProxy(https://hub.docker.com/_/haproxy/)镜像,则面临的挑战之一是决定如何更新所有实例的状态,得更改配置并重新加载代理的每个副本。
比如可以在集群中的每个节点上安装一个NFS卷,并确保在所有HAProxy容器内挂载相同的主机卷。乍看起来,这样可以解决状态问题,因为所有实例都会共享相同的配置文件,主机上配置的任何改变对所有实例都可见,然而,这本身并不会改变服务的状态。
HAProxy在初始化过程中加载配置文件,并且忽略之后我们对配置所做的任何更改。为了将文件状态的改变反映到服务状态上,我们需要重新加载它。问题在于,实例可能运行在集群内的任何节点上。最重要的是,如果采用动态缩放(稍后会详细介绍),那么我们甚至可能都不知道有多少实例正在运行。所以需要发现有多少个实例,找出它们正在哪个节点上运行,获取每个容器的ID,然后才能发送信号以重新加载代理。虽然所有这些都可以通过脚本执行,但这远非最佳解决方案。而且,挂载的NFS卷是故障单点,如果拥有该卷的服务器发生故障,则数据将会丢失。当然,我们可以创建备份,但它们只能提供恢复部分丢失数据的方法。也就是说,我们可以恢复备份,但最后一次备份被创建和节点发生故障之间生成的数据还是会丢失。
另一种方法是将配置嵌入HAProxy镜像中。可以创建一个基于haproxy的新Dockerfile,并加上添加配置的COPY指令。这意味着每次要重新配置代理时,都需要更改配置,构建一组新的镜像(新版本),并更新当前在集群内运行的代理服务。可以想象这也不实际,为了重新配置代理这样简单的事情,这么做实在太折腾。
Docker Flow Proxy使用一种不太常规的方法来解决这个问题。它在将其状态的一个副本存储到Consul里。它还使用了未公开的Swarm networking功能(至少在编写本书时还未公开)。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。