还在用ES查日志吗,快看看石墨文档 Clickhouse 日志架构玩法-InfoQ
source link: https://www.infoq.cn/article/u3z3dQubLIgxTKsgFCQC
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
还在用 ES 查日志吗,快看看石墨文档 Clickhouse 日志架构玩法
日志作为线上定位问题的关键手段,我们在选择日志采集、日志查询系统的时候需要考虑成本,架构,美观,易用性等问题,针对这些方面,本文由石墨文档架构师彭友顺,采用 Clickhouse 技术和石墨开源的 Mogo 日志查询系统,介绍了日志采集、日志传输、日志存储、日志管理的日志架构玩法。
石墨文档全部应用部署在Kubernetes
上,每时每刻都会有大量的日志输出,我们之前主要使用SLS
和ES
作为日志存储。但是我们在使用这些组件的时候,发现了一些问题。
成本问题:
SLS
个人觉得是一个非常优秀的产品,速度快,交互方便,但是 SLS 索引成本比较贵。我们想减少
SLS
索引成本的时候,发现云厂商并不支持分析单个索引的成本,导致我们无法知道是哪些索引构建的不够合理。ES
使用的存储非常多,并且耗费大量的内存。通用问题:
如果业务是混合云架构,或者业务形态有
SAAS
和私有化两种方式,那么SLS
并不能通用。日志和链路,需要用两套云产品,不是很方便。
精确度问题:
SLS
存储的精度只能到秒,但我们实际日志精度到毫秒,如果日志里面有traceid
,SLS
中无法通过根据traceid
信息,将日志根据毫秒时间做排序,不利于排查错误。
我们经过一番调研后,发现使用Clickhouse
能够很好的解决以上问题,并且 Clickhouse 省存储空间,非常省钱,所以我们选择了Clickhouse
方案存储日志。但当我们深入研究后,Clickhouse
作为日志存储有许多落地的细节,但业界并没有很好阐述相关Clickhouse
采集日志的整套流程,以及没有一款优秀的Clickhouse
日志查询工具帮助分析日志,为此我们写了一套Clickhouse
日志系统贡献给开源社区,并将Clickhouse
的日志采集架构的经验做了总结。先上个Clickhouse
日志查询界面,让大家感受下石墨最懂前端的后端程序员。
2 架构原理图
我们将日志系统分为四个部分:日志采集、日志传输、日志存储、日志管理。
日志采集:
LogCollector
采用Daemonset
方式部署,将宿主机日志目录挂载到LogCollector
的容器内,LogCollector
通过挂载的目录能够采集到应用日志、系统日志、K8S 审计日志等。日志传输:通过不同
Logstore
映射到Kafka
中不同的Topic
,将不同数据结构的日志做了分离。日志存储:使用
Clickhouse
中的两种引擎数据表和物化视图。日志管理:开源的
Mogo
系统,能够查询日志,设置日志索引,设置LogCollector
配置,设置Clickhouse
表,设置报警等。
以下我们按照这四大部分,阐述其中的架构原理。
3 日志采集
3.1 采集方式
Kubernetes
容器内日志收集的方式通常有以下三种方案。
DaemonSet 方式采集:在每个
node
节点上部署LogCollector
,并将宿主机的目录挂载为容器的日志目录,LogCollector
读取日志内容,采集到日志中心。网络方式采集:通过应用的日志
SDK
,直接将日志内容采集到日志中心 。SideCar 方式采集:在每个
pod
内部署LogCollector
,LogCollector
只读取这个pod
内的日志内容,采集到日志中心。
以下是三种采集方式的优缺点:
我们主要采用DaemonSet
方式和网络方式采集日志。DaemonSet
方式用于ingress
、应用日志的采集,网络方式用于大数据日志的采集(可能需要简单的说明下原因)。以下我们主要介绍下DeamonSet
方式的采集方式。
3.2 日志输出
从上面的介绍中可以看到,我们的DaemonSet
会有两种方式采集日志类型,一种是标准输出,一种是文件。引用元乙的描述:虽然使用 Stdout
打印日志是 Docker
官方推荐的方式,但大家需要注意:这个推荐是基于容器只作为简单应用的场景,实际的业务场景中我们还是建议大家尽可能使用文件的方式,主要的原因有以下几点:
Stdout
性能问题,从应用输出stdout
到服务端,中间会经过好几个流程(例如普遍使用的JSON
LogDriver
):应用stdout
->DockerEngine
->LogDriver
-> 序列化成JSON
-> 保存到文件 ->Agent
采集文件 -> 解析JSON
-> 上传服务端。整个流程相比文件的额外开销要多很多,在压测时,每秒 10 万行日志输出就会额外占用DockerEngine
1 个CPU
核;Stdout
不支持分类,即所有的输出都混在一个流中,无法像文件一样分类输出,通常一个应用中有AccessLog
、ErrorLog
、InterfaceLog
(调用外部接口的日志)、TraceLog
等,而这些日志的格式、用途不一,如果混在同一个流中将很难采集和分析;Stdout
只支持容器的主程序输出,如果是daemon/fork
方式运行的程序将无法使用stdout
;文件的
Dump
方式支持各种策略,例如同步/异步写入、缓存大小、文件轮转策略、压缩策略、清除策略等,相对更加灵活。
从这个描述中,我们可以看出在docker
中输出文件在采集到日志中心是一个更好的实践。所有日志采集工具都支持采集文件日志方式,但是我们在配置日志采集规则的时候,发现开源的一些日志采集工具,例如fluentbit
、filebeat
在DaemonSet
部署下采集文件日志是不支持追加例如pod
、namespace
、container_name
、container_id
等label
信息,并且也无法通过这些label
做些定制化的日志采集。
基于无法追加label
信息的原因,我们暂时放弃了DeamonSet
部署下文件日志采集方式,采用的是基于DeamonSet
部署下标准输出的采集方式。
3.3 日志目录
以下列举了日志目录的基本情况:
因为我们采集日志是使用的标准输出模式,所以根据上表我们的LogCollector
只需要挂载/var/log
,/var/lib/docker/containers
两个目录。
3.3.1 标准输出日志目录
应用的标准输出日志存储在/var/log/containers
目录下,文件名是按照 K8S 日志规范生成的。这里以nginx-ingress
的日志作为一个示例。我们通过ls /var/log/containers/ | grep nginx-ingress
指令,可以看到nginx-ingress
的文件名。
nginx-ingress-controller-mt2wx_kube-system_nginx-ingress-controller-be3741043eca1621ec4415fd87546b1beb29480ac74ab1cdd9f52003cf4abf0a.log
我们参照 K8S 日志的规范:/var/log/containers/%{DATA:pod_name}_%{DATA:namespace}_%{GREEDYDATA:container_name}-%{DATA:container_id}.log
。可以将nginx-ingress
日志解析为:
pod_name
:nginx-ingress-controller-mt2wnamespace
:kube-systemcontainer_name
:nginx-ingress-controllercontainer_id
:be3741043eca1621ec4415fd87546b1beb29480ac74ab1cdd9f52003cf4abf0a
通过以上的日志解析信息,我们的LogCollector
就可以很方便的追加pod
、namespace
、container_name
、container_id
的信息。
3.3.2 容器信息目录
应用的容器信息存储在/var/lib/docker/containers
目录下,目录下的每一个文件夹为容器ID
,我们可以通过cat config.v2.json
获取应用的 docker 基本信息。
3.4 LogCollector 采集日志
3.4.1 配置
我们LogCollector
采用的是fluent-bit
,该工具是cncf
旗下的,能够更好的与云原生相结合。通过Mogo
系统可以选择Kubernetes
集群,很方便的设置fluent-bit
configmap
的配置规则。
3.4.2 数据结构
fluent-bit
的默认采集数据结构。
@timestamp
字段:string or float,用于记录采集日志的时间。log
字段:string,用于记录日志的完整内容。
Clickhouse
如果使用@timestamp
的时候,因为里面有@
特殊字符,会处理的有问题。所以我们在处理fluent-bit
的采集数据结构,会做一些映射关系,并且规定双下划线为Mogo
系统日志索引,避免和业务日志的索引冲突。
_time_
字段:string or float,用于记录采集日志的时间。_log_
字段:string,用于记录日志的完整内容。
例如你的日志记录的是{"id":1}
,那么实际fluent-bit
采集的日志会是{"_time_":"2022-01-15...","_log_":"{\"id\":1}"
该日志结构会直接写入到kafka
中,Mogo
系统会根据这两个字段_time_
、_log_
设置clickhouse
中的数据表。
3.4.3 采集
如果我们要采集ingress
日志,我们需要在input
配置里,设置ingress
的日志目录,fluent-bit
会把ingress
日志采集到内存里。
然后我们在filter
配置里,将log
改写为_log_
。
然后我们在ouput
配置里,将追加的日志采集时间设置为_time_
,设置好日志写入的kafka borkers
和kafka topics
,那么fluent-bit
里内存的日志就会写入到kafka
中。
日志写入到Kafka
中_log_
需要为json
,如果你的应用写入的日志不是json
,那么你就需要根据fluent-bit
的parser
文档,调整你的日志写入的数据结构:https://docs.fluentbit.io/manual/pipeline/filters/parser
4 日志传输
Kafka
主要用于日志传输。上文说到我们使用fluent-bit
采集日志的默认数据结构,在下图kafka
工具中我们可以看到日志采集的内容。
在日志采集过程中,会由于不用业务日志字段不一致,解析方式是不一样的。所以我们在日志传输阶段,需要将不同数据结构的日志,创建不同的Clickhouse
表,映射到Kafka
不同的Topic
。这里以ingress
为例,那么我们在Clickhouse
中需要创建一个ingress_stdout_stream
的 Kafka 引擎表,然后映射到Kafka
的ingress-stdout Topic
里。
5 日志存储
我们会使用三种表,用于存储一种业务类型的日志。
Kafka
引擎表:将数据从Kafka
采集到Clickhouse
的ingress_stdout_stream
数据表中。
create table logger.ingress_stdout_stream
(
_source_ String,
_pod_name_ String,
_namespace_ String,
_node_name_ String,
_container_name_ String,
_cluster_ String,
_log_agent_ String,
_node_ip_ String,
_time_ Float64,
_log_ String
)
engine = Kafka SETTINGS kafka_broker_list = 'kafka:9092', kafka_topic_list = 'ingress-stdout', kafka_group_name = 'logger_ingress_stdout', kafka_format = 'JSONEachRow', kafka_num_consumers = 1;
物化视图:将数据从
ingress_stdout_stream
数据表读取出来,_log_
根据Mogo
配置的索引,提取字段在写入到ingress_stdout
结果表里。
CREATE MATERIALIZED VIEW logger.ingress_stdout_view TO logger.ingress_stdout AS
SELECT
toDateTime(toInt64(_time_)) AS _time_second_,
fromUnixTimestamp64Nano(toInt64(_time_*1000000000),'Asia/Shanghai') AS _time_nanosecond_,
_pod_name_,
_namespace_,
_node_name_,
_container_name_,
_cluster_,
_log_agent_,
_node_ip_,
_source_,
_log_ AS _raw_log_,JSONExtractInt(_log_, 'status') AS status,JSONExtractString(_log_, 'url') AS url
FROM logger.ingress_stdout_stream where 1=1;
结果表:存储最终的数据。
create table logger.ingress_stdout
(
_time_second_ DateTime,
_time_nanosecond_ DateTime64(9, 'Asia/Shanghai'),
_source_ String,
_cluster_ String,
_log_agent_ String,
_namespace_ String,
_node_name_ String,
_node_ip_ String,
_container_name_ String,
_pod_name_ String,
_raw_log_ String,
status Nullable(Int64),
url Nullable(String),
)
engine = MergeTree PARTITION BY toYYYYMMDD(_time_second_)
ORDER BY _time_second_
TTL toDateTime(_time_second_) + INTERVAL 7 DAY
SETTINGS index_granularity = 8192;
6 总结流程
日志会通过
fluent-bit
的规则采集到kafka
,在这里我们会将日志采集到两个字段里。_time_
字段用于存储fluent-bit
采集的时间_log_
字段用于存放原始日志通过
mogo
,在clickhouse
里设置了三个表。app_stdout_stream
: 将数据从Kafka
采集到Clickhouse
的Kafka
引擎表app_stdout_view
: 视图表用于存放mogo
设置的索引规则app_stdout
:根据app_stdout_view
索引解析规则,消费app_stdout_stream
里的数据,存放于app_stdout
结果表中最后
mogo
的UI
界面,根据app_stdout
的数据,查询日志信息。
7 Mogo 界面展示
查询日志界面:
设置日志采集配置界面:
以上文档描述是针对石墨Kubernetes
的日志采集,想了解物理机采集日志方案的,可以在下文中找到《Mogo
使用文档》的链接,运行docker-compose
体验Mogo
全部流程,查询Clickhouse
日志。限于篇幅有限,Mogo
的日志报警功能,下次讲解。
github 地址: https://github.com/shimohq/mogo
Mogo 文档:https://mogo.shimo.im
Mogo 使用文档:https://mogo.shimo.im/doc/AV62KU4AABMRQ
fluent-bit 文档:https://docs.fluentbit.io/
K8S 日志
6 个 K8S 日志系统建设中的典型问题,你遇到过几个:https://developer.aliyun.com/article/718735
一文看懂 K8S 日志系统设计和实践:https://developer.aliyun.com/article/727594
9 个技巧,解决 K8S 中的日志输出问题:https://developer.aliyun.com/article/747821
直击痛点,详解 K8S 日志采集最佳实践:https://developer.aliyun.com/article/749468?spm=a2c6h.14164896.0.0.24031164UoPfIX
Clickhouse
Clickhouse 官方文档:https://clickhouse.com/
Clickhouse 作为 Kubernetes 日志管理解决方案中的存储:http://dockone.io/article/9356
Uber 如何使用 ClickHouse 建立快速可靠且与模式无关的日志分析平台?:https://www.infoq.cn/article/l4thjgnr7hxpkgpmw6dz
干货 | 携程 ClickHouse 日志分析实践:https://mp.weixin.qq.com/s/IjOWAPOJXANRQqRAMWXmaw
为什么我们要从 ES 迁移到 ClickHouse:https://mp.weixin.qq.com/s/l4RgNQPxvdNIqx52LEgBnQ
ClickHouse 在日志存储与分析方面作为 ElasticSearch 和 MySQL 的替代方案:https://mp.weixin.qq.com/s/nJXorcgi0QfXPCKr_HdUZg
快手、携程等公司转战到 ClickHouse,ES 难道不行了?:https://mp.weixin.qq.com/s/hP0ocT-cBCeIl9n1wL_HBg
日志分析下 ES/ClickHouse/Loki 比较与思考:[https://mp.weixin.qq.com/s/n2I94X6tz2jOABzl1djxYg
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK