LC3视角:Kubernetes下日志采集、存储与处理技术实践

摘要: 在Kubernetes服务化、日志处理实时化以及日志集中式存储趋势下,Kubernetes日志处理上也遇到的新挑战,包括:容器动态采集、大流量性能瓶颈、日志路由管理等问题。本文介绍了“Logtail + 日志服务 + 生态”架构,介绍了:Logtail客户端在Kubernetes日志采集场景下的优点;日志服务做为基础设施一站式解决实时读写、HTAP两大日志强需求;日志服务数据的开放性以及与云产品、开源社区相结合,在实时计算、可视化、采集上为用户提供的丰富选择。node

Kubernetes日志处理的趋势与挑战nginx

Kubernetes的serveless化web

Kubernetes容器技术促进了技术栈的去耦合,经过引入栈的分层使得开发者能够更加关注自身的应用程序和业务场景。从Kubernetes自己来看,这个技术解耦也在更进一步发展,容器化的一个发展的趋势是:这些容器都将会在无服务器的基础设施上运行。正则表达式

谈到基础设施,首先能够联想到云,目前在AWS、阿里云、Azure的云上都提供了无服务器化的Kubernetes服务。在serverless Kubernetes上,咱们将再也不关心集群与机器,只须要声明容器的镜像、CPU、内存、对外服务方式就能够启动应用。算法

图片描述

如上图,左右两边分别是经典Kubernetes、serverless Kubernetes的形态。在从左向右发展的过程当中,日志采集也变得复杂:docker

在一个Kubernetes node上,可能会运行更大规模量级的pod,每一个pod上均可能有日志或监控指标采集需求,意味着单node上的日志量会更大。
一个Kubernetes node上可能会运行更多种类的pod,日志采集来源变得多样化,各种日志的管理、打标需求愈来愈迫切。
日志实时性需求愈来愈强
首先要强调的是,并不是全部日志都须要实时处理,当前不少"T+1"时效的日志交付依然很是重要,好比:作BI可能天级别延迟足够,ctr预估可能1小时延迟的日志也能够。数据库

可是,在有些场景下,秒级或更高时效性的日志是必备前提条件,下图横坐标从左到右的对比展现了数据实时性对于决策的重要性。json

图片描述

举两个场景来谈一下实时日志对于决策的重要性:后端

告警处理。搞devops同窗都深有体会,线上问题越早发现、越早处理咱们就能够更淡定,处理时间拖得久了故障就可能跟着升级。实时日志帮助咱们更快发现系统的异常指标,触发应急处理流程。
AIOps。目前已经有一些算法基于日志作异常点检测、趋势预测:根据日志的pattern变化态势发现正常和异常状况下各种型日志出现的分布;针对IT业务系统,给定参数因子、变量对诸如硬盘故障等问题进行建模,加以实时日志来实现故障事件预警。
日志的集中式存储
日志来源有不少,常见的有:文件,数据库audit log,网络数据包等等。而且,针对同一份数据,对于不一样的使用者(例如:开发、运维、运营等)、不一样的用途(例如:告警、数据清洗、实时检索、批量计算等),存在使用多种方式重复消费日志数据的状况。缓存

在日志数据的系统集成上,从数据源到存储节点再到计算节点,能够定义为一个pipeline。以下图,从上到下的变化是:日志处理正在从O(N^2) pipelines向O(N) pipelines演进。

图片描述

在过去,各种日志用特定的方式来存储,采集到计算链路不具被通用和复用条件,pipeline很是复杂,数据存储也可能重复冗余。当前日志数据集成上,经过依赖一个中枢(Hub)来简化日志架构的复杂度、优化存储利用率。这个基础设施级别的Hub很是重要,须要支持实时pub/sub,可以处理高并发的写入、读取请求,提供海量的存储空间。

Kubernetes日志采集方案的演进
前面一节总结了Kubernetes日志处理上的趋势,那么家下来会盘点一下Kubernetes上几种常见日志采集的作法。

命令行工具
Kubernetes集群上要看日志,最基础的作法就是登机器,运行kubectl logs就能够看到容器写出的stdout/stderr。

图片描述

基础的方案无法知足更多需求:

只支持标准输出
数据不能持久化
除了看作不了别的事
节点日志文件落盘
在Kubernetes node维度去处理日志,docker engine将容器的stdout/stderr重定向到logdriver,而且在logdriver上能够配置多种形式去持久化日志,好比以json格式保存文件到本地存储。

和kubectl命令行相比,更进一步是把日志作到了本地化存储。能够用grep/awk这些Linux工具去分析日志文件内容。

图片描述

这个方案至关于回到了物理机时代,但仍然存在不少问题没有解决:

