五年双十一:SLS数据管道发展之路

日志服务SLS是一款飞天团队自研产品,服务云上云下3W+客户,并在阿里经济体中做为日志数据的基础设施,在过去几年中经历屡次双11、双12、新春红包锤炼。前端

在2019双十一中:算法

  • 服务阿里经济体3W+ 应用,1.5W外部独立客户
  • 峰值30TB/min、单集群峰值11TB/min
  • 单日志峰值600GB/min
  • 单业务线峰值1.2TB/min
  • 支持核心电商、妈妈、蚂蚁、菜鸟、盒马、优酷、高德、大文娱、中间件、天猫精灵等团队日志的全量上云
  • 与30+数据源、20+数据处理、计算系统无缝打通(以下)

可以服务这个体量和用户规模,对产品的功能、体验、系统的稳定性和可靠性的要求是很高的。感谢阿里经济体独一无二的环境与挑战,使得咱们过去五年中持续不断地对产品与技术进行考验与磨炼。数据库

数据管道是企业的基础设施

数据管道是什么?

数据管道概念诞生在2009年,提出的是LinkedIn工程师Jay Krep,Jay也是Apache Kafka做者+Confluent公司CEO。2012年他在文章《The Log: What every software engineer should know about real-time data's unifying abstraction》中提到设计管道设施的两个初衷:后端

  • 解耦生产者与消费者,下降系统对接复杂性
  • 定义出统一格式与操做语义

这两个核心痛点的解决+实时系统的兴起使得Kafka类产品在几年间有了一个量的飞跃,成了脍炙人口的基础软件。随着数据分析系统成为企业标配,各大厂商也逐步将数据管道产品化成服务互联网的服务,比较有表明性的有:缓存

  • AWS:Kinesis
  • Azure:EventHub
  • IBM:BlueMix Event Pipeline

数据管道(Data Pipeline)是实现系统之间数据迁移的载体,所以包括数据的采集、传输链路、存储队列、消费/转储等都属于数据管道的范畴。在SLS这里,咱们专为数据管道相关的功能集合起了一个单独的名称:LogHub,LogHub提供数30+种数据接入方式、提供实时数据管道、对接各种下游系统等功能。
然而数据管道因足够底层,在企业数字化过程当中担任重要的业务,必须足够可靠、足够稳定、确保数据的通畅,而且可以弹性知足流量变化需求。咱们把过去5年来咱们遇到的挑战展开,和你们回顾下。网络

数据管道的挑战

管道这个概念很是简单,以致于每一个开发者都能用20行代码写一个原型出来:架构

  • Immutable队列,只支持写入,不支持更改
  • 消费者写入后返回,写入时保序
  • 消费者能够根据点位来消费数据
  • 数据没法更改,只能根据TTL(写入前后顺序)进行删除

但在现实过程当中,维护一个天天读写百亿次,几十PB数据流量,而且被万级用户依赖的管道是一件颇有挑战的事情,举几个例子:并发

  • 生产者:某个消费者程序写错了,忽然引发一大波流量把管道入口都占满了。某些数据源因促销活动,流量在一个小时内上涨至原先十几倍或几百倍
  • 消费者:对一个数据源,同时有20+订阅者来同时消费数据
  • 天天有几百个数据源接入,方式各不相同,须要大量适配

这样例子天天都在发生,如何把简单的管道作得不简单,须要大量的工做,在下面篇幅中咱们娓娓道来。负载均衡

挑战1:生产者适配

SLS 初版本支持一类数据源-- 飞天格式的日志文件,在五年中逐步扩展到各语言SDK,移动端,嵌入式芯片,物联网和云原生等环境:less

飞天日志

SLS起源与阿里云的飞天项目,当时咱们飞天有一个基础的日志模块,几乎全部的系统都会使用这个模块打印日志,因此最开始咱们开发了Logtail用于采集飞天日志,当时的Logtail还只是一个阿里云飞天系统内部使用的工具。

SDK可扩展

随着非阿里云团队使用,因此咱们扩展了Logtail,支持通用的日志格式,好比正则、Json、分隔符等等。同时还有不少应用不但愿落盘,所以咱们提供了各类语言的SDK用于日志上传的代码集成。

多平台接入

