1. 日志系统

和监控系统类似,日志系统从架构上也分为采集(Logstash),储存(ES),展示(Kibana)和报警(Alert)这几个部分。

1.1. 采集

采集上分为主动推送和被动采集两种方式

  • 主动推送:我们最常见的应该就是Linux的syslog服务了,log进程(syslog,rsyslog,jounal)通过syslog协议把本机产生的日志推送到syslog服务器。既然有syslog协议,那么也就有其他的协议,比如http协议或者tcp协议,这些方式基本都是由程序自己来实现,比如经典的ELK,promtail+loki。
  • 被动采集:这里的被动采集并不是单纯的被动采集,而是有一个中间层,比如:程序主动把日志写入kafka,然后fluentd去kafka拿日志,最后写入elasticsearch。

1.2. 储存

目前最流行的免费日志解决方案基本都是以ElasticSearch为底层存储来实现的。截止到这篇文章的成文时间(2022年7月26日),Elastic公司已经把ElasticSearch的版本推进到了8版本(lucene 9.0)。而目前市面上常见的系统,基本还都是以7版本为主。

我们可以简单的把ES看做数据库,那么增删改查就成为了ES的主要功能。

1.3. 展示

不同的解决方案展示的工具不尽相同,但是也有一些兼容性比较好的工具,比如Grafana,可以兼容常见工具,用来做统一展示是非常好的选择。如果想要更深层次的功能,比如数据分析,可能就要借助其他工具了,比如kibana。

但是,kibana上的这类功能,比如机器学习之类,都是需要购买商业版才能有的,这就大大限制了我们的发挥空间。

1.4. 报警

和展示功能一样,kibana上的报警功能同样需要购买商业版,这就让我们非常不爽。我们急需一种免费又好用的解决方案。

2. 常见的日志解决方案

2.1. ELK/EFK

这种是我们最常见到的解决方案,ELK是指ElasticSearch,Logstash和Kibana这三款由elastic公司提供的软件。ElasticSearch作为存储,Logstash作为日志收集工具,Kibana用来展示和报警。

Logstash被人诟病的地方在于他的运行机制是非常小众的Jruby,也就是开发语言是ruby,但是运行在JVM虚拟机上,这就导致了他运行起来非常笨重。特别是在云原生环境环境中,作为sidecar来运行的时候,只为了采集某一个小程序的日志,就需要运行一个有可能比程序本身还消耗资源的sidecar显然是不太合适的。

针对这种情况,我们开始寻求新的解决方案,那就是EFK,使用Fluentd来替代Logstash作为新的采集工具。Fluentd运行起来消耗的资源只有logstash百分之一甚至更少,这就使得他在容器环境中更加的便捷。

但是,fluentd并没有就此止步,他后来又推出了新的日志收集工具fluentbit,进一步缩小了日志收集工具的运行所需资源,变成了目前最为流行的fluentbit–>fluentd–>elasticsearch–>kibana。

2.2. Opensearch

ElasticSearch的免费版有两种,一个叫开源版,代码是公开的,一个叫社区版,免费试用。这就导致了很多商业软件和公有云厂商都来薅他的羊毛,特别是近年来的公有云厂商尤为疯狂。公有云厂商有完善的开发团队,通过对免费代码二次封装,很容易就包装成了自己的ES商业版并进行售卖。另外一个决定性因素就是ElasticSearch的开源版是基于apache协议的,他是完全免费使用并且针对二次封装等行为也不会收费。Elastic公司在2021年把矛头指向AWS,指责他剽窃ElasticSearch,并且在ES的7.10.3版本正式修改apache协议为SSPL协议,主要是针对公有云厂商,如果在公有云上使用开源版ES也同样需要付费给Elastic公司。

但是这种程度的行为并没有让AWS妥协,而是激发了新的产品,也就是OpenSearch。OpenSearch是基于ES的7.10.2版,也就是最后一个apache协议的版本独立了一个分支出来的。而AWS很早就有一套针对ES收费版功能的对策,那就是Opendistro。这套插件可以完全适配ES开源版并且经过了非常多的实践验证,这一次,aws把opendistro嵌入es7.10.2版本中,形成了一个新的es分支,那就是OpenSearch。而Kibana被branch之后叫做OpenSearch dashboard。

