庖丁解牛(二)丨360集群控制系统概述

如何在5s之内控制5w台服务器执行命令

之前在360工作,主导设计了360的服务器集群控制系统,希望能给大家一点借鉴。

什么是控制系统

  • 快速,安全的进行服务器任务分配最终达到的性能指标是5s对30000台服务器进行任务分发&执行&结果获取

1

 

  • 用要求的权限进行执行,精确控制任务
  • 严格的权限树限制插件审核机制用户只能操作自己有权限的树节点
  • agent控制信道加密(对称加密算法 with salt)
  • 能在任务执行的任何时间进行 暂停、继续、停止仅限于子任务(机器)粒度
  • HTTP回调接口如果在创建任务时提供一个HTTP URL,子任务(机器粒度)的任何状态变化都会通过这个回调进行通知
  • 对任务的输出,返回值进行收集,汇总,入库

控制系统架构

由于我们的控制系统属于基础服务,一般来说我们对公司的一般业务的可用性要求是要达到99.99%。作为基础架构类的服务必须比普通业务的可用性要高1~2个数量级,也就是:99.999%~99.9999%。

所以,一个最基本的要求就是,整套系统必须没有任何单点。

我们整套系统的架构图如下:

2.webp

我们为了达到这一点,我们做了如下设计:

      1. 我们的控制系统本身没有任何状态,任何状态都是保存在数据库中。
      2. 我们单独设计了一个hermes-sitter模块, 负责健康检查以及在健康检查失败的时候进行必要的触发错误逻辑。

整套系统在各种情况下的故障恢复逻辑如下图所示:

3.webp

整套系统的开发是在我们详细分析并绘制了上述图表的情况下给出的, 所以整体的Coding时间也只用了一周,在加上我们用gtest做的较为详尽的单元测试 由于各种错误检查的逻辑在里面,最后的单测行覆盖率也就在70%左右, 系统上线后3年,仅修复了一处关于暂停点的bug。

 

控制系统的实现细节

第三方库的应用

我们在构建控制系统的时候主要应用的第三方库有:

    • libev
      • 著名网络事件库libevent的弟弟,更轻,更快
    • c-ares
      • 异步DNS解析库,对DNS over TCP的支持,提高响应速度
      • 由于只对select模型提供了支持,我们给它加了个patch来配合libev使用
    • gtest
      • google的单测框架
      • 单测是一种很好的在软件构建初期就可以发现潜在的bug的方法

网络模型

      • 我们的网络模型是参考memcached的线程模型,对等多线程模型
        • 对client模型和server模型都给予很好的支持
        • “对等”不会由于线程分工造成性能瓶颈,减少内存拷贝

 代码地址:https://github.com/auxten/gko_pool

内存分配优化

在项目优化的后期,我们发现内存分配是我们系统的一个性能瓶颈, 为此,我们特意构建了一个比较专用的内存池。

主要针对我们在创建连接之前所要开辟的国定大小的内存进行加速。

主要目的有如下两点:

    • 减少为每个连接分配初始的r/w buf的开销
    • 实现了一个简单的内存池,只能分配4K块大小的内存块,bucket大小可以配置,stupid but works

代码地址: https://github.com/auxten/gkoAlloc

瓶颈发现与解决

经过我们利用gpreftools的寻找性能瓶颈,我们发现DNS成为我们系统最大的瓶颈。

我们尝试过用多线程DNS解析,但收效甚微,就像这样:

 

4.webp

然后,我们又很自然的想到,可以使用DNS Cache来解决。

但是,遇到不能解析正确的域名,我们还是需要走DNS查询,还是弱爆了, 就像这样:

 

5.webp

我们需要一个突破性的解决方案,像这样的:

5.webp (1)

首先,需要解释一个问题,我们的控制系统由于一些特殊的网络ACL限制原因设计成了:

由我们的中心控制端主动发起连接去连接我们的agent

这点,也让我们的控制系统成为了一个在普通服务端工程中比较少见的高性能”客户端”编程模型。

一般来说,”服务端”编程模型的特征是:监听在一个端口上等待连接的到来

    • 服务端编程模型的流程伪代码是这样的:
    • 短连接:

    • 长连接:

而”客户端”编程模型的特征:调用connect(2)来主动发起连接:

短连接:

长连接:

我们的控制系统就属于”客户端高并发短连接”编程的一个很好的实例。

客户端网络编程里大家容易忽略,也是glibc给大家无意间误导的就是:DNS查询也是一个 请求&响应的网络操作。

通常情况下,DNS都是基于UDP,请求和响应都是各自一个UDP数据包就可以完成的。 其速度也是非常快的。但由于DNS查询是一个递归的过程,如果一个DNS域名查询的请求在第一次请求的DNS服务器中没有被找到,DNS服务器就会询问上层DNS……依次递归。

这样就会导致当查询的域名为不存在的域名的时候就格外的费时,在异步非阻塞的main loop 中这种费时是致命性的。

所以,我们的解决方案是:

包括DNS在内,全异步 libev + 状态机 + c-ares

经过我们的异步DNS改造,这个问题被完美的解决。

相关网络框架代码参见:https://github.com/auxten/gko_pool

易用性改造

为了安全性考虑,我们没有直接的把bash命令调用的功能直接开放给用户。

因为,我们的控制系统是可以在5s之内给6万台主机下发命令的, 所以,如果发生误操作那影响也是相当大的。

我们只允许用户调用审核过的插件进行任务执行。

为了让我们的系统更容易的接入,我们为我们的系统包装了一层RESTful 的API:

 

通过这种方式,我们就允许用户用任何的语言进行开发。

 

运维开发交流QQ群: 238757010

欢迎大家关注公共号:Reboot

7008c662deb255e4403640e068f6fd02_r

 

 

 

文章分类 后端, 未分类, 运维, 运维开发

发表评论

电子邮件地址不会被公开。

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

在线交流

数百位业内高手和同行在等你交流
Reboot运维开发分享