留存期做到了 6 个月,日志一条不少地躺在盘上,但真要回溯某台机器在某个时间点发生了什么时,时间线却拼不起来——问题通常不在采集端。常见的尴尬是这样:一次内部排查中,某台应用主机在凌晨留下了权限提升的记录,边界设备在同一批次留下了一条放行记录,两份数据都在留存期内、都能从磁盘读出来,可两边的时间戳差了六分多钟;再往前翻,发现那台主机在三个月前因为一次重装改过主机名,两个月前它的内网地址又被回收分配给了另一台机器。三条本该串成一条线的记录,最后只能当作三件互不相干的事各自归档。
这类失败有个共同点:丢的不是"有没有记",而是"能不能连"。日志要当证据,关键不在采集端写了多少条,而在于这条记录从产生到归档之间有没有被悄悄改过、断过或错位过。集中外发与留存链路上有四个位置最容易出问题:传输层会不会静默丢、缓冲落在哪一侧、时间基准与主机标识是否一致、留存分层里的保留期与完整性校验有没有兜住。这四关任何一关没过,事后回溯就拼不出完整时间线,前面攒下来的量只是无法举证的堆积。
本文面向有留存期要求的中小企业与自建机房:目标是把服务器、网络设备、安全设备的日志集中到一台日志服务器,留存 6 个月以上,半年之后还能回答某台机器在某个时间点发生了什么。
先把账算出来,因为后面四道关的每一道都要付出磁盘或 IO 代价:加 TLS 要多一轮加解密、磁盘辅助队列要占写入带宽、结构化字段会让单条消息变长、留存副本会放大总盘。容量算不准,方案在第三个月就开始被迫删数据。
可套用公式:单日原始字节 = EPS × 单条平均字节 × 86400;保留期原始总量 = 单日原始字节 × 天数;落盘量 = 原始总量 ÷ 压缩比。EPS 是每秒事件数,单条平均字节取自家样本的实际均值。
算例(代入值,仅作估算口径):忙时平均 EPS 取 2000,含 RFC 5424 头部后单条平均 500 字节。单日 = 2000 × 500 × 86400 = 864 亿字节,约 86.4 GB(十进制口径),折合约 80.5 GiB。180 天全量不压缩就是 15,552 GB,约 15.55 TB。若把忙时集中系数按 2 倍计入突发,这条数会翻倍到 31 TB 上下,这也是很多自建环境半年后发现盘不够的直接原因。
上面的演算只覆盖了理想情形,落地时还要再加三项修正。一是来源构成:把总量拆成服务器操作系统日志、网络设备日志、安全设备日志几类分别估算,安全设备的单条往往明显长于其他类型,混在一起取平均值会低估。二是副本:前面提到的数已经是按两份在算,如果冷层只做单副本而热层做双副本,就要按层分别乘系数,不能用一个全局倍数糊过去。三是写入带宽:日均写入压力不大,但磁盘辅助队列、压缩与归档动作集中在特定时段,峰值写入量要单独核一遍,机械盘上这一步最容易成为隐藏瓶颈。
还有一笔容易被漏掉的账,是倒推本地队列配额。队列需要撑住的中断时长,乘以峰值 EPS,再乘以单条字节,得到的就是漏斗下限。按 2000 EPS、500 字节、容忍 4 小时远端中断来算,约是 2000 × 500 × 14400 = 14.4 GB;这个数必须按峰值而不是日均值去重算,否则恢复时间稍微拉长就会击穿队列。
分层之后账会好看很多。按热层 14 天不压缩、温层第 15 到 90 天共 76 天按 4:1 压缩、冷层第 91 到 180 天共 90 天按 8:1 压缩来算:热层 1210 GB,温层约 1642 GB,冷层约 972 GB,合计约 3823 GB。再乘副本系数 2,加文件系统开销与水位线预留 20%,规划可用容量约 9.2 TB。压缩率这一项必须拿自家样本压一遍才能用,纯文本日志的通用压缩能到数倍到十几倍,本文取的是偏保守区间。
Syslog 的消息格式与传输约定由 IETF 标准化:RFC 3164 描述的是 BSD syslog 的既有约定,习惯跑在 UDP 514;RFC 5424 定义了带 STRUCTURED-DATA 的新格式,RFC 5425 定义了对应的 TLS 传输。UDP 这一层的麻烦在于它的三个"没有":没有确认、没有重传、没有背压。
没有确认,意味着发送端发出去之后不知道对方收没收到;没有重传,意味着丢了的报文永远不会再来第二遍;没有背压,意味着接收端处理不过来时不会反向通知发送端降速,超出接收缓冲的报文被内核直接丢弃,而发送端的应用程序通常完全无感知。三个叠加的结果是一种最难查的故障形态:日志看起来在正常产生,接收端看起来在正常收,中间的缺口谁都不知道。
把它从"看不见"变成"看得见",是这一关最低成本的动作,成本远低于换协议。做法是让每条消息带上每台设备内单调递增的序号(或依赖发送线程自身的计数),接收端按设备维度记录上一次序号,出现跳号就立刻记为缺口事件并告警;同时对每条链路维护"发送端计数"与"接收端落盘计数"两个累累值,定期取差。这两个值一旦长期相等,说明这一关是干净的;一旦在高峰期拉开差距,就需要回头看接收端的 UDP 缓冲与处理吞吐。
网络抖动、日志服务器重启、上游交换机瞬时拥塞、接收端 UDP 缓冲区被打满,都会触发这种静默丢。它的可怕之处在于丢的比例不固定——平时可能是万分之几,出事那一刻往往正是流量最大的时候,缺口恰好落在最需要证据的时间窗里。另一个隐蔽来源是 MTU 与分片:单条消息超过路径 MTU 后被分片,任一片丢失整条消息就不可用,而传统的设备输出往往不限长度,超长消息比重不低。
换成 TCP 之后,"报文在网络上被丢"这一层基本被补上了:有序、有确认、有重传、有流控。但它引入了两个新问题。一是连接本身的生命周期管理:连接断了要重建,重建之后从哪一条继续,需要发送端自己记位置;如果发送端没有记录位点,重连后要么重发造成重复,要么跳过造成缺口。二是队头阻塞:单个连接上一条消息写得慢或者接收端处理卡了一下,后续消息会排队等待,多条设备复用同一条连接时,一台慢设备会拖住一批设备的时延。
TLS(RFC 5425 定义的 syslog over TLS)在原地带上了机密性、完整性与对端身份认证,代价是握手开销、加解密 CPU、证书签发与轮换运维,以及连接失效后的重协商处理。对跨公网或跨机房边界的日志外发,这一层的成本基本省不掉;对同机房内网,是否上 TLS 要看是否有明确的传输保护要求。
RELP(Reliable Event Logging Protocol)是 rsyslog 提供的、带应用级确认与重传的协议。它要解决的是 TCP 也管不到的那一段:TCP 只能保证"字节到了内核缓冲区",不能保证"接收进程已经处理并落盘"。RELP 在应用层给确认,发送端拿到确认才认为这批消息安全。这一关要不要上,看的是你能不能接受"进程崩了之后最后几百条是否丢失"这个风险。
| 四道关 | 常见做法 | 失败时的具体表现 | 这一关要补的东西 | 怎么验证它真的生效 |
|---|---|---|---|---|
| 传输可靠性 | UDP 514 直发,部分设备改 TCP 或 TLS | 高峰时段成段缺口,发送端无任何报错,采集侧指标显示正常 | 带应用级确认的传输(RELP 一类),或至少 TCP 加发送端位点记录;边界与跨机房链路加 TLS | 两端按序列号对账,定期统计缺号;主动断链后检查恢复后是否重复或缺段 |
| 缓冲位置 | 默认纯内存队列,队列满后丢弃 | 日志服务重启或升级后丢失重启前若干分钟到若干小时的消息;远端不可达期间的消息整体蒸发 | 磁盘辅助(disk-assisted)队列,内存溢出到磁盘;队列上限与同步策略按实现版本文档配置 | 人为停采集端再恢复,统计缺口窗口长度;断电测试后检查队列文件是否完整可读 |
| 时间基准与主机标识 | 各设备各自走默认时间源,用 hostname 或来源 IP 区分设备 | 跨设备事件对不上先后,重装后同一台机器出现两个名字,IP 回收后出现两台机器共用一个地址 | 统一 NTP 时间源、统一时区口径、写入时刻同时落来源 IP 与 hostname,维护一张历史对应表 | 抽样跨三台设备同一事件的时间差;抽查半年内 hostname 与 IP 的变更有没有留下记录 |
| 留存分层与完整性 | 单目录平铺保存,到期手动清理 | 留存期内被提前清理,或被改动后无法自证未篡改;冷数据想查时发现格式已不可读 | 热/温/冷三层保留期与压缩策略,只追加存储加周期性校验值,删除动作按策略自动执行 | 按天校验和见证文件逐个复算;随机抽一条半年前的记录,走一遍还原到可读的完整流程 |
| 消息长度与字段 | 沿用 RFC 3164 格式,长消息靠截断保平安 | 堆栈、审计命令、策略命中详情被切在 1024 字节附近,关键参数恰好在截断之后 | 接收端放宽长度上限,优先让设备按 RFC 5424 输出结构化字段,关键字段前置 | 构造一条超长消息端到端发送,检查落盘内容是否完整、字段能否被直接检索 |
传输层补完之后,往下走是发送端自己的缓冲。绝大多数 syslog 实现默认用内存队列:消息从应用出来,进内存队列,再由发送线程写出。这段路径快,但有个致命性质——内存里的内容在进程退出时不复存在。一次服务重启、一次版本升级、一次 OOM 被杀,队列里还没发出去的几百到几十万条消息整段消失,而这段时间往往正是系统状态最不稳定的时候。
队列满时的处理策略同样关键。常见的行为是在队列达到上限后开始丢弃新消息,且优先丢弃低优先级(严重级别数值大)的消息,也就是说 debug、info 先没。如果取证依赖的恰好是某条 info 级的审计记录,它在高峰期被策略性丢弃,日志服务器这边永远不会知道。严重级别共 8 级,从 0 的 emergency 到 7 的 debug,设施值(facility)是固定集合的编码,两者组合成消息头部的优先级值,过滤规则就是基于这两写的。
rsyslog 与 syslog-ng 都提供磁盘辅助(disk-assisted)机制:内存队列作为常态路径保持速度,队列满或远端不可用时,消息溢出到磁盘上的持久化队列文件,等链路恢复后再补发。这一层把丢失窗口从"进程生命周期"缩小到"落盘与实际断电之间的那一瞬"。
但这个窗口不是零。是否开启、队列大小上限、多久做一次落盘同步,全部由配置决定,具体参数需以实际版本文档为准。用激进的同步策略,断电窗口可以压到很小,代价是每条消息都引入一次 fsync 级别的写放大,对 IOPS 有限的机械盘是个不小负担;用宽松策略,断电或系统崩溃时可能丢掉最后一次同步之后的一段。这里的取舍很直白:要低丢失率就付出磁盘写性能,想要省磁盘写就接受一个能用 UPS 和供电冗余去覆盖的残余窗口。
还有一条容易被忽略的边界:磁盘辅助队列本身也要占空间。如果给它划的分区太小,队列打满之后行为和内存队列打满一样,退化为丢弃;如果远端日志服务器停机超过本地队列能撑住的时长,数据照样丢失。所以队列配额应该按"能容忍的远端中断时长 × 峰值 EPS × 单条字节"来倒推,把口语化的保证换成真正跑过的动作。
配套的还有两件事。一是队列文件的存放位置,把它与操作系统所在的盘分开,避免日志突发把系统盘写满,导致日志服务之外的其他组件一起故障;二是对队列水位本身做监控,积压深度是一个必须上监控面板的指标,持续上涨就意味着下游消费能力不足,等到打满再处理时数据已经在丢了。
停机检修也要提前规划。计划内的日志服务器停机,可以把设备侧缓冲区作为临时兜底,或者让设备在这段时间把日志落到本地文件再由采集端补抓;计划外停机就只能靠本地队列撑住。把"计划内中断"和"计划外中断"分别跑一次恢复演练并做好记录,比事后口头声称"应该没丢"有说服力得多。
跨设备关联靠两个字段:时间和来源。时间这一项上最常见的失败不是设备没配 NTP,而是各自配了不同的上级时间源,且没有人验证过偏差量。NTP 的实际对时精度取决于层级与网络条件,机房内同层同步和跨广域网同步能拿到的偏差不是一个量级,这里不写具体毫秒数是常识判断,写成"需要按自家链路验证"更诚实。
时区问题比时钟偏差更隐蔽。RFC 3164 定义的时间戳格式里没有年份、没有时区偏移,只有本地时间的月日时分秒。这意味着跨年归档要靠文件系统时间补年份,跨时区要靠约定猜偏移。夏令时导致的重复小时会让同一天的同一个本地时间戳出现两次,回溯时这两小时的先后顺序完全无法从时间戳本身判断。RFC 5424 的 TIMESTAMP 采用带时区偏移的完整日期时间格式,把年份和偏移写进消息本身,这一步是它能当证据的基础条件之一。
可利用的判断标准是:所有设备指向同一组内部时间源,这组源本身要有冗余而不是单点;统一按 UTC 输出或在消息里强制带偏移;日志服务器侧定期采集各来源的偏差量并纳入告警,超过阈值就修。虚拟机还要额外留意一件事:虚机时钟在高负载宿主机上可能漂移甚至跳变,只在对时那一瞬间做检查不够,需要持续观察。另外建议在每条记录里同时保留设备本地时间与接收时刻两个时间,两者长期共存,日后一旦发现某台设备时钟出错,至少还能用接收时刻重建相对顺序。
第二个字段是主机标识。设备侧通常默认用 hostname 或由接收端补上来源 IP,两者单独用都有硬伤。hostname 会因为重装、标准化改名、主机名策略调整而变,一台机器半年里可能用过两个名字;来源 IP 会因为 DHCP 回收、资源池重编排、虚拟机漂移而变,一个地址半年里可能属于过两台机器。回溯时如果只拿着其中一个字段,很容易把两段不同机器的历史拼成一个人的履历,或者把同一个人的两段历史当成两个人。
还有一种情况容易被忽略:经过网络地址转换之后,多台内部设备在日志服务器上看到的来源地址是同一个,这时候来源 IP 不仅会重复,还会同时失效。解决办法是在发送端就把资产标识写进消息,而不是指望接收端从网络层补信息。
可落地的做法是在接收端同时记录来源 IP、消息里的 hostname,以及接收时刻,并把它们写进一张长期保存的对应关系表里;设备侧最好补一个稳定的自定义标识(资产编号、带结构化字段的设备 ID),让它在改名换 IP 之后仍然一致。这条要求是自证的:如果明天有台机器换了名字,你的表里必须能查到它昨天叫什么,否则证据链就断在改名那一刻。对应关系表本身也要纳入留存范围并且是只追加的,否则它会变成下一个失效环节。
留存不是"存满 180 天然后删"这么简单,因为前 14 天和第 170 天的使用方式完全不同。热层存最近 14 天,保持原始格式不压缩或少压缩,要求能被直接检索、能被直接打开看,这一层的价值是响应速度:排查当天问题时,没人愿意先解压。温层覆盖第 15 到 90 天,用通用压缩算法压掉大部分体积,查询时按需解压,代价是秒级到分钟级的响应延迟。冷层覆盖第 91 到 180 天,进一步压缩并按天或按周归档打包,通常不再提供实时检索,只在取证时整体取出。
三层的边界要写成可执行的策略而不是口头约定:哪一天滚到下一层、滚动时做不做格式转换、转换后原文件何时删、删除动作由谁触发、有没有审批与留痕。很多环境的"6 个月留存"最后是败在一条手工执行的清理脚本上——它在某次磁盘告警时被提前跑了一遍,删掉的那段正好是后来要查的那个月。把清理改成按策略自动执行且有执行日志,本身就是留存可信度的一部分。
文件组织方式也值得提前定好。按天滚动还是按大小滚动,要考虑到"取出某一天全部数据"这个取证动作的成本:按天滚动的文件一次就能拿全,按大小滚动则要先做一次归并。归档文件名里建议带上数据来源、日期区间与所属层级,半年后从一堆编号文件里找某天的记录时,命名规范能省掉大量人工核对。索引最好与原文分离存放,原文一旦封箱就不再变动,索引损坏时可以重建,反过来则不行。
还有一个容量陷阱:取证时往往需要把某段时间的数据整体导出到另一处,导出的解压后体积会瞬间放大数倍。规划设计时要留一块临时空间给导出操作,否则会在最着急的时刻卡在磁盘空间不足上。
冷层的介质选择会影响另外一个变量:取回时延。留在本机组内的磁盘上,冷数据和热数据在同一套 IO 通道里,归档阶段会跟在线查询抢带宽;放到外部对象存储或可离线保存的介质上,日常成本与访问方式都不同,取回一段半年前的记录可能需要先做恢复动作。选择时先看两个数字——每年新增冷层体积,以及实际发生过的取证频次,取证频次低的场景可以更激进地把冷层推向离线形态,频次高的场景则需要把可查询窗口拉长。
留存期内不被提前删是一回事,能不能证明"没被改过"是另一回事。三层的边界之上还需要两个机制。一个是只追加存储:归档文件一旦封箱就不再改写,修改只能追加新文件或追加记录,任何历史内容的变更都会留下痕迹。另一个是周期性校验:按天或按文件生成校验值,集中保管并与原数据分离存放,定期复算比对。
这两件事解决的是举证时的最后一问——别人凭什么相信这半年前的日志没有被加工过。注意这里不涉及任何合规结论,本文也不提供合规承诺,只陈述技术上的必要条件:如果存储是可改写的、校验值放在同一块盘上由同一个账号管理,那么"不可篡改"在技术层面就不成立。另外别忘了备份副本——单副本方案在一块盘故障时会同时丢掉留存数据和它的校验值。
再来补一个常常被放到最后才发现的问题:消息被截断。传统 BSD syslog 实现里消息经常被限制在 1024 字节左右,超出部分直接丢掉,具体上限以实际实现与版本为准。RFC 5424 建议实现至少支持 480 字节,应当支持至少 2048 字节,但它同样允许接收方自行截断超长消息。于是出现一种典型的残缺证据:一条审计记录的前半段有用户名、有操作类型,正好在参数列表前面被切掉,留存完好,可用性为零。
结构化字段是这个问题的解药之一。RFC 3164 的消息体是自由文本,事件的所有细节都挤在一句话里,检索只能靠关键词匹配,字段稍有变化就漏掉。RFC 5424 的 STRUCTURED-DATA 允许在消息里带 SD-ID 与参数名值对,把来源、会话、对象、结果这类关键字段独立出来,接收端可以按字段精确过滤与聚合,不再依赖字符串猜词。代价是单条消息变长,直接放大前面算过的容量账。
实践上的折衷是分层处理:让支持 RFC 5424 的设备输出结构化格式,仍只支持 RFC 3164 的设备在接收端做字段提取,同时把接收端的最大消息长度提高并把被截断的消息单独计数告警。截断这件事必须能被观测到——沉默的截断比没记更危险,因为它留下的是一条看起来完整、实际残缺的证据。
落地顺序建议反向排,先做改动小、收益高的一步:把第三关的时间基准与主机标识补齐,因为越晚修,历史数据就越不可关联;再补第二关的磁盘辅助队列与队列配额,让间歇性中断不再丢数据;接着处理第一关的传输协议替换,可以按设备重要性分批迁移,先迁关键主机与安全设备;留存分层放在整个改造的末段,它代价最大,却不影响新数据从第一天起就完整。
拓扑上还要决定设备直发,还是需要一层汇聚转发。直发的优点是链路短、日志服务器看到的来源地址就是真实设备地址,缺点是每台设备各自持有一条到日志服务器的连接,几百台设备就是几百条流,连接管理、证书管理与策略变更都会被放大。加一层转发节点的好处,是把"换协议"这件改造工作的改动面从几百台设备收敛到几台转发器,由转发器统一做加密封装、字段补齐与本地缓冲;代价是转发之后日志服务器看到的来源变成了转发节点,前面讲过的主机标识问题必须在转发之前先解决掉,否则这层会把来源信息直接抹平。
每一步都要配一条"这一关已经通过"的可观测指标,而不是靠人的感觉判断:时间偏差的允许阈值、队列积压的水位阈值、缺口序号的告警阈值、校验值复算的失败率阈值。四道关是不是真的过了,看的是这四个数字有没有长期稳定在阈值内。
容量与 IO 是这套方案的物理底座。前面算过的约 9.2 TB 规划容量、以及磁盘辅助队列带来的持续随机写,决定了这台日志服务器不能用"能装多少盘"这一个维度去挑,写入路径的持久性、掉电保护、以及余下的扩展槽位都要一并考虑。一万网络深耕 IDC 行业 19 年(成立于 2007 年),在华南、华东、华北及中国香港、海外多地整合自营节点与 BGP 多线资源,提供服务器租用、机柜托管与带宽接入的一体化交付;具体机型、带宽与报价以官网实时信息或咨询确认为准。
划清本文边界:这里只讨论日志外发与留存链路的设计变量与容量估算方法,不提供任何具体合规结论与合规承诺,不提供报价,也不给通用服务器选型清单;所有容量数字都是可套用公式下的估算,实际值需按自家日志样本与实现版本代入同一公式重新核算。
自建日志服务器的经济性有个大致适用范围。在设备规模几十到两三百台、EPS 峰值几千以内、留存期半年到一年的区间里,一台物理机加分层存储基本能覆盖,边际成本低,可控性强。超过这个范围之后,瓶颈往往先出现在检索侧而不是存储侧:单机的索引能力撑不住多并发的全文查询,温冷层数据一解压就打爆内存。
需要换成别的形式的信号有几种:留存期要求延长到数年且访问频率极低(这种情况下冷层交给对象存储归档更合适,本地只留热层与索引);跨地域多机房需要统一检索与统一身份(自建的跨站点复制复杂度会迅速超过托管方案);审计方要求提供第三方可验证的时间戳或保管链,而这个能力自建不方便补齐。反过来,如果留存要求就是半年、设备都在同一个机房、且需要日志不出内网,自建依然是更直接的选择。
必须由你在自家环境里跑一遍的项目有这些:日志的单条平均字节与忙时峰值 EPS;温层与冷层归档的压缩率(拿真实样本压一遍,不要直接套用本文的 4:1 与 8:1);设备到日志服务器的实际时间偏差;队列同步策略在这套磁盘上的写性能损耗;消息长度上限提高之后端到端是否真的不再截断。这些数字换一个环境就不成立,本文给的全部是口径和方法,不是结论。
本文引用的标准事实来自 IETF 公开文档:RFC 3164(BSD syslog 的约定与 UDP 514 端口)、RFC 5424(结构化消息格式、建议至少支持 480 字节、应当支持至少 2048 字节、8 级严重级别与固定集合的设施值编码)、RFC 5425(基于 TLS 的 syslog 传输)。RELP 与磁盘辅助队列是 rsyslog、syslog-ng 的常见能力,前者提供应用级确认与重传,后者决定内存溢出后的落盘行为,是否开启、队列大小与同步策略需以实际版本文档为准。
容量算例中的 EPS、单条字节、压缩比均为演示代入值,标记为估算口径;NTP 对时精度取决于层级与网络条件,本文只给出"必须统一时间源与时区口径"的判断,不给出具体精度数值。全文不含我们在生产环境上取得的测量数据、不含性能评测结果、不含具体项目案例;所有需要在生产环境执行的变更,请先在与业务隔离的环境中验证后再上线。
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品