这套方案目前已经被AWS应用在了公有云上,在创建ES实例的时候可以选择ES或者OpenSearch了。也就是说这套方案已经相对成熟了,我们也可以看到虽然OpenSearch是完全免费的,但是他的更新速度和商业软件比,一点也不慢,从前阵子的Log4j的安全漏洞的例子来看,基本就是0day。而且目前也升级到了2.0版本,算是发展非常迅猛的一套产品了。

2.3. Loki

Loki是grafana lab的产品,基本上是Promtail–>Loki–>Grafana的架构来做的,同样是免费的解决方案。这套方案相比于上面两种就轻量多了,因为ES和OpenSearch主要是搜索引擎,日志搜索不过是他众多功能之一,而Loki本来就是为了存储日志而生,他没有使用lucene那种搜索引擎,所以Loki是没有索引概念的,他借鉴了prometheus的存储方式,使用类似时序数据库的方式来存储日志,这就让Loki本身的写日志非常快,而查询的话,相对ES来说还是慢了许多,更别提日志的分析功能了,所以这个方案本身就是为了方便日志的存储。

2.4. 其他开源解决方案

依然建议大家去CNCF的网站上去看一下image-20220727212108528

里面同样有很多不错的开源解决方案,比如graylog,sumo logic,这就需要我们大家去深挖了。

3. 日志采集

日志收集从日志系统搭建的角度是第二步,在日志收集的过程中是第一步。日志需要被发送或者采集到统一的位置才可能被利用起来。日志统一发送的目的有两个,可能是直接发送到了存储,比如:logstash直接发送到ES。也有可能是发送到了代理节点,然后在代理节点做处理之后才向存储发送,比如:filebeat发送日志到logstash,经过整理之后发送给ES。

那么,问题来了。

  1. 如果日志是主动发送的,怎样保证日志在发送的过程中是不丢数据的?我们知道,rsyslog或者是syslog协议是UDP的,也就是说,如果发送数据,发送端只管发,而不管服务端时候能接收到。所以这种方式不太适合比较重要的日志,比如应用日志,HTTP请求等等,我们会用另外一种方式来处理这些日志。这种方式比较适合那种非常大量的,而且无法安装agent的场景,比如:交换机,路由器日志。
  2. 如果日志是通过log agent从设备上抽取的(俗称叫挖日志),那么在抽取的过程中,需要指定日志的位置,在读取的过程中需要设置内存的大小和一次性发送的数量,而且在发送的过程中如果有time out,或者404之类的情况,可以选择把没有发送的日志存起来,或者记录下发送日志的位置,下次再重新发送。
  3. 从服务端的角度,如果某一个日志接收器(比如ES或者collector挂了),agent会重新向另外的日志接收器发送日志,直到成功,指针才会转移向下一段日志。而日志接收器之间也需要一些同步来保证数据的一致性和单一性,不会出现丢日志或者日志重复的情况。比如ES或者kafka这种本身有集群机制的接收器可能好理解,如果我们的接收器是fluentd或者logstash这种的中转站应该如何处理呢?
  4. 日志是有格式的,syslog有syslog的格式,比如:syslog-rfc5424,syslog-rfc3164,nginx有nginx的格式,如果是自定的log,就需要自定义一个日志格式,自定义日志格式就需要使用正则表达式来把一段string类型的日志变成对应的格式。比如: ^\<(?<pri>[0-9]{1,5})\>1 (?<time>[^ ]+) (?<host>[^ ]+) (?<ident>[^ ]+) (?<pid>[-0-9]+) (?<msgid>[^ ]+) (?<extradata>(\[(.*)\]|-)) (?<message>.+)$

我们带着上面的思考去重新审视我们后面要做的事情,就可以清晰的理解到一些日志系统的真谛,从而升华我们在应对日志系统架构问题时候的境界。也许目前我们的眼光并没有这么犀利和透彻,所以我建议大家在学完之后再回头来看一下这些问题,他们会全部迎刃而解。

