微服务 斗鱼配置管理中心 Minerva 的设计与实现

askuy for 斗鱼 · 2020年02月18日 · 最后由 askuy 回复于 2020年03月01日 · 1580 次阅读
本帖已被设为精华帖!

作者:斗鱼杜旻翔

前言

伴随微服务的推广,程序粒度的日趋小型化,配置文件的数量成几何级数增长,采用传统的本地文件方式管理应用,凸显的问题越来越多,很难适应于快速发展的微服务架构和容器虚拟化技术。我们需要对配置进行治理,能够对配置进行下发管理、版本管理、安全管理、权限管理等,因此斗鱼的 Minerva 配置中心应运而生。

Minerva 配置中心是斗鱼研发的配置管理中心,集中化管理在不同机房、不同环境的应用配置,配置采用 Key-Value 的数据结构,存储使用 MySQL 和 ETCD,支持配置快速搜索,支持配置修改实时生效、 支持 K8S 下使用配置中心、支持配置关系可视化等功能,适用于微服务的配置管理。

为什么需要配置中心

应用数量 配置数量 配置体积 变更频率
单体应用时代 较少 较少 内容少
微服务时代 增多,机器节点增多 与应用数量呈正相关增加 增加大量依赖关系配置 变更频率远大于版本发布频率

在单体应用时代,应用特点是:集成化、中心化、总量少;服务间依赖关系简单,较少考虑上下游依赖配置,迭代速度慢,配置变更频率低,只需要维护一个集成类型的配置文件,部署在数量可控的高性能机器上即可,遇到紧急情况可以让运维同事之间上机器操作便可以快速解决问题。

到微服务时代,应用开始进行功能化拆分,总量呈几何上升;采用低配置机器,注重弹性扩容,部署节点显著增多;更关注上下游服务相关配置,且配置文件的修改频率因为精细化操作、依赖管理等远高于代码本身的修改频率。

所有的这些麻烦是由于我们对配置和代码在管理和发布过程中不加区分造成的。配置本身源于代码,是为了提高代码的灵活性而提取出来的一些经常变化的或需要定制的内容,正是配置的这种天生的变化特征给我们带了很大的麻烦。

因此,在借鉴了业内其他分布式配置中心的设计和实现后,我们设计了下面的分布式配置管理系统,致力于将配置内容从代码中完全分离出来,及时可靠高效地提供配置访问和更新服务。

主流产品差异性

优点 缺点
QConf 方案 设计简单清晰 重度依赖 ZK 集群 横向扩展困难
淘宝 Diamond 方案 简单 可靠 易用 过于依赖 MYSQL 数据库 客户端性能存在瓶颈 高可用性受限
携程 Apollo 方案 基于 JAVA+Eureka 集群 多层负载,多级缓存保证高可用 权限管理,审计操作完善 设计复杂,部署维护成本高

综合各种方案调研,考虑到现有的技术积累,在斗鱼实际业务场景中没有大规模使用 ZK 集群,实际服务部署架构上存在大量多机房部署的现象,我们希望配置中心:设计简单清晰;可靠易用;支持多机房高可用。携程的 Apollo 是不错的实现方案,功能完善设计完整。但是对斗鱼而言略显臃肿、维护成本高、对斗鱼的多机房架构和 k8s 结构支持不友好,所以我们必须要有一套更加适合斗鱼的配置中心。

Minerva 配置中心简介

Minerva 配置中心理念

Minerva 配置中心有三个核心理念:配置完整性定义;配置下发回显功能;配置缓存优先级设计。

配置完整性定义

配置完整性定义是:环境变量、编译注入配置、命令行配置、文本文件、远端配置,这五类数据共同构成了一份完整配置,形成一份完整配置。配置中心会对以下五类配置进行管理,涉及应用全生命周期的所有配置数据。

环境变量 运维初始化机器时或服务初次部署时定义的配置;
编译注入配置 应用在源码编译过程中注入的配置;
命令行配置 服务启动时写入的配置;
文本文件 以文本方式更随服务或下发到服务的配置;
远端配置 服务通过 API 获取到的远端配置;

配置下发回显

配置下发后会经过以下流程,保证配置下发成功。这是配置中心的核心所在,保证配置在跨机房下发过程中数据正常,保证服务能适应新版本的配置。

  1. 下发配置,等待配置下发状态回传;
  2. 等待配置生效状态监测;
  3. 双重确认完成配置发布。

配置缓存优先级