随着移动互联网兴起,咱们专门针对移动端开发了Android、IOS的SDK,便于用户快速接入日志;这个时间点阿里也开始了微服务改造、pouch开始上线,Logtail开始兼容pouch,同时咱们还专门为Java微服务提供Log4J、LogBack的Appender,提供数据直传的服务。
对ARM平台、嵌入式系统、国产化系统也定制适配客户端进行接入。

Logtail平台化

在2018年初,为了应对多样化的需求,咱们为Logtail增长了插件功能,有自定义需求的用户能够经过开发插件的方式扩展Logtail,实现各类丰富的功能;同时咱们也紧跟时代步伐,支持云原生、智能设备、IoT等新兴领域的数据采集

云原生支持

随着云原生落地,Logtail的数据采集在18年初就开始全面支持Kubernetes,并提供了CRD(CustomResourceDefinition)用于日志和Kubernetes系统的集成,目前这套方案已经应用在了集团内、公有云几千个集群中。

云原生后的无盘化

在阿里高度虚拟化的场景中,一台物理机可能运行上百个容器,传统的日志落盘采集方式对物理机磁盘的竞争很大,会影响日志写入性能,间接影响应用的RT;同时天天物理机须要为各个容器准备日志的磁盘空间,形成巨大的资源冗余。

所以咱们和蚂蚁系统部合做开展了日志无盘化项目,基于用户态文件系统,为应用虚拟出一个日志盘,而日志盘的背后直接经过用户态文件系统对接Logtail并直传到SLS,以最快的方式实现日志可看、可查。

挑战2:多协议支持

SLS服务端支持HTTP协议写入,也提供了众多SDK和Agent,但在不少场景下仍是和数据源间有巨大鸿沟,例如:

  • 客户基于开源自建系统,不接受二次改造,但愿只修改一下配置文件就能接入;
  • 不少设备(交换机、路由器)提供的固定协议,没法使用HTTP协议;
  • 各类软件的监控信息、二进制格式等,而这些开源Agent能够支持。

为此SLS开展了通用协议适配计划,除HTTP外还兼容Syslog,Kafka、Promethous和JDBC四种协议来兼容开源生态。用户现有系统只须要修改写入源便可实现快速接入;已有的路由器、交换机等能够直接配置写入,无需代理转发;支持众多开源采集组件,例如Logstash、Fluentd、Telegraf等。

挑战3:客户端(Agent)流控

在2017年先后,咱们遇到了另一个挑战:单机Agent的多租户流控,举一个例子:

  • 某主机上有20+种日志,其中有须要对帐的操做日志,也有级别为Info的程序输出日志
  • 因日志生产者的不可控,在一段时间内可能会大量产生程序输出日志
  • 该数据源会在短期将采集Agent打爆,引发关键数据没法采集、或延迟采集

咱们对Agent(Logtail)进行了一系列多租户隔离优化:

  • 经过时间片采集调度保证各个配置数据入口的隔离性和公平性
  • 设计多级高低水位反馈队列保证在极低的资源占用下依然能够保证各处理流程间以及多个配置间的隔离性和公平性,
  • 采用事件处理不阻塞的机制保证即便在配置阻塞/停采期间发生文件轮转依然具备较高的可靠性
  • 经过各个配置不一样的流控/停采策略以及配置动态更新保证数据采集具有较高的可控性

该功能上线后,通过不断调优,较好解决了单机上多个数据源(租户)公平分配的问题。

挑战4:服务端流控

除了客户端流控外,咱们在服务端也支持两种不一样的流控方式(Project级、Shard级反压),防止单实例异常在接入层、或后端服务层影响其余租户。咱们专门开发QuotaServer模块,提供了Project全局流控和Shard级流控两层流控机制,在百万级的规模下也能实现秒级的流控同步,保证租户之间的隔离性以及防止流量穿透致使集群不可用。

粗粒度流控:Project级

  • 每秒上千个Nginx前端,将各类接收到的Project的流量、请求次数进行汇总,发送至QuotaServer(也是分布式架构,按照Project的进行分区)
  • Quota Server汇总全部来自各Nginx的Project统计信息,计算出每一个Project的流量、qps是否超过设置的quota上限,肯定否须要禁用Project的各种操做,以及禁用时间
  • 对于超过Quota的Project列表,QuotaServer能秒级通知到全部的Nginx前端
  • Nginx前端获取禁用Project列表以后,马上作出反应,拒绝这些Project的请求。

