日均百亿级:基于 ClickHouse 的 eBPF 安全日志存储与高并发检索架构演进实践
当安全审计的粒度下沉到内核级(eBPF),系统吞吐量会迎来指数级爆发。一次普通的内核态系统调用捕获(如 sys_enter_execve 或 sys_enter_connect),在百万级 QPS 的 Kubernetes 集群中,会产生每秒数十万条的结构化安全日志。
传统的 ELK (Elasticsearch) 架构在此类场景下,常因写入放大严重、全文索引开销巨大而导致机器成本失控。ClickHouse 凭借其列式存储引擎与极强的向量化执行能力,成为承载 eBPF 海量安全日志的黄金选择。但在高并发检索、实时告警与 PB 级存储的实际生产中,若没有合理的架构设计与调优,ClickHouse 极易被高频写入压垮,或在并发查询时遭遇 CPU 耗尽。
本文将分享一套经过生产验证的 基于 ClickHouse 的 eBPF 安全日志海量存储与高并发检索架构方案。
一、 系统架构蓝图
整套数据链路遵循 “轻量采集 -> 削峰聚合 -> 极速写入 -> 分层检索” 的原则。
+------------------+ +---------------+ +--------------------+
| K8s Nodes | | Kafka Cluster | | Flink / Vector |
| (Tetragon/eBPF) | --> | (Ingress Log) | --> | (Enrich & Batch) |
+------------------+ +---------------+ +--------------------+
| (Batch HTTP)
v
+--------------------+
| chproxy / LB |
+--------------------+
|
+-------------------+-------------------+
| |
v v
+-----------------+ +-----------------+
| ClickHouse Shard1| | ClickHouse Shard2|
| (MergeTree) | | (MergeTree) |
+-----------------+ +-----------------+
- 采集层 (Data Collection):使用基于 eBPF 的安全工具(如 Cilium Tetragon 或自研 eBPF Probe)监控进程行为、文件读写、网络连接等,以 JSON / Protobuf 格式输出。
- 缓冲层 (Queueing):Kafka 承载高并发写入,提供削峰填谷能力。
- 清洗与分批层 (Ingress Pipe):使用 Vector 或 Flink 消费 Kafka 数据,进行元数据补全(例如将 Pod IP 转化为 PodName、Namespace、Owner 等 K8s 标签),并保证单次写入 ClickHouse 的 Batch 大小在 10万条以上 或 时间窗口达到 5-10 秒。
- 路由与查询代理 (Proxy):使用
chproxy进行查询限流、合并和路由,保护后端 ClickHouse 集群不被高并发查询击垮。
二、 ClickHouse Schema 极致优化设计
eBPF 安全日志字段多、基数大,包含大量长字符串(如进程路径、命令行参数、K8s 标签)。如果采用默认建表配置,存储空间和检索性能将大打折扣。
1. 生产级 DDL 模板
以下是一个针对 eBPF 进程执行行为日志(Process Exec Events)深度优化后的表结构设计:
CREATE TABLE security_audit.ebpf_process_log_local ON CLUSTER cluster_3shards_2replicas
(
-- 1. 时间与基础元数据(DoubleDelta+LZ4 极度压缩时间戳)
event_time DateTime64(3, 'Asia/Shanghai') CODEC(DoubleDelta, LZ4),
event_type LowCardinality(String) CODEC(ZSTD(1)),
trace_id String CODEC(ZSTD(1)),
-- 2. 进程上下文(整型使用 T64 / DoubleDelta 压缩)
pid UInt32 CODEC(T64, ZSTD(1)),
ppid UInt32 CODEC(T64, ZSTD(1)),
tgid UInt32 CODEC(T64, ZSTD(1)),
uid UInt32 CODEC(T64, ZSTD(1)),
user_name LowCardinality(String) CODEC(ZSTD(1)),
-- 3. 进程核心载荷(高频查询列使用 LowCardinality 降低字典基数)
proc_name LowCardinality(String) CODEC(ZSTD(1)),
binary_path String CODEC(ZSTD(3)),
arguments String CODEC(ZSTD(3)),
-- 4. K8s 元数据上下文
namespace LowCardinality(String) CODEC(ZSTD(1)),
pod_name String CODEC(ZSTD(1)),
container_id FixedString(64) CODEC(ZSTD(1)),
image_name String CODEC(ZSTD(3)),
-- 5. 动态扩展属性(使用 Map 存储非常规审计元数据)
extensions Map(String, String) CODEC(ZSTD(1)),
-- 跳数索引:加速对命令行参数等大文本的模糊检索
INDEX idx_args arguments TYPE tokenbf_v1(30720, 2, 0) GRANULARITY 1
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/ebpf_process_log_local', '{replica}')
PARTITION BY toYYYYMMDD(event_time)
ORDER BY (namespace, event_type, proc_name, event_time)
SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1;
2. DDL 核心设计要点解析
LowCardinality(低基数类型):
对于namespace、event_type、proc_name等字段,其全局去重后的总数通常小于 1 万个。使用LowCardinality(String)会将其在内部转换为整数索引,不仅极大节省了存储空间,还能让WHERE过滤过滤速度提升一个数量级。- Codec 链式压缩:
- 时间戳:
event_time连续性极强,使用DoubleDelta编码后,再用LZ4压缩,几乎不占用存储。 - 数字型(PID/UID):使用
T64编码,它会剔除高位连续的 0,非常适合大整型。 - 长文本(Arguments/BinaryPath):使用
ZSTD(3)。eBPF 命令行参数重复率高且体积大,ZSTD 的高压缩比在此优势显著。
- 时间戳:
- 物理排序键 (
ORDER BY):
ClickHouse 的物理检索依赖稀疏索引。我们将最常用、基数相对较低的过滤条件放至ORDER BY最前列(如namespace、event_type)。时间戳event_time必须放在最后,否则会导致稀疏索引文件过大,失去索引过滤效果。 - 布隆过滤器跳数索引 (
tokenbf_v1):
安全分析常需要通过LIKE '%sh -i%'检索异常命令行。在arguments字段上建立tokenbf_v1(分词布隆过滤器),能直接在 Merge 阶段过滤掉 90% 不包含关键字的 Data Part,避免全表扫描。
三、 高并发检索架构破局
ClickHouse 的底层设计初衷是大宽表、低并发、大查询。如果前端控制台有 100 个安全分析师同时刷新仪表盘,或者告警引擎每秒发起数百次策略匹配检索,ClickHouse 的 CPU 会瞬间打满。
为了解决高并发查询的短板,我们必须在存储引擎之上加入查询网关与分层路由。
[ 客户端检索请求 ]
|
v
+---------------+
| chproxy 网关 |
+---------------+
/ \
(命中缓存 / 简单点查) / \ (大范围聚合/报表)
v v
+---------------+ +-------------------+
| Redis 缓存层 | | ClickHouse 副本集群 |
+---------------+ +-------------------+
1. 使用 chproxy 做并发控制与流控
chproxy 作为一个专为 ClickHouse 打造的 HTTP 代理,可以完美解决恶意/无序的高并发冲击:
# chproxy 关键配置片段
users:
- name: security_fe
max_execution_time: 10 # 限制单个查询最大执行时间 10 秒
max_concurrent_queries: 20 # 限制该用户最大并发数为 20
queue_max_wait_time: 5s # 超过并发后在队列中等待 5 秒
queries_path_regexp: "^SELECT.*" # 仅允许执行查询语句
# 开启查询缓存(Query Cache)
cache:
- name: memcached
size: 2048MB
expire: 30s # 对安全分析大屏等常见重复查询进行 30s 缓存
2. 读写分离与副本负载均衡
ClickHouse 的 Distributed 引擎在执行查询时,会自动分发到不同的 Shard 和 Replica。为避免写入操作影响查询体验,建议将读写流量彻底分离:
- 写入路径:向 Local 表直接写入,不通过 Distributed 表。Flink 采用轮询(Round-robin)方式直接向各分片的 Local 表进行 HTTP Batch 写入。
- 读取路径:通过 Distributed 表或使用 Chproxy 的
load_balancing策略,将查询分发到专门的 Read-Only 副本(读取节点不参与 MergeTree 的后台 Merge 合并计算)。
3. 物化视图(Materialized View)异步预聚合
安全仪表盘(如“当前命名空间下 Top 10 异常进程”)不需要每次都扫描明细表。通过物化视图将数据秒级预聚合,查询时只需访问聚合后的高度精简表。
-- 创建目标聚合表
CREATE TABLE security_audit.process_summary_hourly
(
event_hour DateTime,
namespace LowCardinality(String),
proc_name LowCardinality(String),
exec_count UInt64
) ENGINE = SummingMergeTree()
ORDER BY (event_hour, namespace, proc_name);
-- 创建物化视图,将明细表增量注入聚合表
CREATE MATERIALIZED VIEW security_audit.mv_process_summary_hourly
TO security_audit.process_summary_hourly AS
SELECT
toStartOfHour(event_time) AS event_hour,
namespace,
proc_name,
count() AS exec_count
FROM security_audit.ebpf_process_log_local
GROUP BY event_hour, namespace, proc_name;
通过这一层优化,大屏和报表等高频聚合查询的 QPS 能够从数十提升至数千,响应时间从秒级缩短至毫秒级。
四、 存储生命周期管理 (TTL) 与冷热分层
eBPF 安全数据具有极明显的“时效性”。通常情况下:
- 0 - 3天 的日志属于“热数据”,需要极速检索用于应急响应;
- 4 - 30天 的日志属于“温数据”,用于追踪溯源,检索频率降低;
- 31 - 180天 的日志作为“冷数据”,用于合规审计,极少查询。
冷热分层配置
ClickHouse 支持多级存储介质(SSD/HDD/对象存储)的分层转移。在 config.xml 中配置存储策略:
<storage_configuration>
<disks>
<fast_ssd>
<path>/clickhouse/data/ssd/</path>
</fast_ssd>
<cold_hdd>
<path>/clickhouse/data/hdd/</path>
</cold_hdd>
</disks>
<policies>
<tier_policy>
<volumes>
<hot_volume>
<disk>fast_ssd</disk>
<max_data_part_size_bytes>10737418240</max_data_part_size_bytes> <!-- 超过 10GB 优先存 HDD -->
</hot_volume>
<cold_volume>
<disk>cold_hdd</disk>
</cold_volume>
</volumes>
</tier_policy>
</policies>
</storage_configuration>
并在建表 DDL 的末尾加上基于时间的移动与删除策略:
-- 修改前文 DDL 的 SETTINGS 部分
SETTINGS storage_policy = 'tier_policy'
TTL event_time + INTERVAL 3 DAY TO VOLUME 'cold_volume', -- 3天后自动移动到 HDD 磁盘
event_time + INTERVAL 180 DAY DELETE; -- 180天后自动物理删除
五、 总结与生产实测表现
在某大型 Kubernetes 集群环境(约 2000 个节点,峰值安全日志写入约 350k EPS / 日均 300 亿条)中落地此架构后,取得了以下显著成果:
- 存储成本降低 75%:得益于
LowCardinality、DoubleDelta以及ZSTD的精细化配置,整表物理压缩比达到了 11.2:1。相比原有 Elasticsearch 架构,服务器节点数量从 36 台缩减至 8 台(三沙箱双副本+2代理)。 - 查询吞吐量跃升:通过
chproxy查询限流与 Redis 物化视图缓存,秒级点查(TraceID 追踪)QPS 稳定在 1200+,未发生 CPU 过载报警。 - P99 查询延迟:对 1 亿行数据进行模糊匹配(如含有
/etc/ld.so.preload的文件篡改行为),检索耗时控制在 1.2 秒 以内。
对于 eBPF 这类结构高度确定、体量极度庞大的安全审计日志,ClickHouse 配合合理的数据流设计,毫无疑问是当前性价比与技术先进性的最优解。