基于docker engine和logdriver,除了默认的标准输出,不支持应用程序的日志
日志文件rotate屡次或者Kubernetes node被驱逐后数据会丢失
无法跟开源社区、云上的数据分析工具集成
基于这个方案的一个进化版本是在node上部署日志采集客户端,将日志上传到中心化日志存储的设施上去。目前这也是推荐的模式,会在下一节再作介绍。

sidecar模式日志客户端采集
一种伴生模式,在一个pod内,除了业务容器外,还有一个日志客户端容器。这个日志客户端容器负责采集pod内容器的标准输出、文件、metric数据上报服务端。

图片描述

这个方案解决了日志持久化存储等基本的功能需求,但两个地方有待提高:

一个节点上若是运行了N个pod,就意味着会同时运行着N个日志客户端,形成CPU、内存、端口等资源的浪费。
Kubernetes下须要为每一个pod单独进行采集配置(采集日志目录,采集规则,存储目标等),不易维护。
日志直写
直写方案通常是经过修改应用程序自己来实现,在程序内部组织几条日志,而后调用相似HTTP API将数据发送到日志存储后端。

图片描述

带来的好处是:日志格式能够按需DIY,日志源和目标的路由能够任意配置。

也能够看到使用上的限制:

侵入代码会对业务改造有直接的依赖,推进业务改造通常比较漫长。
应用程序在发数据到远端遇到异常(好比网络抖动,接收服务端内部错误)时,须要在有限内存中缓存数据作重试,最终仍是有几率形成数据丢失。
Kubernetes日志处理架构
来自社区的架构
目前见到比较多的架构中,采集工做经过每一个Kubernetes node上安装一个日志客户端完成:

Kubernetes官方推荐的是treasure data公司开源的fluentd,它的性能现、插件丰富度比较均衡。
社区有也很多在使用elastic公司开源的beats系列,性能不错,插件支持略少一些。针对一种数据须要部署特定的客户端程序,好比文本文件经过filebeats来采集。
也有些架构在使用logstash,ETL支持很是丰富,可是JRuby实现致使性能不好。
图片描述

日志客户端把数据格式化好以后用指定协议上传到存储端,常见的选择有Kafka。Kafka支持实时订阅、重复消费,后期能够再根据业务须要把数据同步到其它系统去,好比:业务日志到Elastic Search作关键词查询,结合Kibana作日志可视化分析;金融场景日志要长期留存,能够选择投递Kafka数据到AWS S3这样的高性价比存储上。

这个架构看起来简洁有效,但在Kubernetes下距离完美还有些细节问题要解决:

首先,这是一个标准的节点级采集方案,Kubernetes下fluentd等客户端的程序部署、采集配置管理是个难题,在日志采集路由、数据打标、客户端配置等问题没有针对性优化。
其次,在日志的消费上,虽然Kafka的软件生态足够丰富,可是仍然须要专业人员来维护,要作业务规划、考虑机器水位、处理硬件损坏。若是要查询分析日志,还须要有对Elastic Search系统有足够了解。咱们知道在PB级数据场景下,分布式系统的性能、运维问题开始凸显,而驾驭这些开源系统须要很强的专业能力。
日志服务的Kubernetes日志架构实践
咱们提出基于阿里云日志服务的Kubernetes日志处理架构,用以补充社区的方案,来尝试解决Kubernetes场景下日志处理的一些细节体验问题。这个架构能够总结为:“Logtail + 日志服务 + 生态”。

图片描述

首先,Logtail是日志服务的数据采集客户端,针对Kubernetes场景下的一些痛点作了针对性设计。也是按照Kubernetes官方建议的方式,在每一个node上只部署一个Logtail客户端,负责这个node上全部的pod日志采集。

其次,针对关键词搜索和SQL统计这两个基本的日志需求:日志服务提供了基础的LogHub功能,支持数据的实时写入、订阅;在LogHub存储的基础上,能够选择开启数据的索引分析功能,开启索引后能够支持日志关键词查询以及SQL语法分析。

最后,日志服务的数据是开放的。索引数据能够经过JDBC协议与第三方系统对接,SQL查询结果与诸如阿里云DataV、开源社区的Grafana系统很方便地集成;日志服务的高吞吐的实时读写能力支撑了与流计算系统的对接,spark streaming、blink、jstorm等流计算系统上都有connector支持;用户还能够经过全托管的投递功能把数据写入到阿里云的对象存储OSS,投递支持行存(csv、json)、列存(parquet)格式,这些数据能够用做长期低成本备份,或者是经过“OSS存储+E-MapReduce计算”架构来实现数据仓库。

日志服务的优点
从四个点上来描述日志服务的特色:

