日志体系的布景

  • 日志是线上定位问题排障的重要东西之一,关于可观测领域而言是不可或缺的。在日志体系中,稳定性、成本、易用性以及可扩展性都极为重要。
  • 一起ELK体系是业界最常用的日志技能栈之一,它选用JSON格局作为传输方法,易于多种语言实现和解析,并支撑动态结构化字段。ElasticSearch作为存储引擎支撑全文检索,能够从许多的日志信息中快速查找到关键字。Kibana则供给漂亮易用的数据展现。
  • 根据以上两点,运用 ELK 搭建日志体系对错常惯例的技能选型。酷家乐自2015年搭建根据ES的ELK日志体系,已经运用了7年多,集群规划为几十台物理机(标准50核256GB),每天记录超越数百亿条日志。
  • 随着业务体系的高速开展,咱们的日志体系规划也在快速扩展。可是,咱们也遇到了一些问题。为了处理这些问题,咱们不得不向下一代日志体系跨进。

ClickHouse在酷家乐日志监控系统中的实践

2020处理ES写入不平等问题

遇到的痛点

  • 索引办理压力:前期,咱们根据运用 cmdb 生成 索引称号,每个运用独占一个索引。大约 500 个索引关于ES master 节点有较大的办理压力,在机器关机毛病或机器搬家后数据康复都极为绵长。
  • 数据写入不均衡:由于新建索引并不知每个索引终究会多大,因而无法确认shard 数量。索引 shard 压力分配不平衡,导致写入QPS 不能最大化,而且导致磁盘占用不均衡。

规划:监控日志ES集群选用k8s宿主机独占形式布置。规划在20台宿主机左右,每台宿主机共2个节点,1个hot、1个warm。其间 hot 节点承当数据写入和热数据查询,配备SSD磁盘。warm 节点存储读写不频频的冷数据,配备高容量HDD磁盘。

处理方法

ClickHouse在酷家乐日志监控系统中的实践
合并索引:ES 官方引荐,一个 shard 大小在 30GB 左右,根据ES 集群节点数,能够算出一个索引的最佳容量600GB。于是将多个运用日志写入一个索引,终究索引数量削减到约13个;

守时创立:每晚8点,取得前一天各个服务对应的 doc 数量,并核算出 doc 平均大小,从而推断出第二日需求的索引个数,并在 hot 节点上当即创立:

ClickHouse在酷家乐日志监控系统中的实践

别号映射:选用背包算法,经过读/写别号的方法尽或许将服务按前一日大小均匀分配到新创立的索引上;

ClickHouse在酷家乐日志监控系统中的实践

# 创立读写别号
def create_aliases(aliases, yesterday, tomorrow):
    for item in aliases.items():
        actions = []
        for alias in item[1]:
            actions.append({
                "add": {
                    "index": item[0],
                    "alias": alias.replace(yesterday, tomorrow),
                    "filter": {
                        "term": {
                            "aliasName": alias.replace(yesterday, tomorrow)
                        }
                    }
                }
            })
        payload = {"actions": actions}
        print(payload)
        r = requests.post('http://%s/_aliases' % ES_ADDR, json=payload)
        print('%d alias are created, return code %d' % (len(actions), r.status_code))

守时归档:每晚9点,将前一日在hot节点的索引迁移到warm节点,减小hot节点空间占用,并删去warm节点指守时刻之前的数据,以清除不再读取的前史数据;

# 挑选 warm 节点进行数据归档
"index.routing.allocation.require.temperature": "warm",
"index.routing.allocation.total_shards_per_node": 4

处理后作用

经过别号办理多个运用到一个索引的读写关系:

ClickHouse在酷家乐日志监控系统中的实践

下午高峰期单节点根本稳定在 QPS=36K 的写入:

ClickHouse在酷家乐日志监控系统中的实践