3.1. 常见的agent

  • fluentd:我们这套课程的核心其实是fluentd,因为我们讲的是云原生,而fluentd是CNCF早期的毕业项目,毕业时间是2019年4月,毕业时间距离kubernetes和prometheus的毕业时间只是滞后了一点点。我们后面会对他做非常详细的讲解。

  • fluent-bit:这款产品和fluentd的母公司Treasure Data的另外一款产品,尽管没有托管在cncf,但是他依然是完全免费开源使用的,在资源消耗上比fluentd要小很多,但是插件没有fluentd丰富,用来做一些简单的日志收集是非常适合的。我们后面也会着重讲解。

  • logstash:这是非常经典的ELK架构中的L,logstash依托于ES的烘托,也算是个不错的选择,但是由于资源消耗问题一直被社区诟病,因为他的基于JVM开发的,使用Jruby语言构建的软件,java的效率大家有目共睹,再加上JRUBY这种反人类的架构,让原来的ELK架构演变为EFK架构,大家纷纷使用Fluentd来取代Logstash来收集日志。

  • Filebeats和各种beat:ES的公司把ES的生态包括ES都统称为elastic stack,而他们的agent,除了logstash,都统称为xxxbeat。我们前面讲ES产品的时候说过了,比如:filebeat,windlogbeat。而logstash被定义为采集、转换、充实,然后输出。也就是对日志做处理的工具。

  • Promtail:grafana lab的产品,主要是和日志工具Loki所对应的一款日志收集工具。他的功能只限于日志收集,他所能兼容的input和output的产品类型非常有限,但是和Loki的兼容性是非常好的,也是目前唯一一个和Loki兼容性好的工具。官方文档中提到了fluentd也可以通过加载动态链接库.so的方式来支持Loki作为output,但是咱们的课程里面不会涉及,有兴趣的可以自己试一下。

  • flume:大数据项目中常用的一种方式,他的优点在于分布式。因为他可以依托于zookeeper来做集群,所以他可以近乎无限的扩展节点,且稳定性很高。但是基本上只有一些涉及到HDFS或者HBase的项目会用到这个工具。咱们课程中不会涉及到他。

  • 其他产品:其实还有很多商用的产品,比如splunk,sysdig还有IBM的logdns都有自己的解决方案,而且适配性也很好,但是缺点都是一样的,那就是收费。但是,如果没有太大的数据量,但是对于日志有高要求的公司,使用产品也是不错的选择。

4. 日志储存

4.1. 搜索引擎类

这一类以ES,OpenSearch或者Solr为代表,他们主打的功能实际上搜索,利用了lucene或者其他引擎的快速检索功能,来实现日志的快速检索,而且其他的功能,比如报警,展示之类的,不过是一些水到渠成的功能。

这一类的工具的检索速度飞快,但是对于大规模的IO,比如大量的读写同时存在时会消耗大量资源,且扩展性较差。

4.2. 数据库类

有一些数据库是支持存储日志的,他们把日志当做一个条目或者一种类型来单独处理,实现了日志的存储。

但是当我们需要这些日志的时候,通常会把日志丢给其他的工具来做,比较常见的就是mongoDB,我们写的时候把所有的数据写入mongoDB,然后通过异构同步机制,把日志写到ES上,读的时候去ES读,从而实现读写分离。

4.3. 日志存储类

这些存储是专门为了存储日志而开发出来的,典型的如Loki,Graylog,sumo logic。这类工具设计的时候就是为日志而生,基本符合当前大部分的用户需求,但是缺点就是核心功能具备,而用户体验一般。毕竟和那些沉淀了几年的工具相比,还是有些年轻。

5. 日志展示/分析

5.1. 常见工具

Kibana/OpenSearch-Dashboard:常见的工具,专门为ES/OpenSearch定做。扩展性好,但是模式非常固定,不适合做那些花花绿绿,博人眼球的界面。

Grafana:集成的工具,对于日志的展示功能非常有限,仅限于展示。

其他的工具:大部分的展示工具都可以把数据存储作为一个数据源来读取,比如PowerBI或者DataEase。但是依然无法逃脱模式固定的问题,我们可以解决很多通用的需求。如果想完全定制,那还是需要自己开发。

5.2. 分析

这个领域可以衍生出非常多的需求,比如数值分析,人工智能,数据挖掘,商业智能等等。他们都是需要依托于大量的数据,特别是日志类的数据来分析用户行为的,比较典型的如头条的推荐系统,淘宝的客户行为分析都是非常典型的场景。

6. 日志报警

日志报警的需求对于SRE来说是非常重要的,我们需要知道系统到底在做什么,遇到什么情况需要告诉管理员。大部分的工具只能基于某些关键字来做报警,比如关键字出现的次数和位置。