在可靠性和稳定性上,它支撑了阿里集团和蚂蚁金服屡次双十一和双十二的大促。
在功能上一站式覆盖了Kafka + ElasticSearch大部分场景。
做为云上的基础设施能够方便地实现弹性伸缩,对于用户来讲,大促时不须要操心买机器扩容、天天坏上数十个盘须要维修等问题。
日志服务也一样具有云的0预付成本、按需付费的特色,而且目前每个月有500MB的免费额度。
图片描述

回顾第一节中提到的Kubernetes日志处理的趋势与挑战,这里总结了日志服务的三个优点:

做为日志基础设施,解决了各类日志数据集中化存储问题。
服务化的产品带给用户更多的易用性,与Kubernetes在serverless的目标上也是契合的。
功能上同时知足实时读写、HTAP需求,简化了日志处理的流程与架构。
日志服务结合社区力量进行Kubernetes日志分析
Kubernetes源自社区,使用开源软件进行Kubernetes日志的处理也是一些场景下的正确选择。

日志服务保证数据的开放性,与开源社区在采集、计算、可视化等方面进行对接,帮助用户享受到社区技术成果。

图片描述

以下图,举一个简单的例子:使用流计算引擎flink实时消费日志服务的日志库数据,源日志库的shard并发与flink task实现动态负载均衡,在完成与MySQL的meta完成数据join加工后,再经过connector流式写入另外一个日志服务日志库作可视化查询。

图片描述

Logtail在Kubernetes日志采集场景下的设计
在本文第二节咱们回顾了Kubernetes日志采集方案演进过程当中遇到的问题,第三节介绍了基于阿里云日志服务的功能、生态。在这一节会重点对Logtail采集端的设计和优化工做作一些介绍,细数Logtail如何解决Kubernetes日志采集上的痛点。

Kubernetes采集的难点
采集目标多样化

容器stdout/stderr
容器应用日志
宿主机日志
开放协议:Syslog、HTTP等
采集可靠性

性能上须要知足单node上大流量日志场景,同时兼顾采集的实时性
解决容器日志易失性问题
在各类状况下尽可能保证采集数据的完整性
动态伸缩带来的挑战

容器扩、缩容对自动发现的要求
下降Kubernetes部署的复杂度
采集配置易用性

采集配置怎么部署、管理
不一样用途的pod日志须要分门别类存储,数据路由怎么去管理
Logtail高可靠采集
Logtail支持至少at-least-once采集的语义保证,经过文件和内存两种级别的checkpoint机制来保证在容器重启场景下的断点续传。

日志采集过程当中可能遇到各类各样的来自系统或用户配置的错误,例如日志格式化解析出错时咱们须要及时调整解析规则。Logtail提供了采集监控功能,能够将异常和统计信息上报日志库并支持查询、告警。

优化计算性能解决单节点大规模日志采集问题,Logtail在不作日志字段格式化的状况(singleline模式)下作到单cpu核100MB/s左右的处理性能。针对网络发送的慢IO操做,客户端将多条日志batch commit到服务端作持久化,兼顾了采集的实时性与高吞吐能力。

在阿里集团内部,Logtail目前有百万级规模的客户端部署,稳定性是不错的。

丰富的数据源支持
应对Kubernetes环境下复杂多样的采集需求,Logtail在采集源头上能够支持:stdout/stderr,容器、宿主机日志文件,syslog、lumberjack等开放协议数据采集。

将一条日志按照语义切分为多个字段就能够获得多个key-value对,由此将一条日志映射到table模型上,这个工做使得在接下来的日志分析过程当中事半功倍。Logtail支持如下一些日志格式化方式:

多行解析。好比Java stack trace日志是由多个天然行组成的,经过行首正则表达式的设置来实现日志按逻辑行切分。
自描述解析。支持csv、json等格式,自动提取出日志字段。
经过正则、自定义插件方式知足更多特定需求。
对于一些典型的日志提供内置解析规则。好比,用户只须要在web控制台上选择日志类别是Nginx访问日志,Logtail就能够自动把一条访问日志按照Nginx的log format配置抽取出client_ip、uri等等字段。
应对节点级容器动态伸缩
图片描述

容器天生会作常态化扩容、缩容,新扩容的容器日志须要及时被采集不然就会丢失,这要求客户端有能力动态感知到采集源,且部署、配置须要作到足够的易用性。Logtail从如下两个维度来解决数据采集的完整性问题:

部署

经过DaemonSet方式来快速部署Logtail到一个Kubernetes node上,一条指令就能够完成,与K8S应用发布集成也很方便。
Logtail客户端部署到node上之后,经过domain socket与docker engine通讯来处理该节点上的容器动态采集。增量扫描能够及时地发现node上的容器变化,再加上按期全量扫面机制来保证不会丢失掉任何一个容器更改事件,这个双重保障的设计使得在客户端上能够及时、完整发现候选的监控目标。
采集配置管理