随着业务开展遇到的问题

  • 写入推迟:日志的推迟会对排障产生极大负面影响。作为一种运用产生的实时数据,随着业务运用规划开展而紧跟着扩大,日志体系有必要在具有高吞吐量的一起,也要具有较高的实时性要求。Elasticsearch由于分词等特性,写入受限时,会产生断路器 429 错误,经过多轮调优后,面临这样的反常,除了扩容没有有效手法能处理。
  • 存储成本:由于ES紧缩率不高,长时刻存储日志,也会导致磁盘压力。由于这些要素,咱们不得不削减日志的存储时长,这些要素也约束了排障的场景。

根据以上问题,一起考虑到成本要素,观测团队着手调研新的存储计划。ClickHouse 从18年后在国内得到广泛运用,其强悍的写入功用得到共同好评,而日志的运用场景恰好是读多写少,因而 ClickHouse 是咱们的重点调研目标。

clickhouse

ClickHouse是一个用于OLAP的列式数据库办理体系,关键特征:

  • 大多数是读恳求 、数据总是以适当大的批(> 1000 rows)进行写入 ;
  • 不修正已增加的数据 、每次查询都从数据库中读取许多的行,可是一起又仅需求少量的列 ;
  • 宽表,即每个表包含着许多的列 ;
  • 较少的查询(通常每台服务器每秒数百个查询或更少);
  • 关于简略查询,答应推迟大约50毫秒 、列中的数据相对较小: 数字和短字符串(例如,每个URL 60个字节) 、处理单个查询时需求高吞吐量(每个服务器每秒高达数十亿行) ;
  • 业务不是有必要的 、对数据共同性要求低 、每一个查询除了一个大表外都很小、查询成果显着小于源数据(数据被过滤或聚合后能够被盛放在单台服务器的内存中 )。

ClickHouse优势

  • 合适在线查询:意味着在没有对数据做任何预处理的情况下以极低的推迟处理查询并将成果加载到用户的页面中。

  • 支撑近似核算:ClickHouse供给各种各样在答应献身数据精度的情况下对查询进行加速的方法:

    1. 用于近似核算的各类聚合函数,如:distinct 、quantiles;
    2. 根据数据的部分样本进行近似查询时,仅会从磁盘检索少部分份额的数据;
    3. 不运用悉数的聚合条件,经过随机挑选有限个数据聚合条件进行聚合。这在数据聚合条件满意某些散布条件下,在供给适当精确的聚合成果的一起降低了核算资源的运用。
  • 支撑数据复制和数据完好性:ClickHouse运用异步的多主复制技能。当数据被写入任何一个可用副本后,体系会在后台将数据分发给其他副本,以确保体系在不同副本上保持相同的数据。在大多数情况下ClickHouse能在毛病后主动康复,在一些少数的复杂情况下需求手动康复。

ClickHouse缺陷

  • 没有完好的业务支撑。
  • 短少高频率,低推迟的修正或删去已存在数据的才能,仅能用于批量删去或修正数据。
  • 根据前面两点,clickhouse 运用于业务后台存储是较为困难的。但在监控或大数据场景就十分合适。

Druid

Druid 具有完善的生态,供给了许多十分便利的数据摄入功用,办理UI也比较全,在监控体系中,咱们保护了数千核规划的Druid集群;

  • 但它的组件构成十分复杂:这也是咱们保护的痛点,节点类型有6种(Overload, Coordinator, Middle Manager, Indexer, Broker和Historical);
  • 除了本身的节点,Druid还依赖于MySQL存储元数据信息、Zookeeper 推举 Coordinator和Overlord、COS备份前史数据。 ClickHouse的架构选用了对等节点的设计,节点只有一种类型,没有主从节点。假如运用了副本功用,则依赖于Zookeeper保存数据段的同步进度;
  • 占用资源高:现在调用链数据存储50%采样,依旧占用了 4000多GB内存;
  • 由于是聚合存储,特别合适存储点击流的数据。

ElasticSearch

