作为一名运维开发工程师,从17年开始就基于ELK打造我们的日志系统,去年随着部门业务的扩展,我们将使用了多年的ELK 6升级到了ELK 8, 并大规模扩容了我们的集群。另一方面作为一名大数据运维开发,我们也在构建基于Clickhouse的实时数仓,在加强我们CK的开发和运维能力的同时,我们关注到了很多国内的互联网大厂都提到了将基于ELK的日志系统迁移到Clickhouse,今年初开始我们也进行这方面的探索。
互联网大厂的相关方案
为什么要从ELK 转向Clickhouse, 各个互联网大厂的文章都有阐述,都是大同小异,ES自身在集群到达一定的规模后开始遇到瓶颈,Clickhouse自身优秀的数据压缩和查询性能。 大家在打造ELK日志体系的时候,架构基本都比较类似, 但从ELK体系迁移到Clickhouse, 不仅仅是将日志存储从ELasticSearch替换到Clickhouse,更是需要重新打造围绕ES的Logstash,Kibana等的整个日志平台,各个互联网大厂的平替方案更是百花齐放,这里我们收集了一下网上的公开方案:
携程作为ES方面的重度用户,我曾经看过多篇关于ES的技术分享,从ES到CK,我最早关注到也是携程的这篇 《携程日志系统治理演进之路》https://zhuanlan.zhihu.com/p/598729102 携程这边仍然是第一步将数据写入Kafka,然后基于携程自己开源的GoHangout(类Logstash)将数据写入CK。
B站的 《B站基于Clickhouse的下一代日志体系建设实践》 https://zhuanlan.zhihu.com/p/565030219, B站的方案整体来说还是比较复杂,自研的东西非常多,我们能够直接借鉴的东西不多,B站的方案中提到了OpenTelemetry Logging ,这个是我们值得关注的一个方向,未来进一步规范我们的日志输出。
唯品会的 《相比ES,ClickHouse简直不要太香!》 https://blog.csdn.net/qq_42046105/article/details/124957826, 这篇文章值得我们参考的东西很多, 唯品会也是将数据先写入Kafka,用Flink从Kafka写到CK, 在数据存储方面,使用ssd_to_hdd,实现冷热数据的分
《使用 ClickHouse 构建通用日志系统》 https://zhuanlan.zhihu.com/p/554103626 这篇文章覆盖的面很广,也值得反复去看看。
石墨文档的 《石墨文档日志架构》http://clickvisual.gocn.vip/clickvisual/05arch/graphite-document-logging-architecture.html#_1-%E8%83%8C%E6%99%AF 石墨文档不仅介绍了他们的日志架构,同时还开源了他们的ClickVisual的日志查询平台,我们最后落地的方案也最类似石墨的架构。
我们按之前ELK方案的Filebeat ->Logstash -> ElasticSearch - > Kibana ,一步步来拆分各个大厂提供的的替代方案:
Filebeat:应用日志收集到Kafka端,这一段每家的方案不尽相同,但是基本仍然可以延续使用之前ELK的那一套。
Logstash:从Kafka写入日志到CK这一部分,这里就是百花齐放,携程采用的GoHangout, B站的Log-Ingester,唯品会采用Flink, 石墨文档的CK Kafka Engine。
Elasticsearch: 对应CK中的建表,存储等,各个文章也提出了不同的方式和优化方案,一开始看的是云里雾里,这个只有逐步进行实战了之后再进行反复优化。
Kibana: ELK 体系中的Kibana非常好用的,尤其是对非开发的业务人员也非常友好,各个大厂也都是以Kibana作为范本进行自研的替代方案,这也是我们这样的小团队从ES转换到CK的最大难点。现在唯一看到的开源的是石墨文档的ClickVisual, 我们打算下一步就开始试用。
小数据团队的从ES到CK演进
因为缺乏比较好的Kibana平替产品,我们团队并没有以数据查询作为切入点,而是先开发基于日志的告警,这样直接在Grafana写SQL就可以了,并不需要将CK暴露给开发和业务同学用于日志查询。我们来逐步拆解一下我们自己的演进方案:
Filebeat:和前面提到的各个互联网大厂一样,我们仍然是首先将日志输出到Kafka,这一块我们仍然维持之前试用的Filebeat。
Logstash + Elasticsearch :从Kafka写入日志到CK这一部分,最初我们采用了和唯品会类似的方案,通过Flink写入CK,但是我们因为我们在Flink这方面的开发资源比较有限,Flink开发和运维都还处于探索期,也遇到了一些问题,最终我们决定将有限的Flink资源投入到更加复杂的实时数仓的的数据处理任务。针对通用日志的数据同步,因为不需要做数据解析,我们希望实现的是基于配置实现,通用和稳定的数据同步方案。经过初步调研和测试后,现阶段我们采用和石墨文档相同的的基于CK Kafka Engine的方案:
首先在CK中建一个Kafka Engine 表
CREATE TABLE LOG.log_queue(`log` String)ENGINE = KafkaSETTINGS kafka_broker_list = 'kafka1:9092'kafka_topic_list = 'app, wechat',kafka_group_name = 'ck-log',kafka_format = 'JSONAsString';
再建一个MergeTree表用来落地日志数据
CREATE TABLE LOG.rawlog(`tags` Array(String),`message` String CODEC(ZSTD(1)),`hostname` String,`logfile_path` String,`log_time` DateTime DEFAULT now(),INDEX message message TYPE tokenbf_v1(30720, 2, 0) GRANULARITY 1)ENGINE = MergeTreePARTITION BY (toDate(log_time), tags)ORDER BY (tags, log_time)TTL log_time + toIntervalDay(30)SETTINGS index_granularity = 8192;
CK的建表我们吸取了大家分享的一些经验,使用ZSTD压缩数据,tokenbf_v1优化message的查询,最后通过物化视图来将Kafka表中的数据落地:
CREATE MATERIALIZED VIEW LOG.mv_rawlog TO LOG.rawlog(`tags` Array(String),`message` String,`hostname` String,`logfile_path` String,`log_time` DateTime) ASSELECTJSONExtractString(log, 'tags') AS tags,JSONExtractString(log, 'message') AS message,JSONExtractString(JSONExtractString(log, 'host'), 'name') AS hostname,JSONExtractString(JSONExtractString(JSONExtractString(log, 'log'), 'file'), 'path') AS logfile_path,now() AS log_timeFROM LOG.log_queue
针对个别需要特殊解析的日志,我们直接采用了CK物化引擎进行处理,充分利用CK强大的函数以及它对json解析特别友好的特性。
小结
现阶段我们在生产环境实现了ELK和CK的双跑,现在CK的日志系统还是局限在日志的监控和告警,下一步我们准备开始试用ClickVisual,将CK日志平台投入到搜索场景进行实战和优化。