Project全局流控最主要的目的是限制用户总体资源用量,在前端就拒绝掉请求,防止用户实例的流量穿透后端把整个集群打爆。真正作到流控更加精细、语义更加明确、可控性更强的是Shard级别流控。

细粒度流控:Shard级

  • 每一个shard明肯定义处理能力, 如5MB/sec写入,10MB/sec的读取
  • 在shard所在的机器资源有空闲的时候,尽可能处理(也有资源消耗上限限制)
  • 当shard队列出现堵塞,根据shard流量是否超过quota,返回用户是限流仍是系统错误(返回的Http错误码是403仍是500),同时将Shard限流信息通知QuotaServer
  • QuotaServer接收到限流信息后,经过Nginx和QuotaServer之间存在Long pull通道,可瞬时将限流信息同步至全部的Nginx
  • Nginx端得到Shard的流控信息以后,对shard进行精确的流控

经过shard级别流控,好处很是明显:

  • 每一个shard接收的流量有上限,异常流量在前端Nginx直接被拒绝,在各类状况下,都没法穿透至后端
  • Project的流控不做为主要流控手段,只做为用户保护手段,防止代码异常等状况而致使的流量剧增
  • 根据错误码(http code是403仍是500),用户能够和明确知道是被限流了,仍是后端日志服务出现问题
  • 出现403流控错误后,用户能够直接经过分裂shard方式,来获取更高的吞吐,用户得到更多自主处理权(花钱买资源)

挑战5:消费端(高并发)

解决日志消费问题仍是须要从应用场景出发,SLS做为实时管道,绝大部分消费场景都是实时消费,SLS针对消费场景提供了一层Cache,但Cache策略单一,随着消费客户端增多、数据量膨胀等问题而致使命中率愈来愈低,消费延迟愈来愈高。后来咱们从新设计了缓存模块:

  • 全局缓存管理,对于每个Shard的消费计算消费权值,优先为权值高的Shard提供缓存空间;
  • 更加精细化、启发式的缓存管理,根据用户近期时间的消费状况来动态调整缓存大小;
  • 对于高保用户,强制分配定量的缓存空间,确保不受其余用户影响。

上述优化上线后,集群日志平均消费延迟从5ms下降到了1ms之内,有效缓解双十一数据消费压力。

挑战6:消费端(多实例与并发)

在以微服务、云原生为主导的大背景下,应用被切分的愈来愈细、整个链路也愈来愈复杂,其中产生的日志种类和数量也愈来愈多;同时日志的重要性也愈来愈强,同一个日志可能会有好几个甚至数十个业务方须要消费。

传统的方式粗暴简单,须要日志的人本身去机器上采集,最终一份日志可能被重复采集几十遍,严重浪费客户端、网络、服务端的资源。

SLS从源头上禁止同一文件的重复采集,日志统一采集到SLS后,咱们为用户提供ConsumerGroup用于实时消费。但伴随着日志的细分化以及日志应用场景的丰富化,SLS的数据消费逐渐暴露出了两个问题:

  1. 日志细分场景下,ConsumerGroup没法支持同时消费多组Logstore的日志,其中的日志还可能跨越多个Project、隶属于多个不一样帐号,资源映射和权限归属管理愈加复杂;
  2. ConsumerGroup分配的最小单位是Shard,SLS的一个Shard在不开启索引的状况下能够支撑几十MB/s的写入,而不少消费端单机并无能力处理几十MB/s的数据,形成严重的生产、消费不对等。

View消费模式

针对日志细分场景下的资源映射和权限归属管理等问题,咱们和蚂蚁日志平台团队合做开发了View消费模式(思路来源于数据库中View),可以将不一样用户、不一样logstore的资源虚拟成一个大的logstore,用户只须要消费虚拟的logstore便可,虚拟logstore的实现以及维护对用户彻底透明。该项目已经在蚂蚁集群正式上线,目前已经有数千个View消费实例在工做中。

Fanout消费模式

针对单消费者能力不足的问题,咱们对ConsumerGroup进一步加强,开发了Fanout消费模式,在Fanout模式下,一个Shard中的数据可交由多个消费者处理,将Shard与消费者解耦,完全解生产者消费者能力不匹配的问题。同时消费端无需关心Checkpoint管理、Failover等细节,Fanout消费组内部所有接管。

挑战7:自动化运维