ElasticSearch 是一个实时的散布式查找分析引擎,它的底层构建在 Lucene 之上的。简略来说是经过扩展 Lucene 的查找才能,最大的优势在于全文索引。

  • 横向扩展性:只需求增加一台服务器,做一点装备,启动一下ES进程就能够并入集群;
  • 分片机制供给更好的散布性:同一个索引分成多个分片(sharding),分而治之的方法来供给处理功率;
  • 高可用:供给复制(replica),一个分片能够设置多个复制分片,使得某台服务器宕机的情况下,集群仍旧能够照常运转;
  • 速度快,负载才能强,在面临海量数据时分,查找速度极快;
  • 各节点数据的共同性问题:其默许的机制是经过多播机制,同步元数据信息,可是在比较繁忙的集群中,或许会由于网络的阻塞,或者节点处理才能达到饱满,导致各数据节点数据不共同——也就是所谓的脑裂问题,这样会使得集群处于不共同状况。现在并没有一个完全的计划来处理这个问题,可是能够经过参数装备和节点人物装备来缓解这种情况。没有详尽的权限办理,也就是说,没有像mysql那样的分各种用户,每个用户又有不同的权限。所以在操作上的约束需求自己开发一个体系化来完结;
  • ES 对机器功用共同性要求很高,简单呈现部分节点功用欠安导致的集群功用长尾问题。由于前史原因,监控组用的机器有许多是3年前的机器,CPU 内存 标准都不能确保完全共同,在组建大规划(hot+warm)的集群时常常会由于单个节点功用稍低导致集群全体写入才能下降。特别是在流量高峰期时,当呈现节点节点负载过高时即会被熔断器除掉集群,进一步导致写入压力增大。

Clickhouse 在监控体系中的日志存储计划

日志存储ClickHouse 的灰度架构

选用可灰度的方法,近半年内只对量极大的单个服务写 ClickHouse(写这篇文章时已全量写 ClickHouse)

ClickHouse在酷家乐日志监控系统中的实践

  1. datahub 是一个可装备的元数据办理中心。供给一个接口,返回需求存 ClickHouse 的服务列表。该服务列表是动态可装备的;
  2. flink流核算根据服务列表,判别日志写入 ClickHouse,还是 ES;
  3. adhoc-apm 是一致查询层,供给日志查询API,根据 datahub 供给的服务列表,判别日志在 ClickHouse 还是 ES,查询对应的存储并返回成果。
  4. Tetris是用户运用的页面,在页面上查询日志。

CK 集群布置

clickhouse 布置架构

  1. chproxy 是ck查询署理,只有被一致查询层拜访;
  2. ck 是 clickhouse 实例节点,每个实例独享一台机器;
  3. ip尾号双数为主本,奇数为副本 (实践没有主副区分),图中 ch01 和 ch02 互为副本;
  4. 一对主副尽量用相邻的两个ip;

ClickHouse在酷家乐日志监控系统中的实践

左下角原图展现了 ch02 节点上的 三种表:

  1. 本地表下划线全小写;
  2. 合并表称号后缀为_merge,用于署理一组 契合指定表名前缀的本地表,首要便利运维,在有必要创立新表时能一起查询旧表和新表的数据;
  3. 在merge表上套一层散布式表,散布式表称号后缀为 _all ,查询恳求会随机路由到ck恣意节点,再由该节点发起到其它节点的恳求;
  4. 散布式表和本地表的区别能够看引荐:入门必读

一切实例根据k8s布置,区分独立的namespace,每个clickhouse实例独享一台宿主机。

  • 数据存储:运用宿主机磁盘,有更高效的读写功用;
  • 节点标识:由于数据存储在宿主机磁盘,因而 pod 运用 deployment 无状况形式布置。一起 macros.xml 需求在宿主机创立,实例启动时挂载到 pod 装备文件夹中以标识当时节点身份。

部分 configmap

ClickHouse在酷家乐日志监控系统中的实践

在 k8s 上运转的实例(局部)