应用程序向 Minerva Agent 请求配置,优先返回 Minerva Agent 内存缓存配置;其次是文件系统本地缓存;最后请求管理中心存储的持久化配置数据。如果整个流程走完没有获取到配置,则会在配置回显流程中产生错误提示,重新进入配置发布流程。保证如果有配置,应用一定可以获取到。

Minerva 配置中心特点

跨机房 集中管理不同机房、不同环境的配置;提供了一个统一的管理界面,处理不同环境(env),不同机房(idc)的配置;
实时生效 配置修改实时生效;在秒级范围内可接收到最新的配置;
版本管理/灰度发布支持 版本发布管理且支持灰度发布;每次发布配置会产生一个版本,支持各个版本的回滚操作;
配置监控 客户端配置信息监控;管理后台直接展示配置在哪些实例上被哪些服务使用
权限管理,发布审核 1. 对线上环境与线下环境进行用户的权限角色区分,用户经历三个流程完成操作:编辑、保存、发布,流程的拆分一定程度上避免了人为操作失误 2. 每次操作都有日志记录,并且有直观的配置差异比对,类似 git diff
全局资源管理 1. 将配置片段设置为特定的变量,管理后台直接选择该配置即可使用; 2. 对所有资源进行默认加密,prod 环境配置管理员设置对应 CURD 权限;其他环境进行权限开放; 3. 支持全局替换。
提供开放平台 API 提供丰富的 API,并包含完善的权限机制
高可用 支持高可用,横向节点扩展便捷
静态配置与动态配置区分 1. 静态配置经过配置发布后,重启服务生效; 2. 动态配置,可进行应用无感热更新操作

Minerva 配置中心设计

关键概念

应用(APP)

客户端在运行过程中,Minerva Agent 根据已有的应用属性从 Minerva 配置中心获取该应用对应的配置。应用的表示采用的是全局唯一应用名称。

环境(ENV)

配置对应不同的环境,Minerva Agent 需要知道当前应用所属环境,根据环境获取对应的配置,获取环境的途径是根据环境变量而来。

机房(IDC)

跨机房配置集中化管理,例如我们的管理后台部署在机房 A,应用部署在机房 B 和机房 C,管理后台集中化管理各个机房的配置数据。

总体设计

层次设计

下图描述 Minerva 配置中心的总体设计,从下往上分析:

  • 配置管理中心提供了配置的修改发布功能,配置数据存入 MySQL 进行持久化;
  • 配置管理中心通过 Minerva Proxy 代理服务将配置发送到对应机房的 ETCD 中;
  • 服务器或宿主机上部署的 Minerva Agent 通过 Watch ETCD 获取到配置数据的变化,进一步调整硬盘以及内存中的配置数据;
  • 客户端可直接读取 Minerva Agent 更新的配置文件,也可以通过 API 直接获取配置数据。

客户端设计

下图简要描述了 Minerva 配置中心客户端(Minerva Agent)的实现原理:

  1. 用户在 Minerva 配置中心进行配置新增或变更操作,在点击发布之后配置会同步到机房代理服务(Minerva Proxy),再将配置数据写入机房内的 ETCD 中;
  2. 客户端(Minerva Agent)监听 ETCD 的数据变化来更新配置数据;
  3. 客户端(Minerva Agent)从配置中心获取到应用程序相关的最新版本配置文件后,更新本地的内存缓存;
  4. 客户端(Minerva Agent)在更新内存缓存的同时会在本地文件系统中缓存一份,在服务不可用或者网络故障时,让应用程序直接使用本地文件配置;
  5. 应用程序从客户端(Minerva Agent)获取最新的配置。 ### 容器虚拟化方案设计 ** **虚拟容器化是大势所趋,Minerva 配置中心对该场景有强大的技术支撑,设计思路是在宿主机中部署 Minerva Agent,保留整体层次结构,由 Minerva Agent 对所有的 pod 提供配置支持,如下图所示。

  1. Node 中部署 Minerva Agent,以获取配置信息;
  2. Pod 通过 API 借口获取当前应用的配置信息; ### 多机房方案设计 Minerva 配置中心支持多机房(IDC)统一管理,在实际场景中会同时使用多个地区的机房,机房之间不进行跨机房通信,所有的数据流转由某一个部署配置中心管理后台的机房完成,该机房依靠专线与其他机房进行通信,在遭遇专线网络故障时,可直接切换到外网 IP 保证服务稳定,对不同机房间的操作互相隔离,不产生依赖影响。

技术点详解

