You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

10 KiB

27 | 微服务容器化运维:容器调度和服务编排

专栏上一期我给你讲解了容器运维平台的两个关键组成镜像仓库和资源调度。复习一下镜像仓库解决的是Docker镜像存储和访问的问题资源调度决定了Docker镜像可以分发到哪些机器上的问题。这两个问题解决后你就该考虑如何在集群中创建容器也就是容器如何调度的问题容器创建后如何运作才能对外提供服务也就是服务如何编排的问题。下面我们就一起看看容器调度和服务编排都是如何解决的。

容器调度

容器调度的问题,说的是现在集群里有一批可用的物理机或者虚拟机,当服务需要发布的时候,该选择哪些机器部署容器的问题。

比如集群里只有10台机器并且已经有5台机器运行着其他容器剩余5台机器空闲着如果此时有一个服务要发布但只需要3台机器就行了这个时候可以靠运维人为的从5台空闲的机器中选取3台机器然后把服务的Docker镜像下载下来再启动Docker容器服务就算完成发布。但如果集群机器的规模扩大到几十台或者上百台时要发布的服务也有几十个或者上百个的时候由于每个服务对容器的要求以及每台机器上正在运行的容器情况变得很复杂就不太可能靠人肉运维了。

这时就需要有专门的容器调度系统了为此也诞生了不少基于Docker的容器调度系统比如Docker原生的调度系统Swarm、Mesosphere出品的Mesos以及Google开源的大名鼎鼎的Kubernetes。下面我就结合微博的实践经验,给你讲讲容器调度要解决哪些问题。

1.主机过滤

主机过滤是为了解决容器创建时什么样的机器可以使用的问题,主要包含两种过滤。

  • 存活过滤。也就是说必须选择存活的节点,因为主机也有可能下线或者是故障状态。

  • 硬件过滤。打个比方现在你面对的集群有Web集群、RPC集群、缓存集群以及大数据集群等不同的集群硬件配置差异很大比如Web集群往往用作计算节点它的CPU一般配置比较高而大数据集群往往用作数据存储它的磁盘一般配置比较高。这样的话如果要创建计算任务的容器显然就需要选择Web集群而不是大数据集群。

上面这两种过滤方式都是针对主机层次的过滤方式除此之外Swarm还提供了容器层次的过滤可以实现只有运行了某个容器的主机才会被加入候选集等功能。

2.调度策略

调度策略主要是为了解决容器创建时选择哪些主机最合适的问题一般都是通过给主机打分来实现的。比如Swarm就包含了两种类似的策略spread和binpack它们都会根据每台主机的可用CPU、内存以及正在运行的容器的数量来给每台主机打分。spread策略会选择一个资源使用最少的节点以使容器尽可能的分布在不同的主机上运行。它的好处是可以使每台主机的负载都比较平均而且如果有一台主机有故障受影响的容器也最少。而binpack策略恰恰相反它会选择一个资源使用最多的节点好让容器尽可能的运行在少数机器上节省资源的同时也避免了主机使用资源的碎片化。

具体选择哪种调度策略,还是要看实际的业务场景,通常的场景有:

  • 各主机的配置基本相同,并且使用也比较简单,一台主机上只创建一个容器。这样的话,每次创建容器的时候,直接从还没有创建过容器的主机当中随机选择一台就可以了。

  • 在某些在线、离线业务混布的场景下为了达到主机资源使用率最高的目标需要综合考量容器中跑的任务的特点比如在线业务主要使用CPU资源而离线业务主要使用磁盘和I/O资源这两种业务的容器大部分情况下适合混跑在一起。

  • 还有一种业务场景主机上的资源都是充足的每个容器只要划定了所用的资源限制理论上跑在一起是没有问题的但是某些时候会出现对每个资源的抢占比如都是CPU密集型或者I/O密集型的业务就不适合容器混布在一台主机上。

所以实际的业务场景对调度策略的要求比较灵活如果Swarm提供的spread和binpack满足不了的话可能就需要考虑自行研发容器调度器了。

服务编排

1.服务依赖

大部分情况下微服务之间是相互独立的在进行容器调度的时候不需要考虑彼此。但有时候也会存在一些场景比如服务A调度的前提必须是先有服务B这样的话就要求在进行容器调度的时候还需要考虑服务之间的依赖关系。

为此Docker官方提供了Docker Compose的解决方案。它允许用户通过一个单独的docker-compose.yaml文件来定义一组相互关联的容器组成一个项目从而以项目的形式来管理应用。比如要实现一个Web项目不仅要创建Web容器比如Tomcat容器还需要创建数据库容器比如MySQL容器、负载均衡容器比如Nginx容器等这个时候就可以通过docker-compose.yaml来配置这个Web项目里包含的三个容器的创建。