ClickHouse在酷家乐日志监控系统中的实践

经过 tabix 检查表

ClickHouse在酷家乐日志监控系统中的实践

数据摄入

Flink 惯例 ETL 处理,由于写散布式表或经过 chproxy 都会有巨大的网络资源消耗,因而直接写入 CK 本地表;

CK 官方引荐的 sink:github.com/ivi-ru/flin…

根据此Sink做了几点改善:

  1. 写ck节点时假如产生网络反常以及节点呼应超时,则会将该ck节点符号为不可写,在一守时刻内不再向该节点发送恳求,超越设守时刻后主动撤销符号;
  2. 运用 apache httpClient 替代 netty Async httpClient,防止堆外内存堆积的问题;一起为了防止Sink线程崩溃后卡死;
  3. INSERT 数据时,从 VALUES 改为 FORMAT 形式,处理字符串转义问题和防止构造复杂的map字段(VALUE 形式: insert into table values (),(),() ; FORMAT 形式:支撑运用紧缩后的JSON批量提交数)
  4. 再可容忍 cpu 开支的条件下,运用 gzip 紧缩后传输,削减机器的网卡带宽占用;

建表句子

本地表:

-- show create table qunhe_log; 
CREATE TABLE monitor.qunhe_log
(
    `timestamp` DateTime64(3, 'Asia/Shanghai'),
    `hostGroup` String,
    `ip` String,
    `podname` String,
    `level` String,
    `cls` String,
    `behavior` String,
    `message` String,
    `id` String DEFAULT '',
    INDEX message_index message TYPE tokenbf_v1(30720, 3, 0) GRANULARITY 1
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/qunhelog', '{replica}')
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (level, hostGroup, podname, ip, timestamp)
SETTINGS storage_policy = 'hdd0_hdd1_only', index_granularity = 8192
  • 按天分区,开发独立守时使命删去过期分区,能够更清晰的掌控数据从磁盘清除的时刻;
  • 排序键首要按照基数排序,hostGroup 在查询时是一定会作为条件的。尽管 timestamp 也会作为条件,可是排在前面会显着影响数据挑选功率;
  • 分配HDD磁盘,SDD 磁盘留给目标类查询比较多的表;
  • 增加tokenbf_v1分词索引,根据测验在过滤清晰关键字是,能提高数据挑选功率80%以上;

merge表:

CREATE TABLE monitor.qunhe_log_merge ON CLUSTER clicks_cluster AS monitor.qunhe_log ENGINE=Merge("monitor", '^qunhe_log($|(.*\d$))')
  • merge表用于署理本地一组表,特别合适创立新表,并对旧表重命名后,需求新旧表一起查询;
  • 这里 ‘^qunhe_log(∣(.∗d|(.*\\d))’ 匹配的表名比方: qunhe_log、qunhe_log_20230101、qunhe_log_20230102;

散布式表:

CREATE TABLE IF NOT EXISTS monitor.qunhe_log_all
    ON CLUSTER clicks_cluster
AS monitor.qunhe_log_merge
    ENGINE = Distributed(clicks_cluster, monitor, qunhe_log_merge)
  • 散布式表首要用于查询数据。众所周知大批量写入时肯定不会挑选写散布式表,而是直接写各个节点本地表。

日志查询

一致查询层对查询封装后,根据此能够便利地在其它产品也做二次开发,包括日志查询的产品页面: 产品页首要分为三个模块:

  • 查询区;
  • 原文展现区;
  • 计算区;

检索功用:

ClickHouse在酷家乐日志监控系统中的实践

ClickHouse在酷家乐日志监控系统中的实践

计算功用:

数量计算:

ClickHouse在酷家乐日志监控系统中的实践

多类型分组计算:

ClickHouse在酷家乐日志监控系统中的实践

功用和监控

日志查询相关监控看板

最近一周日志量计算以及最近1小时各个服务日志量TopN排名:

ClickHouse在酷家乐日志监控系统中的实践

查询功率监控

ClickHouse在酷家乐日志监控系统中的实践

ClickHouse 监控

根据 clickhous_exporter 和宿主机 node_exporter 对ck 的功用和资源监控

资源运用量

ClickHouse在酷家乐日志监控系统中的实践

ClickHouse在酷家乐日志监控系统中的实践

表动态监控

ClickHouse在酷家乐日志监控系统中的实践

集群查询监控

ClickHouse在酷家乐日志监控系统中的实践

日志存储改造完结后,日志存储的机器规划削减一半以上。在写入方面,未呈现由于功用导致的写入推迟。在查询方面,99%的查询能在2s内返回,90% 的查询能在 1s 内返回,惯例查询显着快于ES。当然了,某些服务日志量的确很大,比方网关这类日志,真实无法经过索引提前挑选掉大多数数据就只能硬查了(加载到内存耗时较大)。假如增加过滤条件,命中分词索引也有极大的提升;

总的来说,比较于 Elasticsearch 来说,ClickHouse 的运维更加简略和清晰,尽管许多操作只能经过一步步命令操作完结,可是关于开发人员而言更易能掌控细节。首要有以下几个方面:

  1. 日志的接入和查询优化:接入很简单,适配表结构即可写入。配合 EXPLAIN 也很便利对查询优化;
  2. 日志生命周期办理:咱们独立开发了 ClickLive 完结对 日志分区的删去。比较 TTL 删去,守时删去具有时刻确认性。
  3. 接入监控体系:运用 clickhouse_exporter 、node_exporter 暴露目标,经过prometheus 接入 thanos 目标体系,完好实现了全拜访目标监控以及警报装备;

常见问题

zk 元信息丢失,导致表进入ReadOnly形式


-- 检查表结构
SHOW CREATE TABLE monitor.qunhe_log
-- 重命名
RENAME TABLE monitor.qunhe_log TO monitor.qunhe_log_old ON CLUSTER clicks_cluster
-- 创立新表,换一个zk途径
CREATE TABLE monitor.qunhe_log ON CLUSTER clicks_cluster . . .
-- 此刻数据正常写入,下面开端数据康复
-- 导入旧表分区到新表
-- 查询旧表分区,取得分区称号
SELECT table, partition  FROM system.parts  WHERE database = 'monitor' and table='qunhe_log'  GROUP BY (table,partition) ORDER BY (table,partition) ASC
-- 导入分区数据,分区逐一导入。非组合分区格局: '20230112'
ALTER TABLE monitor.qunhe_log ON CLUSTER clicks_cluster ATTACH PARTITION (20230112,'2') FROM monitor.qunhe_log_old
-- 到此应该能处理问题

重启实例后,表进入 ReadOnly形式

假如只是修正装备导致实例进入ReadOnly形式,大概率不需求做任何处理,会主动康复;

写入数据时报 Too many parts 反常

  1. 这个问题根本就是分区不合理导致的,一般检查下是不是运用了时刻戳分区和是不是有用其它字段做分区。咱们遇到的过将日志等级做分区,可是flink将日志等级解析反常,解析出了数千个值。
  2. 也有或许 写入批次中的数据条数太少,咱们日志写入场景中设置的 50k条/批;

呈现许多损坏数据,进入detach目录

咱们最开端发现某一个节点呈现磁盘运用率反常,才发现许多数据进入detach 目录。在双副本形式下,一般查询数据还是数据完好的。

有或许是磁盘有损坏,一般换完磁盘能够处理。能够用smartctl 东西检测一下:

  1. 磁盘短时刻测验:sudo smartctl -t short /dev/sdc1
  2. 检查测验进度:sudo smartctl -c /dev/sdc1
  3. 检查测验成果:sudo smartctl -l selftest /dev/sdc1

换新磁盘后,zk 会主动下发使命开端拉数据,此刻一般带宽或磁盘io会跑满,最好在业务流量低峰期做;