配置推送实现

  1. 配置通过 Minerva Proxy 由管理后台推送到对应机房的 ETCD 中;
  2. Minerva Agent 监听 ETCD 数据变化,获取到更新的配置数据;
  3. 配置数据在内存缓存和服务器/宿主机文件系统中更新。 ### 配置时序图 ** ** 配置的整个生命周期流转如下图所示,归纳为四个配置点进行表述:管理后台(前端交互 UI)、配置服务(管理后台服务)、下发服务(Minerva Proxy+Minerva Agent)、应用。

兜底配置策略

用户下发配置如果存在某些参数遗漏,例如 MySQL 或者 Redis 连接配置,用户对超时或者连接数没有配置,其中比较重要的就是超时,如果漏掉了会引发严重的线上问题,Minerva SDK 为了解决这个问题,开发了相关的兜底配置,对这些关键配置进行默认值补充。

配置回显实现

配置中心涉及配置下发后批量重启服务的执行流程如下,过程中强制配置回显,可保证配置下发状态和内容一定正确。

  1. 配置通过前一步的流程下发到应用程序;
  2. Minerva Agent 获取到配置数据之后,执行 Report 流程上报当前配置数据内容到配置中心;
  3. 应用程序给配置中心上报服务运行版本,以及当前服务状态;
  4. 配置中心接收到应用程序上报的版本符合下发版本,并且服务状态正常,便认可配置下发流程完成。 ### 高可用性分析 分析多故障场景,当前 Minerva 配置中心的降级方案如下:
场景 影响 降级方案
某台 minerva-agent 挂掉 无影响 minerva-agent 无状态,客户端重连其他的 minerva-agent
全部的 minerva-agent 挂掉 客户端无法读取 admin 配置的最新数据 客户端可以读取本地的文件缓存,新扩的机器可以从其他客户端获取缓存配置
某个 ETCD 节点挂掉 无影响 minerva-agent 可以重连其他 ETCD 节点
全部 ETCD 节点挂掉 minerva-agent 无法读取 admin 配置的最新数据,无法推送配置更新 minerva-agent 读取本地文件缓存提供给客户端
AdminServer 挂掉 客户端无影响,admin 无法管理配置
ConfigDB 宕机 客户端无影响,admin 配置的新数据无法持久化
专线故障 配置中心相关发布、回滚操作失败 使用公网 IP 继续相关操作

敏感配置信息

  • 适度隔离:将敏感配置信息同源代码、普通配置信息隔离存储。
  • 访问控制:通过白名单等方式,限制敏感配置信息的访问权限。
  • 加密存储:将敏感配置信息加密后存储,仅在使用前临时解密,以进一步防止信息泄露。
  • 安全传输:企业内网环境并非 100% 可信,通过 HTTPS 等加密手段以保证敏感配置信息的传输安全。
  • 日志记录:尽量详细记录下针对配置信息的操作,以便于事后追查或者合规审查。
  • 差别配置:不同的环境(例如生产环境,灰度环境,开发环境等)使用不同的证书或秘钥,以避免 “把鸡蛋放到一个篮子里”。

    为什么选择 ETCD

    为什么采用 ETCD 作为服务注册中心和配置存储/订阅通知引擎,而不是使用传统的 ZK,Eureka?我们大致总结了一下,有以下几方面的原因:

  • 它提供了强大和灵活的 K-V 存储能力,可以在保证性能的前提下对配置项进行最小粒度对存储

  • 它提供了对 key 或者 key 前缀的监听功能,正好满足我们对某些配置项需要动态下发的需求

  • 我们的项目本身就使用了 ETCD 做服务注册与发现和存储功能,维护和使用相对于 zk,Eureka 会熟练的多

    为什么采用 GRPC

    为什么 Minerva Agent 与 Client 之间的长链接通信我们采取 GRPCStream,而不是其他配置中心使用的 tcp 长链接,Http long polling?其实有下面一些考虑:

  • 相比于自己搭建和维护一套 TCP 长链接服务,GRPCStream 是现成的解决方案,可以帮我们屏蔽底层的通信细节和长链接管理

  • 相比于 Http long polling 服务器 hold 连接会消耗资源,GRPCStream 消耗更少的服务器资源

  • 我们内部基于 GRPC 服务的开发体系已经很成熟了,当 client 很多时,可以轻松解决横向扩展的问题

  • ETCDv3 本身也是 GRPC 实现的,可用性与可靠性比较有保证

    其他场景

    开关

  • 有时候 a 应用依赖 b 应用的新接口,但是 b 应用的发布时间点比较靠后,这时候 a 应用发布时可以加上一个默认关闭的开关,等 b 应用发布后再打开开关。

  • 有些新功能上线存在较高的风险,可以加个开关,一旦发现问题可以迅速关闭。

