TL; DR :描述我们内部网络配置管理系统QControl的客户端-服务器体系结构。 它基于两级传输协议,该协议可处理gzip压缩的邮件,而无需在端点之间解压缩。 分布式路由器和端点接收配置更新,并且协议本身允许安装本地化的中间中继。 该系统基于
差异备份的原理(“近期稳定”,如下所述)构建,并使用JMESpath查询语言以及Jinja模板引擎来呈现配置文件。
Qrator Labs管理着一个全球分布式的攻击缓解网络。 我们的网络按照任播原则工作,子网通过BGP宣布。 作为物理上位于地球几个区域的BGP任意播网络,我们可以处理和过滤更接近Internet核心-Tier-1运营商的非法流量。
另一方面,成为地理上分散的网络并不容易。 网络存在点之间的通信对于安全服务提供商来说至关重要,以便对所有网络节点进行一致的配置并及时更新它们。 因此,为了向消费者提供最高水平的基本服务,我们需要找到一种在各大洲之间可靠地同步配置数据的方法。
起初是道。 它很快成为需要更新的通信协议。
QControl存在的基石以及与此同时花费大量时间和资源来构建这样的协议的主要原因是需要获得单一的权威配置源,并最终使我们的存在点与其同步。 存储库本身只是QControl开发过程中的几个要求之一。 此外,我们还需要在存在点(TP),数据验证的智能(和自定义)方法以及访问控制方面与现有和计划的服务进行集成。 除此之外,我们还希望使用命令来管理这样的系统,而不是对文件进行修改。 在QControl之前,数据几乎以手动模式发送到存在点。 如果其中一个接入点不可用,而我们又忘记了对其进行更新,则表明配置不同步-您必须花一些时间将其恢复服务。
结果,我们提出了以下方案:

配置服务器负责数据验证和存储,路由器具有多个端点,这些端点从客户端和支持团队向服务器以及从服务器到存在点接收和广播配置更新。
在世界各地,Internet连接的质量仍然存在很大差异-为了说明这一论点,让我们看一下从布拉格,捷克共和国到新加坡和香港的简单地铁。

从布拉格到新加坡的地铁

与香港相同
高延迟意味着较低的速度。 另外,还有丢包。 通道的宽度不能弥补这一问题,在构建分散式系统时应始终将其考虑在内。
完整的存在点配置是需要通过不可信连接发送给许多收件人的大量数据。 幸运的是,尽管配置不断变化,但这种变化很少发生。
近期稳定的设计
可以说,基于增量更新的原理构建分布式网络是一个相当明显的解决方案。 但是差异存在很多问题。 我们需要保留控制点之间的所有差异,并能够在有人错过某些数据的情况下发送它们。 每个目的地都必须按照严格定义的顺序应用它们。 通常,在多个目的地的情况下,这种操作可能需要很长时间。 接收者还应该能够请求丢失的部分,并且当然,中央部分应该正确地响应此类请求,仅发送丢失的数据。
结果,我们得出了一个相当有趣的解决方案-我们只有一个固定的支持层,我们称其为稳定的,并且最近才有了一个区别。 每个最近的数据都基于最后形成的稳定数据,足以重建配置数据。 只要有最新的邮件到达目的地,就不再需要旧的邮件。
例如,由于最近变得太大,因此仅不时发送新的稳定配置。 在这里同样重要的是,我们以广播/多播模式发送所有这些更新,而不必担心单个接收者及其收集在一起的数据的能力。 一旦我们确信每个人都有正确的马stable,我们只会发送新的马stable。 澄清这是否值得吗? 可以用 稳定文件缓存在配置服务器和收件人上,根据需要创建最新文件。
二级运输架构
我们为什么要在两个层次上构建运输工具? 答案很简单-我们希望将路由与高级逻辑分开,以从OSI模型及其传输层和应用程序层中汲取灵感。 对于传输协议的作用,我们采用了Thrift,对于高级控制消息格式,我们采用了msgpack序列化格式。 这就是为什么路由器(正在执行多播/广播/中继)不会在msgpack内查找,不会解压并且不会将内容打包回去,而仅执行数据传输的原因。
Thrift(来自英语-“ thrift”,发音为[θrift])是一种界面描述语言,用于为不同的编程语言定义和创建服务。 它是用于远程过程调用(RPC)的框架。 它将软件管道与代码生成引擎结合在一起,用于开发在某种程度上可以在语言之间高效且轻松地工作的服务。由于RPC和对许多语言的支持,我们选择了Thrift框架。 像往常一样,客户端和服务器是容易的部分。 但是,事实证明,路由器是一个棘手的问题,部分原因是在我们的开发过程中缺少现成的解决方案。