SLS对外SLA承诺99.9%服务可用性(实际99.95%+),刚开始的时候咱们很难达到这样的指标,天天收到不少告警,常常夜里被电话Call醒,疲于处理各类问题。总结下来主要的缘由有2点:

  1. 热点问题:SLS会把Shard均匀调度到各个Worker节点,但每一个Shard实际负载不一并且随着时间会动态变化,常常因为一些热点Shard存在同一台机器而致使请求变慢甚至超出服务能力;
  2. 出现问题定位时间太长:线上问题终究不可避免,为了实现99.9%的可靠性,咱们必须可以在最短的时间内定位问题,及时止血。虽然有不少监控和日志,但人工去定位问题仍是要花不少时间。

自动热点消除

针对热点问题,咱们在系统中增长了调度角色,经过实时数据收集和统计后,自动作出调整,来消除系统中存在的热点,主要有如下两个手段:

  • 自动负载均衡

    • 系统实时统计各节点的负载,以及节点上每一个数据分区对于资源的消耗(CPU、MEM、NET等资源)
    • 负载信息汇报至调度器,调度器自动发现当前是否有节点处于高负载状况
    • 对于负载太高节点,经过优化组合的方式,将高压力数据分区,自动迁移到负载低的节点,达到资源负载均衡的目的
  • 自动分裂

    • 实时监控每一个Shard负载压力
    • 若是发现持续超过单分片处理上限,则启动分裂
    • 旧的分区变成Readonly,生成2个新的分区,迁移至其余节点

实际场景下有不少状况须要特殊考虑,例如颠簸状况、异构机型、并发调度、迁移的负面影响等,这里就再也不展开。

秒级流量分析(Root Cause Analysis)

目前SLS线上收集了数千种实时指标,天天的访问日志有上千亿,出现问题时纯粹手工调查难度很是大。为此咱们专门开发了根因分析相关算法,经过频繁集和差别集的方式,快速定位和异常最相关的数据集合。

如样例中,将出现错误(status >= 500)的访问数据集,定义为异常集合A,在这个集合发现90%的请求,都是由ID=1002引发,因此值得怀疑,当前的错误和ID=1002有关,同时为了减小误判,再从正常的数据集合B(status <500)中,查看ID=1002的比例,发如今集合B中的该ID比例较低,因此更增强系统判断,当前异常和这个ID=1002有很是高的相关性。

借助此种方法大大缩短了咱们问题调查的时间,在报警时咱们会自动带上根因分析结果,不少时候收到告警时就已经可以定位具体是哪一个用户、哪台机器仍是哪一个模块引起的问题。

其余挑战:正在解决的问题

1. Shardless

为了便于水平扩展咱们引入了Shard的概念(相似Kafka Partition),用户能够经过分裂Shard、合并Shard来实现资源的伸缩,但这些概念也会为用户带来不少使用上的困扰,用户须要去了解Shard的概念、须要去预估流量分配Shard数、有些时候由于Quota限制还须要手动分裂...

优秀的产品应该对用户暴露尽量少的概念,将来咱们会弱化甚至去除Shard概念,对于用户而言,SLS的数据管道只须要声明必定的Quota,咱们就会按照对应的Quota服务,内部的分片逻辑对用户完全透明,作到管道能力真正弹性。

2. 从At Least Once到Exactly Once

和Kafka同样,SLS目前支持At Least Once写入和消费方式,但不少核心场景(交易、结算、对帐、核心事件等)必需要求Exactly Once,如今不少业务只能经过在上层包装一层去重逻辑来Work around,但实现代价以及资源消耗巨大。

立刻咱们会支持写入和消费的Exactly Once语义,且Exactly Once语义场景下也将支持超大流量和高并发。

3. LogHub Select功能(过滤下推)

和Kafka相似,SLS支持的消费是Logstore级别的全量消费方式,若是业务只须要其中的一部分数据,也必须将这段时间的全部数据全量消费才能获得。全部的数据都要从服务端传输到计算节点再进行处理,这种方式对于资源的浪费极其巨大。

所以将来咱们会支持计算下推到队列内部,能够直接在队列内进行无效数据过滤,大大下降无效的网络传输和上层计算代价。

双12来袭!500元淘宝红包、iPhone11等你拿https://www.aliyun.com/1212/2019/home?utm_content=g_1000092611


本文做者:元乙

阅读原文

本文为云栖社区原创内容,未经容许不得转载。

相关文章
相关标签/搜索