Logtail从设计之初就选择了服务端集中式采集配置管理,保证采集指令能够从服务端更高效地传达给客户端。这个配置管理能够抽象为"机器组+采集配置"模型,对于一个采集配置,在机器组内的Logtail实例能够即时获取到机器组上所关联的采集配置,去开启采集任务。
针对Kubernetes场景,Logtail设计了自定义标识方式来管理机器。一类pod能够声明一个固定的机器标识,Logtail使用这个机器标识向服务端汇报心跳,同时机器组使用这个自定义标识来管理Logtail实例。当Kubernetes节点扩容时,Logtail上报pod对应的自定义机器标识到服务端,服务端就会把这个机器组上的挂载的采集配置下发给Logtail。目前在开源采集客户端上,常见的作法是使用机器ip或hostname来标识客户端,这样在容器伸缩时,须要及时去增删机器组内的机器ip或hostname,不然就会致使数据采集的缺失,须要复杂的扩容流程保证。
解决采集配置管理难题
Logtail提供两种采集配置的管理方式,用户根据本身的喜爱任选来操做:

CRD。与Kubernetes生态深度集成,经过在客户端上事件监听能够联动建立日志服务上的日志库、采集配置、机器组等资源。
WEB控制台。上手快,可视化方式来配置日志格式化解析规则,经过wizard完成采集配置与机器组的关联。用户只须要按照习惯来设置一个容器的日志目录,Logtail在上开启采集时会自动渲染成宿主机上的实际日志目录。
咱们将日志从源到目标(日志库)定义为一个采集路由。使用传统方案实现个性化采集路由功能很是麻烦,须要在客户端本地配置,每一个pod容器写死这个采集路由,对于容器部署、管理会有强依赖。Logtail解决这个问题的突破点是对环境变量的应用,Kubernetes的env是由多个key-value组成,在部署容器时能够进行env设置。Logtail的采集配置中设计了IncludeEnv和ExcludeEnv配置项,用于加入或排除采集源。在下面的图中,pod业务容器启动时设置log_type环境变量,同时Logtail采集配置中定义了IncludeEnv: log_type=nginx_access_log,来指定收集nginx类用途的pod日志到特定日志库。

图片描述

全部在Kubernetes上采集到的数据,Logtail都自动进行了pod/namesapce/contanier/image维度的打标,方便后续的数据分析。

日志上下文查询的设计
上下文查询是指:给定一条日志,查看该日志在原机器、文件位置的上一条或下一条日志,相似于Linux上的grep -A -B。

在devops等一些场景下,逻辑性异常须要这个时序来辅助定位,有了上下文查看功能会事半功倍。而后在分布式系统下,在源和目标上都很难保证原先的日志顺序:

在采集客户端层面,Kubernetes可能产生大量日志,日志采集软件须要利用机器的多个cpu核心解析、预处理日志,并经过多线程并发或者单线程异步回调的方式处理网络发送的慢IO问题。这使得日志数据不能按照机器上的事件产生顺序依次到达服务端。
在分布式系统的服务端层面,因为水平扩展的多机负载均衡架构,使得同一客户端机器的日志会分散在多台存储节点上。在分散存储的日志基础上再恢复最初的顺序是困难的。
传统上下文查询方案,通常是根据日志到达服务端时间、日志业务时间字段作两次排序。这在大数据场景下存在:排序性能问题、时间精度不足问题,没法真实还原事件的真实时序。

Logtail与日志服务(关键词查询功能)相结合来解决这个问题:

一个容器文件的日志在采集上传时,其数据包是由一批的多条日志组成,多条日志对应特定文件的一个block,好比512KB。在这一个数据包的多条日志是按照源文件的日志序排列,也就意味着某日志的下一条多是在同一个数据包里也可能在下一个数据包里。

Logtail在采集时会给这个数据包设置惟一的日志来源sourceId,并在上传的数据包里设置包自增Id,叫作packageID。每一个package内,任意一条日志拥有包内的位移offset。

虽然数据包在服务端后存储多是无序状态,但日志服务有索引能够去精确seek指定sourceId和packageId的数据包。

当咱们指定容器A的序号2日志(source_id:A,package_id:N,offset:M)查看其下文时,先判断日志在当前数据包的offset是否为数据包的末尾(包的日志条数定义为L,末尾的offset为L-1):若是offset M小于(L-1),则说明它的下一条日志位置是:source_id:A,package_id:N,offset:M+1;而若是当前日志是数据包的最后一条,则其下一条日志的位置是:source_id:A,package_id:N+1,offset:0。

图片描述

在大部分场景下,利用关键词随机查询获取到的一个package,能够支持当前包长度L次数的上下文翻页,在提高查询性能同时也大大下降的后台服务随机IO的次数。

原文连接

相关文章
相关标签/搜索