3. 集群架构

核心原理:从单机到集群的必然演进

在日志采集与智能分析系统中,单机架构的瓶颈非常清晰:一台服务器能承载的采集通道有限,磁盘写入速率受限于单盘 IOPS,索引与检索能力受 CPU 和内存上限约束。当业务集群每天产生数百 GB 甚至数 TB 日志时,单机方案必然出现采集延迟、日志丢失、查询超时等问题。集群架构的核心思想是“分而治之”——将采集、缓冲、索引、检索、告警等职责分散到多台节点上,通过横向扩展提升整体吞吐量与可用性。

一个典型的日志集群由三类节点组成:采集节点(Agent/Collector)、传输与缓冲节点(Broker/Queue)、存储与检索节点(Indexer/Search)。采集节点部署在业务服务器上,负责读取日志文件或接收网络日志;传输节点负责削峰填谷,防止突发流量打垮下游;存储节点负责将日志写入索引并对外提供查询能力。三者通过内部网络协议协作,形成一条完整的日志流水线。

日志集群三层架构与日志分布 采集层(Agent) 业务服务器 A app.log / nginx.log filebeat / fluentd 业务服务器 B order.log / pay.log filebeat / fluentd 业务服务器 C auth.log / audit.log filebeat / fluentd K8s 节点 1~N stdout / container fluent-bit (DaemonSet) TCP / Kafka 协议 缓冲与传输层(Broker) Kafka 集群(3 节点) topic: app-log / audit-log 分区数=6 副本=2 Redis Stream / Pulsar 可选替代方案 按需选择 Logstash / Flume 过滤、清洗、格式转换 grok 正则解析 批量写入 存储与检索层(Indexer) ES 主节点 ×3 集群状态管理 索引路由 / 分片分配 ES 数据节点 ×6 分片存储 / 索引写入 热节点 ×3 + 温节点 ×3 Kibana 节点 ×2 可视化 / 检索界面 负载均衡对外服务 告警引擎 elastalert 阈值 / 频率匹配 日志分布:业务日志 → 采集层(按主机) → Kafka(按 topic/partition) → ES(按日期/级别分片)

节点角色与职责划分

集群中各节点并非对等关系,而是按职责划分为不同角色。理解角色划分是设计集群架构的基础。下表总结了日志集群中常见的节点角色及其核心职责:

节点角色典型组件核心职责扩展方式
采集节点Filebeat、Fluentd、Fluent Bit读取日志文件、监听端口、解析字段、发送到缓冲层随业务服务器横向扩展
缓冲节点Kafka、Redis Stream、Pulsar削峰填谷、消息持久化、多消费者订阅增加分区数、增加 Broker
传输/过滤节点Logstash、Flume数据清洗、格式转换、富化、路由分发增加实例,按 topic 分流
存储/索引节点Elasticsearch分片存储、倒排索引、聚合查询增加数据节点,调整分片数
检索/展示节点Kibana、Grafana可视化查询、仪表盘、告警配置多实例 + 负载均衡
协调节点ZooKeeper / ES 主节点集群元数据管理、分布式协调固定奇数节点(3 或 5)

日志产生与分布特征

在集群环境中,日志的产生位置和内容类型高度分散。从来源看,日志主要分布在以下四个层面:

  • 应用日志:由业务代码直接输出,通常写入本地文件(如 /var/log/app/order.log),包含业务操作记录、异常堆栈、交易流水等。这类日志格式多样,需要正则解析。
  • 中间件日志:由 Kafka、Redis、MySQL、Nginx 等组件产生,记录访问请求、慢查询、连接状态、错误信息等。例如 Nginx 的 access.log 每行一条请求记录,包含状态码、响应时间、客户端 IP。
  • 系统日志:由操作系统产生,如 /var/log/messages、/var/log/secure,记录内核事件、用户登录、sudo 操作等安全审计信息。
  • 容器与编排日志:在 Kubernetes 环境中,容器标准输出(stdout/stderr)由容器运行时重定向到宿主机文件,通常位于 /var/log/containers/ 目录,文件名格式为 pod-name_container-name_container-id.log。

日志分布的关键规律:日志量通常遵循“80/20 法则”——约 80% 的日志来自 20% 的热点节点(如订单服务、网关服务)。同时,日志的时间分布极不均匀,业务高峰期的日志产生速率可能是低谷期的 10 倍以上。因此,集群设计必须考虑 Topic 分区策略 和 动态扩容能力,避免因单分区热点导致写入瓶颈。

案例:电商平台日志集群的节点规划

以一个日订单量 50 万的电商平台为例,其日志集群规划如下:

业务规模:20 台应用服务器 + 5 台 K8s 节点
日志总量:约 1.2 TB/天,峰值速率约 80 MB/s

采集层:每台服务器部署 Filebeat 1 个实例,监控 /var/log/app/ 目录
缓冲层:Kafka 集群 3 台(8 核 32GB),创建 4 个 topic:
  - app-log:分区数 12,副本数 2
  - access-log:分区数 6,副本数 2
  - audit-log:分区数 3,副本数 3(安全日志,高可靠)
  - sys-log:分区数 3,副本数 2

传输层:Logstash 集群 3 台,按 topic 消费,grok 解析后写入 ES
存储层:Elasticsearch 6 个数据节点(16 核 64GB,SSD)
  - 按天创建索引:app-log-2025.04.01
  - 每个索引 6 个主分片 + 1 个副本分片
  - 冷热分离:3 天前的索引迁移到冷节点(HDD)

该方案中,Kafka 的分区数(12)大于 Logstash 消费实例数(3),确保每个消费者都能分配到多个分区,实现并行消费。ES 分片数(6)与数据节点数(6)匹配,使每个节点承载一个主分片,避免分片堆积在少数节点上。

误区与复盘:集群架构中的常见陷阱

误区一:采集节点越多越好。有些团队在每台服务器上同时部署 Filebeat + Logstash + Fluentd 三个采集器,导致重复采集、CPU 浪费、日志重复写入。正确做法是每台服务器只部署一个轻量采集器(如 Filebeat),将重活(解析、清洗)交给下游 Logstash 集群。

误区二:忽略 Kafka 分区数的上限约束。Kafka 分区数过多会带来两个问题:一是文件句柄占用过高,二是分区迁移耗时过长。经验值是单 Broker 分区数不超过 2000,单 topic 分区数不超过 Broker 数量的 10 倍。若分区数不足,可通过增加 topic 或重新设计 key 策略解决,而不是盲目调大分区。

误区三:ES 分片数设置后不可变。很多团队在创建索引时随意设置分片数,事后发现写入或查询性能不佳却无法调整。实际上,ES 索引的分片数在创建后无法直接修改,只能通过 reindex 重建索引。建议根据数据节点数和预期数据量提前规划:每个分片数据量控制在 30GB~50GB 之间,分片总数不超过数据节点数的 2 倍。

复盘总结:在一次大促压测中,我们发现日志采集延迟从 2 秒飙升到 30 秒。排查后发现,Kafka 某个 topic 的 key 设计不合理——所有日志都使用相同的 key(如服务器 IP),导致所有消息都路由到同一个分区,该分区 Broker 的磁盘 IO 成为瓶颈。修复方案是将 key 改为“服务器 IP + 日志文件名的哈希值”,使消息均匀分布到 12 个分区,延迟恢复到 1 秒以内。这个案例说明,集群架构不仅要关注节点数量,更要关注数据分布策略是否合理。