背景与挑战
B站的日志系统(Billions)自2017年5月开始建设,基于elastic stack,为全站提供统一的日志采集、检索和监控服务。目前集群规模为20台机器,接入业务超过200个,单日日志量达到10T以上。在Billions之前,B站内部没有统一的日志平台,各业务自行实现日志方案,普遍存在维护成本高、系统不稳定、丢日志、易用性不足等问题。同时,业务日志格式各异,无法统一切分,增加了分析和检索成本。此外,容器化应用缺乏有效的日志采集方案,日志利用程度也仅限于检索层面。
设计目标
针对上述问题,新日志系统的设计目标包括:业务日志平滑接入,仅需简单配置;支持多样环境(物理机、容器)、多样来源(系统日志、业务日志、中间件日志)和多样格式(单行/多行、plain/json);实现日志快速可查、监控和统计分析;保证数据实时性和丢失率可控(业务分级、全链路监控)。
Billions的演进
日志规范
为解决日志格式多样性问题,统一制定日志格式规范,使用JSON作为输出格式,必须包含四类元信息:time(ISO8601格式)、level(FATAL、ERROR、WARN、INFO、DEBUG)、app_id(应用id,全局唯一)、instance_id(实例id)。日志详细信息保存到log字段中,业务方可添加额外字段,但mapping应保持不变。
{"log": "hello billions, write more", "level": "INFO", "app_id": "testapp111", "instance_id": "instance1", "time": "2017-08-04T15:59:01.607483", "id": 0}
系统技术方案
日志从产生到消费经历采集、传输、切分、检索四个阶段。
日志采集
针对非落盘方式,与平台技术部合作开发了基于Go的log agent模块,部署在物理机上,通过domain sock文件接收日志(unixgram方式),容器初始化时sock文件被mount到容器内部。log agent分为collector和sender,通过文件缓存交互,保证传输系统故障时日志正常接收。针对非业务模块(中间件、系统模块、接入层)日志,使用filebeat采集,每类日志对应独立配置文件,并打上app_id标签。
日志传输
选择公司内部统一的数据传输平台lancer,基于flume+kafka二次开发,支持可靠传输协议、链路监控和多种消费方式。log agent的sender模块基于lancer协议发送日志,最终传输到kafka集群的不同topic(统一前缀)。
日志切分
使用logstash从kafka消费日志,进行处理后存储到elasticsearch。对于标准格式日志,采用topics_pattern消费,并设置partition_assignment_strategy为RoundRobinAssignor以避免分区分配不均。对于非标准格式日志,每种配置需要单独的logstash进程。
日志检索
elasticsearch集群规模为master node*3, hot node*20, stale node*20, client node*2,版本5.4.3。采用冷热分离方案:hot node使用SSD接收实时日志,stale node使用SATA存储历史日志(只读不写),每日定时进行热-冷迁移。使用curator管理index(默认保留7天),通过template管理mapping,index提前一天创建。监控方面,自研es_exporter采集es状态,基于Prometheus实现报警,覆盖集群状态、thread rejected、节点数量、unassign shard等关键指标。
系统迭代
shard管理
最初所有index对应5*2个shard,导致小index浪费资源,大index检索性能不佳。开发了shard管理模块,根据历史数据量决定shard数量(策略为30GB/shard,上限15),集群shard数量降低70%以上,磁盘IO使用更高效。
日志采样
针对日志量大的业务(如访问日志),在log agent的collector模块增加采样功能:INFO级别以下按比例随机采样,WARN以上全部保留。采样比例通过配置中心动态调整。为避免json解析的高CPU消耗,采用字符查找(bytes.Index)获取app_id字段。目前每天减少3T+日志写入。
data node硬件瓶颈解决
高峰期出现bulk request rejected和SSD IO Utiliztion 100%问题,经测试发现SSD写性能较差。临时将部分实时写流量迁移到stale node,最终为每台机器添加2*1.2T PCIe SSD,彻底解决IO瓶颈。
logstash性能解决
logstash性能测试显示(2 core+4G memory)极限性能仅8000qps,高峰期流量40W/s,需要50个实例。使用Go自研切分模块(billions index),性能提升至5W+qps,内存仅消耗150M,线上资源消耗从(2 core+4G memory)*30减少到(2 core+150M memory)*15。
日志监控
基于elastalert进行二次开发,改进点包括:将rule存储在elasticsearch中,提高可靠性和分布式扩展性;使用模板封装监控规则,降低配置复杂度;封装Restful API进行规则管理;与公司监控系统Bili Moni结合,通过web配置和报警平台发送报警;利用全局锁解决单点问题(一热一冷,热故障时冷自动接手);调整报警内容格式和汉化。
现有问题和下一步工作
- 缺乏权限控制:需实现统一认证、基于index的授权、操作审计等功能。
- 缺乏全链路监控:各层监控独立,无法精准监控堆积和丢失情况。
- 日志监控性能瓶颈:单节点顺序执行,需优化为分布式架构+规则并行执行。
- 日志切分配置复杂:非标准格式日志需单独logstash实例,配置和上线复杂,需平台化支撑。
