引文
本文是参加 2021 K+全球软件研发行业创新峰会 专题分享后的一些总结。同时也学习了其他多个专题分享,总体感觉是整个大会的分享专题覆盖面特别广,专业度都很高。此次大会的所有专题简介可参考 K+专题列表。
先做下自我介绍,本人目前就职于阿里云 SLS 团队,负责 SLS 数据加工服务研发工作,服务阿里云上客户的海量日志处理。对机器数据处理场景痛点、核心技术和架构有深刻理解。当前主要关注实时计算、云原生等技术,欢迎交流。顺便附上几张参会的现场照片。
专题介绍
据统计,在数据统计分析的场景中,80%时间(人力)都是用于数据的预处理,而最重要的分析逻辑其实只占20%的投入,这源于原始数据的多样性和无序性。这次分享的主题是如何实现免系统运维、低代码的数据处理系统。
此次分享我希望带给听众对于整个日志数据处理场景的现状、常规解决方案,以及带大家深入了解我们团队自研的数据加工服务的技术架构、核心技术难点等。另外就是数据加工的场景实践,以及对未来发展的简述。
场景、方案概述
首先我们来看一个概念“机器数据”。现实中我们是生活在数据的海洋里,比如我们的个人信息是存储在互联网的服务器中,对应的就是一条条数据。而这些服务器实时产生记录其运行状态的日志,对应的又是更多的数据。这就是机器数据的第一个特征,数据来源广泛,随之而来的就是数据量巨大。
其另一个特点是蕴含丰富的价值,比如我们刷手机时,每一次点击操作,都是一条数据,可以用于服务商人工智能算法的训练、优化,从而实现精准推荐、广告投放等。而APP在使用中崩溃事件,也会被记录并用于APP使用体验的优化。
其还有一个特点是数据格式非常复杂多样,包括非结构化、复杂结构化等无法直接用于分析的数据内容,要求在分析前做精细化的处理。
为了处理以上所述各种各样的机器数据,我们需要这么一个云上日志平台,统一存储、统一计算、统一分析所有类型的数据。
一个云上日志平台需要包括以下模块结构,其内部结构我画了一个简图参考下图:
- 采集端:作为数据链路的起点,需要能够采集各种数据源,同时实现高性能、低开销
- 消息队列:扛住数据洪峰,保护后台系统的同时,提高系统吞吐量
- 数据计算:让数据变得规范化、易使用,这是这次分享将继续深入的部分
- 数据存储(冷热分存):对于数据长时间保存的场景(比如合规需求),冷热分存可以有效降低存储成本
- 数据查询、分析、监控:数据的使用、分析是数据链路直接目的
- 交互式可视化:作为分析结果的展示输出
- 高级应用:比如 DevOps、SecOps、AIOps、可观测性等等
目前的开源生态对于日志数据有丰富的解决方案,其中上文所述的每个模块的可选方案如下图。以 Elasticsearch 作为存储核心组件为例,采集端可以使用其生态相关的 Beats 组建,消息队列则使用最流行的 Kafka,数据计算使用 Flink(Logstash 在计算性能上表现一般),可视化组件使用 Kibana。从这个案例可以看到,通过开原生态搭建完整的解决方案在技术上是可行的,但是实施复杂度很高,需要维护多套系统进行协同。
接下来我们看下数据加工的常见场景(具体场景很多,这里仅列典型、常见的场景):
- 规整:这是使用频率最高的场景,比如从文本日志数据中提取出关键信息,将其转为规范化的数据
- 富化:比如用户的点击数据中只包含商品 ID,在分析时需要从数据库关联起详细信息
- 脱敏:随着我国信息安全相关法律的完善,对于敏感数据(比如个人信息)的处理要求越来越高
- 分裂:在数据写出时,处于性能、便捷性考虑会将多条数据合并输出,在分析前则需要拆分出独立数据条目
- 分发:将不同类型的数据分别写到不同的特定目标中,以供下游定制化使用
我们总结一下基于开源生态搭建完整日志处理解决方案的核心痛点:
- 开源日志平台不完善:每个功能模块需要维护独立的系统,针对不同的数据类型(Log、Metric、Trace等)也需要不同的存储、计算方案
- 自建计算服务成本高:包含两个方面,一是需要专人进行搭建、运维系统,二是需要资源冗余,以备数据峰值之用,造成浪费
- 计算任务开发门槛高:要求开发者前期很多的知识储备、代码开发量大;数据边界处理极其繁琐,而且其可变形性高
在以上的内容中,我们讨论了云上日志系统的场景需求、整体架构、开源方案、实现痛点,那我们来看下一个典型的云上日志系统应该长什么样。
首先要能够统一接收处理不同的数据(系统日志、业务数据、用户操作等等);其次能够满足不同的场景需求,比如延迟要求极高的系统监控、T+1级别的业务分析、年级别的数据审计;另外能够服务于不同的业务角色,研发、运维、安全、运营。
基于以上所说典型的云上日志系统,我们需要做数据计算时可用的用户接口:
- Coding on API:典型的应用是 Flink、Spark Streaming、Logstash Ruby 等。这种方式的特点是极其灵活,几乎可以实现所有需求。但是缺点是门槛太高,需要投入太多精力来处理繁琐的边界条件,而且数据模式可能是动态的,需要一直保持维护。
- 扩展 SQL:SQL 目前可以说基本上统一了大数据计算的半壁江山,最直接的例子就是 Spark SQL、Flink SQL都很受欢迎。其优点是标准统一,只需要在上面扩展自定义函数就可以解决大多数问题。但是面对某些特殊场景时,SQL 是无法实现的,比如将数据分发到多个不同的目标中,由于SQL执行的输出都是一个数据表,无法完成按目标分发。
- DSL (Domain Specific Language):DSL 是在特定的场景中自创一套语法,通过实现内置函数来扩展能力范围,比如 Splunk Knowledge Object,InfluxDB Flux等等。其目标是将特定的场景封装掉具体实现细节,达到使用时极简化的效果。但是其有两个弱点:一是用户需要学习一套新的非通用语法,对于新用户上手不友好;二是可扩展性很难保障,因为每一个功能都封装对应的内置函数成本实在太高,对于商业化产品来说开发内置函数性价比太低,对于开源方案来说功能迭代、性能保证都是不确定的。
根据以上的对比,我们的思路是如果基于标准、而且简洁的语法(比如时下最流行的Python),对于每一个场景都持续实现相应的内置函数,则可以完成低代码的数据处理系统。具体设计与实现请看下文。
原理、架构,以及核心技术难点
总体来说,数据加工服务就是根据用户的计算任务配置信息,从相应的数据源读取原始数据,依据用户期望的规则加工完成以后,将每一条结果写出到其预期的目标中。整体设计包括以下几个关键点:
- 函数完整性:内置200+函数、400+Grok模式,无规则文本、复杂结构Json等等极简解析
- 代码编排:代码内自由编排,极简化实现数据处理、流转
- 计算能力:流式处理高吞吐,秒级延迟,极易扩展
- 下游生态:无缝对接下游可视化、异常检测、监控告警、数据湖等
- 用户友好:开箱即用,低代码,按量付费
如上文所说,数据加工服务基于Python语法设计用户侧接口,并提供完善的内置函数,这里我们将其称为 Cloud Data Processing Language (DPL)。
这里先说一下函数命名规则,函数的命名是通过其前缀确定函数的具体功能,有如下前缀:
e_
事件接收函数,其设计核心是隐藏事件变量在代码逻辑中的传递,而在运行时动态地加入事件变量,从而简化用户侧使用。其实现原理是闭包,比如调用函数e_func(*args, **kwargs)
时,内部具体实现如下。一个特殊的事件接收函数是v
,其作用是从事件中获取特定字段的值,比如v("x")
def e_func(event):
def impl(*args, **kwargs):
process event to result
return result
res_
表示外部资源连接函数,比如res_rds_mysql
dt_
表示时间处理函数,比如dt_parse
ip_
和geo_
表示 IP 处理函数,比如geo_parse
- 其他,比如
base64_
用于 Base64 数据编解码,url_
用于 URL 处理,mat_
表示数学计算函数等等
数据加工内置函数根据作用不同分为2大类(具体的函数举例可参考下图):
- 全局操作函数:完成特定场景计算,比如数据正则提取;同时也用于过程编排,比如 if-else、for-each、代码块等。此类函数设计核心是,它的返回值是被加工后的事件(或者事件列表),当用户编写代码逻辑时并不需要考虑返回值的接收,而是在系统运行中自动完成返回结果的接收与传递,进一步简化用户编写。
- 表达式函数:这一类函数设计目的是面向特定的实现逻辑较为复杂的计算场景,其核心设计是返回值是。它可以是事件接收函数,比如检索函数
e_search
用于判断输入事件是否满足给定查询条件;也可以不是事件接收函数,单纯用于实现计算,比如geo_parse
用于实现 IP地理位置解析,并不需要输入完整事件,只需要输入 IP 即可。
接下来我们看下数据加工自由编排的案例。如下图中所示,在数据流处理过程中,可以便捷的事件过滤(1 to 0/1)、分裂(1 to N)、分发(多目标输出)等场景。上文已经提到 SQL 的局限,这里再看一个例子,假如我们需要完成事件分裂、关联外表、字段精简这一系列需求,通过 SQL 就至少需要3层嵌套子查询,非常复杂。但是使用数据加工只需要如下3行代码。更多具体的案例实现请参考下一章节。
e_split(...)
e_table_map(...)
e_keep_fields(...)
下图描述的是数据加工服务整体架构。左侧源 logstore 基于分片(shard)存储,分片方便存储实现备份与扩展,数据加工服务的伸缩也是依赖存储的分片机制。当数据加工调度器启动一个新的作业时,会自动创建并绑定到源 logstore 的一个消费组,消费组内部的多个消费者独立负责处理不同分片中的数据。随着数据量增多,存储需要产生更多的分片,同时数据加工作业便可以扩展更多的消费者独立工作。
当作业需要关联外部资源的时候,每一个消费者独立维护一份资源的拷贝,实现高性能关联计算。
日志数据除了数据量巨大,另一个特点是数据量呈周期性波动,而且波动波峰极高极窄。比如对于直播平台,其一天的90%流量都来源于 21:00至23:00 这一个休闲时段,这就导致这一时间段的数据量是平时的数十倍。面对这样的极端场景,数据加工就需要能够实现计算力的弹性伸缩,保障用户作业高性能运转的同时,尽可能减少资源浪费。
下图中描述的是数据加工在任务调度中的弹性伸缩,在系统内部通过存储计算分离实现数据加工计算力自由伸缩,在用户侧看到的则是服务化计算平台,按量付费,无需关心繁杂的细节。
对于数据计算平台而言,用户侧的易用性保证和系统自身的高性能往往是一对矛盾,这也是数据加工服务实现中最大的技术难点。举个例子,我们通过dt_parse
函数解析时间字符串,需要能够实现用户无需指定时间内容的格式,而是有系统自动检测。这个功能有2方面的必要性:第一点是简化用户的配置步骤,时间格式的拼装比较繁琐;另一个更重要的点是日志数据很不规范,同一个数据源中可能存在不同的时间格式,甚至有多少种时间格式用户自己也无法确定,这种情况用户的逻辑几乎无法通过简洁的代码逻辑实现。
在以上的例子中,我们为了简化一个时间格式的参数,引入的问题是系统可能需要做几十种时间格式的尝试,这对于性能的开销是致命的。下图列出了一些我们在性能优化方面的思路,这里就不做详细探讨。
案例与实践
场景1 是数据字段的规整,这里的例子是 Nginx 访问日志的解析。通过4行代码就可以完成了无规则文本解析、KV数据提取、字段精简一系列操作,而且无需手动写复杂的正则表达式,直接使用内置 GROK 模式即可。
场景2 是信息富化,这里的例子接着上一场景,在http请求中,我们需要将请求状态码(http_status
字段)的详细描述(http_code_desc
字段)添加到原始数据中,具体实现参考下图。
场景3 是数据流转,比如在全球化的游戏APP中,其服务器需要部署在世界各个区域,以满足全球用户顺畅游戏,但是系统的监控工作就极其复杂,因为数据分散在不同的服务区中。基于数据加工可以实现全球数据规划,完成数据的归集与分发。跨地域的数据传输还可以结合阿里云全球加速服务保证数据的传输稳定。
场景4 是事件、字段剪裁,最常见的需求就是数据去冗余、无效信息。以下例子中是需要对系统日志数据按照日志层级做区别处理:error级别极其重要要求全量保存180天,用于系统运维;info级别用于业务分析,只需要部分字段,保存30天;debug级别则没有特殊用途,直接丢弃。这样既实现了数据下游的业务区分,又可以优化存储成本。
场景5 是增值能容,数据加工服务内置2类增值内容:
- GeoIP:从 IP 地址解析其对应的地理位置,可用于业务分析
- 威胁情报:比如访问 IP 近期是否发起过 DDoS 攻击、下载文件是否为可以木马等,可用于风险洞察
场景6 是结合 SLS 的分析、可视化能力完成用户画像,比如统计访问用户的地域分布、各个用户群体的喜好分析。
最后则是结合 SLS 的告警能力,对数据加工任务进行监控。比如当发现有新的数据格式写入,当前的处理逻辑无法覆盖则立即通知维护人员进行逻辑升级。
未来展望
- 拥抱开源:开源鼓励大家以更开放的心态分享技术,对于技术的发展有非常积极的作用。数据加工服务计划将 Cloud Data Processing Language (DPL) 标准化开源,回馈开源社区。
- 性能优化:性能对于计算服务来说是一个永恒的主题,通过技术的升级降低成本,最终让利于客户更是作为云上服务的最关键目标。数据加工服务计划通过计算下推进一步优化运行性能,与存储层做精细化整合。
结语
以上分享中所介绍,SLS 数据加工服务设计关键点有两方面:一是用户侧可以以极简方式完成数据处理(接口语法、服务化、按量付费等),将精力放在更加重要的业务场景;二是数据加工系统内部保障用户作业高性能运转,同时尽可能减少资源浪费。接下来也会在这两方面持续深化。
下图是 SLS 团队的技术博客,我们会不定期推出技术文章分享和产品更新介绍,欢迎大家订阅,有任何问题也欢迎与我们反馈。