作者 | 童子龙 掌门教育基础架构部架构师
导读:本文整理自作者于 2020 年云原生微服务大会上的分享《掌门教育云原生落地实践》,本文主要介绍了掌门教育云原生落地实践,主要围绕 Spring Cloud Alibaba & Nacos & Sentinel & Arthas 等微服务云原生技术栈实施构建,基于 Docker 和 阿里云 Kubernetes 云原生容器的实现落地,着重介绍 Nacos 服务器高可用性部署、监控,Nacos 和 Eureka 同步服务器高可用双向同步和容灾,以及和 DevOps 运维发布平台的整合。
公众号后台回复 818 即可获取直播回看地址和大会 PPT 合集。
背景
掌门教育自 2014 年正式转型在线教育以来,秉承“让教育共享智能,让学习高效快乐”的宗旨和愿景,经历云计算、大数据、人工智能、 AR / VR / MR 以及现今最火的 5G ,一直坚持用科技赋能教育。掌门教育的业务近几年得到了快速发展,特别是今年的疫情,使在线教育成为了新的风口,也给掌门教育新的机遇。
随着业务规模进一步扩大,流量进一步暴增,微服务数目进一步增长,使老的微服务体系所采用的注册中心 Eureka 不堪重负,同时 Spring Cloud 体系已经演进到第二代,第一代的 Eureka 注册中心已经不大适合现在的业务逻辑和规模,同时它目前被 Spring Cloud 官方置于维护模式,将不再向前发展。如何选择一个更为优秀和适用的注册中心,这个课题就摆在了掌门人的面前。
为什么选择 Spring Cloud Alibaba & Nacos?
经过对 Alibaba Nacos 、HashiCorp Consul 等开源注册中心做了深入的调研和比较,以下是各个注册中心的特性对比:
Nacos
支持 AP+CP 一致性共识协议
支持 Agent DNS-F 服务注册发现方式,跨语言
支持负载均衡,雪崩保护机制
支持多数据中心,跨注册中心迁移
Consul
只支持 CP 协议
支持 HTTP/DNS 协议
K8s CoreDns
支持 DNS 协议
结论:Nacos 满足目前掌门的服务治理技术栈,能实现注册中心的平滑迁移,社区发展非常活跃,所提供的特性,使得围绕 Spring Cloud Alibaba&Nacos 能够非常方便的构建云原生应用的动态服务注册发现。
Nacos server 落地
Nacos Server 部署概览图
Nacos Server 环境和域名
掌门的应用环境分为 4 套,DEV、FAT、UAT、PROD 分别对应开发、测试、准生产环境、生产环境,因此 Nacos Server 也分为 4 套独立环境。除了 DEV 环境是单机部署外,其他是集群方式部署。对外均以域名方式访问,SLB 做负载均衡,包括 SDK 方式连接 Nacos Server 和访问 Nacos Server Dashboard 控制台页面。
Nacos Server 环境隔离和调用隔离
Nacos 数据模型由 namespace / group / service 构成。可以通过创建不同的命名空间,做到同一个应用环境的基础上更细粒度的划分,隔离服务注册和发现。在某些场景下,开发本地有需要连接测试环境的 Nacos Server ,但其他测试服务不能调用到开发本地,这时候可以将 NacosDiscoveryProperties 的 enabled 属性设置为 false 。
Nacos Server 集成 Ldap
Nacos Server Dashboard 集成公司的 Ldap 服务,并在用户首次登录时记录用户信息。
Nacos 界面权限
Nacos Server Dashboard 用户首次登陆时,默认分配普通用户(即非 ROLE_ADMIN )角色,对查询以外的按钮均无操作权限,以免出现误操作导致服务非正常上下线。
Nacos 界面显示服务概览
Nacos Server Dashboard 页面增加服务总数及实例总数的统计,该信息每 5 秒刷新一次。
标准监控
基于公司现有的 Prometheus 、 Grafana 、 AlertManager 从系统层监控 Nacos。
高级监控
根据 Nacos 监控手册,结合 Prometheus 和 Grafana 监控 Nacos 指标。
服务实例状态监控
- 监听实例下线事件
- 监听实例注销事件
- 监听实例注册事件
- 监听实例上线事件
- 监听实例心跳超时事件
日志合并及 JSON 格式化
将 Nacos 多模块的日志统一按 info 、 warn、error 级别合并,定义 schema 字段标记不同模块,按 JSON 格式滚动输出到文件,供 ELK 采集展示。
业务服务上下线的告警
服务名大写告警
核心脚本
def registry(ip):
fo = open("service_name.txt", "r")
str = fo.read()
service_name_list = str.split(";")
service_name = service_name_list[random.randint(0,len(service_name_list) - 1)]
fo.close()
client = nacos.NacosClient(nacos_host, namespace='')
print(client.add_naming_instance(service_name,ip,333,"default",1.0,{'preserved.ip.delete.timeout':86400000},True,True))
while True:
print(client.send_heartbeat(service_name,ip,333,"default",1.0,"{}"))
time.sleep(5)
压测数据
压测结果图
总结:Nacos Server 是 3 台 1C4G 集群,同时承受 1499 个服务和 12715 个实例注册,而且 CPU 和内存长期保持在一个合适的范围内,果真 Nacos 性能是相当 OK 的。
Nacos Eureka Sync 落地
经过研究,我们采取了官方的 Nacos Eureka Sync 方案,在小范围试用了一下,效果良好,但一部署到 FAT 环境后,发现根本不行,一台同步服务器无法抗住将近 660 个服务(非实例数)的频繁心跳,同时该方案不具备高可用特点。
既然一台不行,那么就多几台,但如何做高可用呢?
我们率先想到的是一致性 Hash 方式。当一台或者几台同步服务器挂掉后,采用 Zookeeper 临时节点的 Watch 机制监听同步服务器挂掉情况,通知剩余同步服务器执行 reHash ,挂掉服务的工作由剩余的同步服务器来承担。通过一致性 Hash 实现被同步的业务服务列表的平均分配,基于对业务服务名的二进制转换作为 Hash 的 Key 实现一致性 Hash 的算法。我们自研了这套算法,发现平均分配的很不理想,第一时间怀疑是否算法有问题,于是找来 Kafka 自带的算法(见 Utils.murmur2 ),发现效果依旧不理想,原因还是业务服务名的本身分布就是不平均的,于是又回到自研算法上进行了优化,基本达到预期,下文会具体讲到。但说实话,直到现在依旧无法做到非常良好的绝对平均。
这个方案是个小插曲,当一台同步服务器挂掉后,由它的“备”顶上,当然主备切换也是基于 Zookeeper 临时节点的 Watch 机制来实现的。后面讨论下来,主备方案,机器的成本很高,实现也不如一致性 Hash 优雅,最后没采用。
折腾了这么几次后,发现同步业务服务列表是持久化在数据库,同步服务器挂掉后 ReHash 通知机制是由 Zookeeper 来负责,两者能否可以合并到一个中间件上以降低成本?于是我们想到了 Etcd 方案,即通过它实现同步业务服务列表持久化 + 业务服务列表增减的通知 + 同步服务器挂掉后 ReHash 通知。至此方案最终确定,即两个注册中心( Eureka 和 Nacos )的双向同步方案,通过 Etcd 来做桥梁。
注册中心迁移目标:
过程并非一蹴而就的,业务服务逐步迁移的过程要保证线上调用不受影响,例如, A 业务服务注册到 Eureka 上, B 业务服务迁移到 Nacos ,A 业务服务和 B 业务服务的互相调用必须正常;
过程必须保证双注册中心都存在这两个业务服务,并且目标注册中心的业务服务实例必须与源注册中心的业务服务实例数目和状态保持实时严格一致。
注册中心迁移原则:
一个业务服务只能往一个注册中心注册,不能同时双向注册;
一个业务服务无论注册到 Eureka 或者 Nacos,最终结果都是等效的;
一个业务服务在绝大多数情况下,一般只存在一个同步任务,如果是注册到 Eureka 的业务服务需要同步到 Nacos,那就有一个 Eureka -> Nacos 的同步任务,反之亦然;在平滑迁移中,一个业务服务一部分实例在 Eureka 上,另一部分实例在 Nacos 上,那么会产生两个双向同步的任务;
一个业务服务的同步方向,是根据业务服务实例元数据( Metadata )的标记 syncSource 来决定。
Nacos Eureka Sync 同步节点需要代理业务服务实例和 Nacos Server 间的心跳上报。Nacos Eureka Sync 将心跳上报请求放入队列,以固定线程消费,一个同步业务服务节点处理的服务实例数超过一定的阈值会造成业务服务实例的心跳发送不及时,从而造成业务服务实例的意外丢失;
Nacos Eureka Sync 节点宕机,上面处理的心跳任务会全部丢失,会造成线上调用大面积失败,后果不堪设想;
Nacos Eureka Sync 已经开始工作的时候,从 Eureka 或者 Nacos 上,新上线或者下线一个业务服务(非实例),都需要让 Nacos Eureka Sync 实时感知。
从各个注册中心获取业务服务列表,初始化业务服务同步任务列表,并持久化到 Etcd 集群中;
后续迁移过程增量业务服务通过 API 接口持久化到 Etcd 集群中,业务服务迁移过程整合 DevOps 发布平台。整个迁移过程全自动化,规避人为操作造成的遗漏;
同步服务订阅 Etcd 集群获取任务列表,并监听同步集群的节点状态;
同步服务根据存活节点的一致性 Hash 算法,找到处理任务节点,后端接口通过 SLB 负载均衡,删除任务指令轮询到的节点。如果是自己处理任务则移除心跳,否则找到处理节点,代理出去;
同步服务监听源注册中心每个业务服务实例状态,将正常的业务服务实例同步到目标注册中心,保证双方注册中心的业务服务实例状态实时同步;
业务服务所有实例从 Eureka 到 Nacos 后,需要业务部门通知基础架构部手动从 Nacos Eureka Sync 同步界面摘除该同步任务。
服务一致性 Hash 分片路由:
Solar 云原生微服务实践
服务维度的异常监控看板,聚合链路中异常数量,帮助业务快速定位问题
Solar 云原生容器化实践
PS:经过全面的压测,一个物理节点上启动一个 FileBeat 进程收集所有 Pod 日志,性能没有问题;如果存在日志收集不及时问题,则可以一个 Pod 挂载一个 FileBeat 进程来解决,这样的缺点是会占用更多系统资源。
FileBeat 将日志信息推送到 Kafka;
GoHangout 并发消费 Kafka 消息性能比较好。
结语
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/69975341/viewspace-2718410/,如需转载,请注明出处,否则将追究法律责任。