还有其他选择,例如protobuf / gRPC,但是,当我们开始我们的项目时,gRPC尚很年轻,我们不敢将其纳入研究范围。
当然,我们可以(事实上,这是值得做的)来创建我们自己的自行车。 为我们所需的协议创建协议会更容易,因为与在Thrift上构建路由器相比,客户端-服务器体系结构的实现相对简单。 一种或另一种方式是,对流行的库的自写协议和实现有一种传统的偏见态度(徒然),此外,讨论总是提出一个问题:“我们如何将其移植到其他语言?” 因此,我们立即提出了有关自行车的想法。
Msgpack是JSON的类似物,但速度更快且更少。 这是一种二进制数据序列化格式,允许在多种语言之间交换数据。在第一层,我们为Thrift提供了路由器转发消息所需的最少信息。 第二层是打包的msgpack结构。
我们选择msgpack是因为它比JSON更快,更紧凑。 但更重要的是,它支持自定义数据类型,使我们能够使用一些很酷的功能,例如传输原始二进制文件或表明缺少数据的特殊对象,这对于我们最近稳定的方案非常重要。
JmespathJMESPath是JSON请求语言。这正是我们从官方JMESPath文档获得的描述的样子,但实际上,它提供了更多内容。 JMESPath允许您搜索和过滤任意树结构中的子树,以及动态地对数据进行更改。 它还允许您添加特殊的过滤器和数据转换过程。 虽然,当然,这需要大脑紧张才能理解。
金佳对于某些使用者,我们需要将配置转换为文件-因此我们使用模板引擎,Jinja是显而易见的选择。 在它的帮助下,我们从模板和在目的地接收的数据生成配置文件。
要生成配置文件,我们需要一个JMESPath请求,一个文件在FS中的模板,一个配置本身的模板。 同样在此阶段,澄清文件权限也很不错。 事实证明,所有这些都成功地组合到一个文件中-在配置模板开始之前,我们将标头以YAML格式放置,以描述其余内容。
例如:
---
selector: "[@][?@.fft._meta.version == `42`] | items([0].fft_config || `{}`)"
destination_filename: "fft/{{ match[0] }}.json"
file_mode: 0644
reload_daemons: [fft]
...
{{ dict(match[1]) | json(indent=2, sort_keys=True) }}
为了制作新服务的配置文件,我们仅添加一个新模板文件。 在现场不需要更改源代码或软件。
自QControl投入运营以来,发生了什么变化? 第一个也是最重要的一点是在网络中所有节点之间一致且可靠地交付配置更新。 第二个是获得功能强大的配置验证工具,并由我们的支持团队以及服务的使用者对其进行更改。
我们设法使用最近稳定的更新方案来完成所有这些操作,以简化配置服务器与配置收件人之间的通信。 使用两层协议来支持与内容无关的数据路由方法。 已成功将基于Jinja的配置生成引擎集成到分布式过滤网络中。 该系统支持我们的分布式和杂色外围设备的多种配置方法。
感谢感谢
NoN的VolanDamrod撰写的材料。
帖子的
英文版 。