限流

一般的做法是在开关或 RPC 框架层添加限流逻辑,结合配置中心的动态推送能力实现动态调整限流规则配置。

数据源迁移

比如:需要将服务从 A 数据库迁移到 B 数据库,通过配置中心配置读写开关和比例配置,来达到动态读写数据库的操作,配合线下的旧数据补齐,平滑的完成数据迁移。

动态日志级别

服务运行过程中发生故障,我们一般会通过日志进行排查,如果 ERROR 日志查不出问题,一般需要打印 DEBUG 日志。相比于花费更多的时间去修改代码,发布部署应用,通过配置中心动态调整应用的日志打印级别显然更加安全和快速。

交互设计

界面概览

下图是 Minerva 配置中心的项目配置首页。

  • 页面左侧,可进行环境与机房的切换;
  • 页面中央,展示了两种不同的配置展现形式:配置表格、配置文本。用户进入时默认以配置文本的方式进行交互,优点是接近文本的使用方式。用户也可以切换到配置表格,切换之后可以很便捷的使用资源变量;
  • 页面上方,用户可进行配置发布、实例重启、配置回滚和查看发布历史的操作。 ### 配置表格交互 用户使用配置文本方式,不再赘述。下列图片展示配置表格的使用方式,以及配置变量的使用。

点击新增配置,有两种配置的新增方式:Key-Value 和资源变量的使用。默认情况下,用户输入 Key 和 Value 点击保存即可;用户使用配置变量的流程:点击关联资源按钮、选择资源类型、最后到配置列表中点击需要的配置即可使用。

配置文本交互

基础操作

  1. 点击配置文本,切换到配置文本编辑界面;
  2. 点击编辑,开启编辑器交互功能;
  3. 编辑器中写入配置文件,点击保存。

在编辑状态下可进行格式化、预览变更等操作。

预览变更

预览变更会直观的暂时配置的修改情况。

应用配置发布

用户点击配置发布按钮后,可以直观的看到本次要发布的配置与目前线上配置的差异,在填写发布信息之后点击提交按钮,即完成发布。

应用配置追踪

跟踪文件下发的三个状态:Systemd 接入状态、文件同步状态、配置文件使用状态。通过这三个状态可完全确认发布的配置是否是成功生效的。

全局资源管理

资源中心定义资源 k-v 的键值对,配置中心里通过对 key 对引用进行关联,当资源中心的资源值发生了变更,则配置中心在发布的时候会同步最新的资源进行发布,这样在迁移和维护资源时,对应用来说是无感知的,应用只需要重新发布一次应用就能完成资源配置项的更新。

搜索界面

管理后台提供资源变量管理界面,用户可以在这个页面进行资源的管理操作,包括:新增对应环境和机房的配置;修改资源参数;查询应用使用的依赖关系等功能,支持资源加密。

新增资源

新增资源变量:选择资源类型;确认环境和机房信息;输入资源名称,填写用户最后需要再配置文本中使用的配置值;增加备注信息;点击确定按钮即可完成资源变量增加,这里输入的资源变量数据,对其他使用者来讲值是加密不可见的。

配置依赖解析

配置管理后台加载配置解析的插件,周期性的解析目前的配置数据存入数据库中,可以更好地了解配置的使用情况,以及互相的依赖关系,快速筛选相关配置,根据配置以及服务的依赖关系我们提供了两种展示方式:

直接把关系用依赖图的方式表示;

用表格的方式展示各个项的详细属性;

总结

从 Minerva 配置中心的层次结构开始分析,设计了跨机房且各自独立的架构,具体分析了 Minerva Agent 的结构,阐述其在 Minerva 配置中心的作用和意义,结合目前的容器虚拟化方向并对实际支持策略进行描述;在架构分析的基础上对各个功能点进行分析,希望帮助各位进一步理解我们的设计思维;最后展示界面交互,以及斗鱼 Minerva 配置中心设计的全局配置管理和依赖解析界面。

加入斗鱼开源技术交流群

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio

后面四张图片失效了,看不了

astaxie 将本帖设为了精华贴 02月18日 10:30
astaxie 回复

明白了。石墨的图片贴过来不能直接看。我手动贴到 gocn 里

yulibaozi GoCN 每日新闻(2020-02-19) 中提及了此贴 02月19日 13:06

很棒的分享!

在写 client 代码的适合,是怎么保证更新更新的过程中不产生脏读的呢?

vue.js 回复

和 etcd 里的数据有 md5 的校验,确保完整写入到文件里

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册