Docker Compose这种通过yaml文件来进行服务编排的方式是比较普遍的算法以微博的业务为例也是通过类似yaml文件的方式定义了服务扩容的模板模板除了定义了服务创建容器时的镜像配置、服务池配置以及主机资源配置以外还定义了关联依赖服务的配置。比如微博的Feed服务依赖了user服务和card服务假如user服务扩容的模板ID为1703271839530000card服务扩容的模板ID为1707061802000000那么Feed服务的扩容模板里就会像下面这样配置它代表了每扩容10台Feed服务的容器就需要扩容4台user服务的容器以及3台card服务的容器。

{"Sid":1703271839530000,"Ratio":0.4}
{"Sid":1707061802000000,"Ratio":0.3}

2.服务发现

容器调度完成以后,容器就可以启动了,但此时容器还不能对外提供服务,服务消费者并不知道这个新的节点,所以必须具备服务发现机制,使得新的容器节点能够加入到线上服务中去。

根据我的经验比较常用的服务发现机制包括两种一种是基于Nginx的服务发现一种是基于注册中心的服务发现。

  • 基于Nginx的服务发现

这种主要是针对提供HTTP服务的当有新的容器节点时修改Nginx的节点列表配置然后利用Nginx的reload机制会重新读取配置从而把新的节点加载进来。比如基于Consul-Template和Consul把Consul作为DB存储容器的节点列表Consul-Template部署在Nginx上Consul-Template定期去请求Consul如果Consul中存储的节点列表发生变化就会更新Nginx的本地配置文件然后Nginx就会重新加载配置。

  • 基于注册中心的服务发现

这种主要是针对提供RPC服务的当有新的容器节点时需要调用注册中心提供的服务注册接口。注册中心的服务发现机制在专栏第5期我有过详细讲解你可以再回顾一下它的原理。在使用这种方式时如果服务部署在多个IDC就要求容器节点分IDC进行注册以便实现同IDC内就近访问。以微博的业务为例微博服务除了部署在内部的两个IDC还在阿里云上也有部署这样的话内部机房上创建的容器节点就应该加入到内部IDC分组而云上的节点应该加入到阿里云的IDC。

3.自动扩缩容

容器完成调度后,仅仅做到有容器不可用时故障自愈还不够,有时候还需要根据实际服务的运行状况,做到自动扩缩容。

一个很常见的场景就是大部分互联网业务的访问呈现出访问时间的规律性。以微博业务为例白天和晚上的使用人数要远远大于凌晨的使用人数而白天和晚上的使用人数也不是平均分布的午高峰12点半和晚高峰10点半是使用人数最多的时刻。这个时候就需要根据实际使用需求在午高峰和晚高峰的时刻增加容器的数量确保服务的稳定性在凌晨以后减少容器的数量减少服务使用的资源成本。

常见的自动扩缩容的做法是根据容器的CPU负载情况来设置一个扩缩容的容器数量或者比例比如可以设定容器的CPU使用率不超过50%,一旦超过这个使用率就扩容一倍的机器。

总结

今天我给你讲解了容器运维平台的另外两个关键组成:容器调度和服务编排,并给出了常用的解决方案。你的业务团队在选择解决方案时,要根据自己的需要选择合适的方案,而不是理论上最好的。

比如Kubernetes解决方案在容器调度、服务编排方面都有成熟的组件并且经过大业务量的实际验证。但是要考虑到Kubernetes本身的复杂性以及概念理解的门槛对于大部分中小业务团队来说在生产环境上使用Kubernetes都会显得大材小用并且还需要部署并运维Kubernetes周边的一些基础设施比如etcd等。

相比之下Docker原生自带的解决方案Swarm和Compose就要简单得多但是功能也比较有限如果不能满足你的业务需求的话也不好再二次开发。

在了解了镜像仓库、资源调度、容器调度、服务编排后你会发现,微服务容器化后最大的挑战其实来自于原有运维设施如何支持容器的运维,是在原有运维平台上升级还是完全采用新的容器运维平台,这才是关键,往往不能一蹴而就,需要逐步按照业务进行替换升级。但是考虑到微服务容器化后所带来的种种好处,采用新的运维模式势在必行。

思考题

容器调度方面业界最有名的莫过于Swarm、Mesos和Kubernetes了你认为它们的优缺点是什么分别适合什么业务场景

欢迎你在留言区写下自己的思考,与我一起讨论。