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.

117 lines
13 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 28 | 微服务容器化运维微博容器运维平台DCP
微服务容器化运维系列的前两期,我给你详细介绍了微服务容器化后如何运维的几个关键问题:镜像仓库、资源调度、容器调度、服务编排,这些问题的产生都是因为微服务部署的节点从一台台物理机或者虚拟机变成了一个个容器,运维模式发生了根本性的变化。此时,容器运维平台也就应运而生。
微博的业务从2013年就开始进行容器化2015年为了应对春晚以及突发热点事件带来的峰值流量开始引入阿里云同时也为了适应业务的发展和运维方式的变化在2015年底开始研发新的容器运维平台DCP。今天我就和你聊聊微博容器运维平台DCP我会讲讲一个真实的容器运维平台是如何建设的在建设过程中面临了哪些问题以及对应的解决方案希望可以让你对容器运维平台的架构有所了解并提供一些经验可供借鉴。
## DCP整体架构
首先我们先来看看DCP的架构设计从下面这张架构图你可以看到DCP的架构主要分为四个部分基础设施层、主机层、调度层、编排层对应的分别解决前面提到的容器运维平台建设的几个关键问题基础设施层用于解决镜像仓库的问题主机层主要解决如何进行资源调度的问题调度层主要解决容器如何在资源上创建的问题编排层主要解决容器如何运作以对外提供服务的问题。下面我们来看各层的详细设计。
![](https://static001.geekbang.org/resource/image/10/64/101e6beb60f0bc482ef6cb0e793d5864.png)
## 基础设施层
DCP中基础设施层主要用于提供各种基础设施以保证其他层功能的正常运行。通常来讲主要包括以下几个基础组件用于存放容器镜像的镜像仓库、提供监控服务的监控中心、实时监控系统容量以便于自动扩缩容的容量评估系统以及容器创建后如何加入线上服务的服务发现组件其中**镜像仓库是DCP最核心的基础组件**。
正如[专栏第26期](http://time.geekbang.org/column/article/42167)我讲的那样DCP以开源镜像仓库Harbor为基础搭建了私有的镜像仓库不过由于微博业务的特征为了应对随时可能到来的突发峰值流量的冲击需要随时随地能够扩容服务池。但在内网冗余度不足的时候也不得不借助公有云来实现因此服务不仅在内网私有云上有部署在阿里云上也有部署这样的话从阿里云申请的主机也需要从镜像仓库中拉取镜像。此时如果镜像仓库只在内网部署的话就需要跨专线去拉取镜像但如果上百台服务器同时拉取镜像带宽占用很可能达到上百G由于专线带宽是有限的显然这样不可取。为此正确的做法就像下图中那样在阿里云机房也部署一套镜像仓库并且通过Harbor的主从复制机制与内网的镜像仓库保持同步。同时为了做到负载均衡每个机房内部都部署了多个Harbor节点内网节点访问内网镜像仓库会通过LVS进行负载均衡阿里云上节点访问阿里云镜像仓库会通过SLB进行负载均衡以满足镜像仓库的带宽需求。
![](https://static001.geekbang.org/resource/image/d3/d2/d3bbd465ed4b053082b011d12be9acd2.png)
## 主机层
DCP中主机层的功能主要是为了完成资源的调度也就是针对不同的集群完成主机的创建、成本的管理以及配置初始化工作也叫Pluto层。前面提到过微博业务不仅在内网私有云上有部署而且在阿里云上也有部署为此Pluto需要适配不同底层提供的创建主机的API进行成本核算并且进行配置初始化操作。Pluto层的架构你可以参看下图我来详细讲一下。
![](https://static001.geekbang.org/resource/image/fc/e7/fc32ede78147d2c19cd51fb4f597b3e7.png)
1.主机创建
Pluto在创建主机时主要有两个来源一个是内部物理机组成的共享池一个是调用阿里云API创建ECS。其中共享池内的资源主要来源于两部分一部分是冗余度高的服务池缩容部分主机加入到共享池一部分是在线业务和离线计算互相补充比如白天在线业务需要的机器多而离线计算的任务主要运行在凌晨这时候就可以在白天把离线计算的集群的部分机器加入到共享池给在线业务使用而在晚上业务低峰期把在线业务的部分机器加入到共享池给离线计算任务使用。而使用阿里云创建ECS主要是在共享池内的资源不足的情况下比如有突发热点事件到来各个服务池都需要紧急扩容这时候共享池内的资源就不足以应对了。而使用阿里云API创建ECS会受到阿里云API的各种限制下面我列举几个微博在使用阿里云创建机器时所遇到的问题你就可以理解主机创建的复杂性所在了。
* 由于阿里云API对单账户的调用有并发限制所以实际业务在创建阿里云ECS上时不能上百台同时创建一般要控制在几十台的规模左右如果这个时候业务需要创建上百台机器该怎么做呢那就需要采取队列机制来控制机器创建的速度。下面这张图就描述了微博在使用阿里云创建ECS时的解决方案在实际创建ECS时不会立即调用阿里云API而是把节点创建任务先放到一个DB队列中然后再通过一个线程定时从DB队列中获取创建任务每次只创建几十台这样的话就不会触发阿里云API对单账号调用的并发限制。
![](https://static001.geekbang.org/resource/image/ec/c0/ec5e29cf71a8b55ed136f18ce786dac0.png)
* 除了有单账户调用的并发限制还会有可用区的库存限制、安全组库存限制以及vSwitch库存限制所以在实际使用阿里云API创建ECS时当机器规模较大如果直接指定使用某个可用区、安全组和vSwitch就可能因为库存原因导致创建失败。微博一开始就使用了这种方案但在突发峰值流量来临时往往要创建几百台甚至上千台的阿里云ECS为此经常会因为以上限制导致创建失败。后来针对可用区、安全组以及vSwitch都做了多可用区、多安全组以及多vSwtich配置在出现库存不够时就自动切换到别的地方来创建极大提高了大规模ECS创建的成功率。
![](https://static001.geekbang.org/resource/image/56/3c/5650102ded876d26c6abde1b97678e3c.png)
2.成本管理
无论是从共享池内创建的机器还是调用阿里云API创建的ECS都是有成本的为此必须对机器的数量以及使用时长进行记录以便进行成本管理。
以阿里云的ECS为例又分为按量付费、按月付费以及按年付费可以按照以下方式来进行管理。
* 按量付费。按照使用时长以秒为单位计费适合突发流量到来临时需要扩容部分机器时使用所以需要记录每台ECS从调用API创建成功到销毁所使用的时长。
* 按月付费。这种比较适合短期业务需要使用机器的场景,比如微博曾经在奥运会期间扩容过大量包月付费的机器,以应对奥运会期间带来的流量上涨。需要注意的是,这种机器到了月底会自动销毁,所以如果还有使用需要的话,需要及时续费。
* 按年付费。这种比较适合需要长期在阿里云上部署的业务,比如有一些新的业务因为业务发展比较快,采用传统自采机器部署的话,由于采购周期比较长不适合业务发展,所以使用公有云更为合适。
3.配置初始化
主机创建完成后还要进行一些基础软件的安装以及配置修改等工作这就是配置初始化的过程。以阿里云创建的ECS为例如果短时间内创建了上千台ECS这个时候配置初始化的工作量会非常大需要同时给上千台ECS下发配置文件并安装基础软件同时还需要记录每台ECS的初始化状态到DB以便查询是否初始化成功。下图描述了初始化的过程DCP在进行主机配置初始化时会通过Ansible向所有主机下发配置文件和基础软件并通过自定义callback queue把每台主机的初始化状态异步写入到DB中避免上百台机器同时并发写入DB造成死锁。
![](https://static001.geekbang.org/resource/image/98/c3/98dd72c57190af3502e037e32fc4b8c3.png)
## 调度层
DCP中调度层的主要功能是在可用的主机上创建容器。由于微博业务早在2013年就开始进行容器化基于当时的背景考虑就选择了Swarm作为容器调度的工具并根据自己的业务特点在Swarm基础上进行二次封装定制了自己的调度层Roam使其具备支持跨IDC、高可用以及可扩展的特性。下面是Roam的架构其主要工作原理是
* Swarm Manager和Swarm Client节点都向Consul中注册并且有一个Active Manager和Standby Manager。任何一个IDC内的Active Manager如果down掉的话Standby Manager就会注册到Consul中成为新的Active Manager以保证高可用性。
* 当发起容器调度时Roam根据IDC参数请求Consul得到该IDC的Swarm Manager信息。
* Roam访问该IDC内的Swarm ManagerSwarm Manager再访问Consul获取Swarm Client信息并根据Roam传递的调度策略从Swarm Client中选择节点创建容器。
![](https://static001.geekbang.org/resource/image/68/bb/68d80a9dac56519d38730c7359e93bbb.png)
## 编排层
DCP中编排层的主要作用是对服务进行整合以对外提供服务主要包括服务依赖、服务发现以及自动扩缩容下面我来详细介绍每一部分的具体实现。
1.服务依赖
DCP通过模板来管理容器的创建一个服务如果需要进行扩容、创建容器就必须按照模板里定义的参数来执行以下图描述的DCP里的一个扩容任务创建模板为例通常来讲模板里定义的参数主要包括几个部分任务的名称、机器的配置、任务依赖、任务详细配置包括调用阿里云API创建ECS时的可用区、安全组参数等其中任务依赖的配置项是
```
{"Sid":1707061842070000,"Ratio":0.2,"ElasticCount":0}
{"Sid":1703271821000000,"Ratio":0.3,"ElasticCount":0}
```
它的含义是执行这个扩容任务时会自动执行ID为1707061842070000和1703271821000000的扩容任务并且按照每扩容10台容器分别扩容2台和3台依赖容器的比例来执行。
![](https://static001.geekbang.org/resource/image/20/7e/20e3c0c7d1eca4738979a675a866d87e.png)
2.服务发现
微博的业务场景主要包含两种服务一种是HTTP服务一种是Motan RPC服务他们分别使用了不同的服务发现方式。
* HTTP服务。考虑到传统的基于Nginx的配置Reload机制实现的服务发现方式在高并发访问的情况下会导致吞吐量下降10%左右如果业务频繁变更的话就会受到影响。为此DCP在实际业务中基于Nginx和Consul研发了一种可行的解决方案[nginx-upsync-module](https://github.com/weibocom/nginx-upsync-module),并且已经开源。
* Motan RPC服务。Motan RPC服务在启动时会向注册中心Config Service注册服务并且注册中心支持多IDC部署。像下图所描述的那样正常情况下服务消费者会访问同一个IDC内的服务提供者并且支持在故障的时候可以切换到其他IDC。
![](https://static001.geekbang.org/resource/image/95/3b/9519bcc735da020dd24b64ba74a41a3b.png)
3.自动扩缩容
DCP系统实现自动扩缩容主要依靠的是容量决策支持系统由容量决策支持系统来实时监控系统的容量。如下图所示一旦容量决策支持系统检测到某个服务需要进行扩容就会创建扩容任务Config Watcher会监控到扩容任务并通知CronTrigger有调度策略变更。CronTrigger接到扩容任务就会调用Scheduler来具体执行扩容。同时还可以通过API来修改、查询扩缩容的信息也可以通过UI来操作。
![](https://static001.geekbang.org/resource/image/7d/54/7d20839ac42bc38fc887875a1397b054.png)
## 总结
今天我给你讲解了微博容器运维平台DCP的架构主要包括基础设施层、主机层、调度层以及编排层并详细介绍了每一层的功能实现以及各自承担的不同职能。下面这张图是一次完整扩容流程包括了资源评估、配额评估、初始化、容器调度、部署服务、服务依赖、服务发现以及自动扩缩容等DCP正是通过把这些过程串联起来实现容器运维的。
![](https://static001.geekbang.org/resource/image/54/ed/5499780f1f12d9b3940988377dae80ed.png)
## 思考题
在讲到服务编排时我提到服务之间会存在依赖关系比如服务A依赖服务B假如此时服务A的流量上涨需要对服务A进行扩容这时候有两种方案一种方案是通过自动扩缩容服务A和服务B的扩容完全独立分别按需自动扩缩容一种方案是通过服务依赖扩容服务A之前先扩容服务B你认为这两种方案哪种更好为什么
欢迎你在留言区写下自己的思考,与我一起讨论。