资讯动态

Rsyslog 集中式日志系统实战:服务端与客户端配置及避坑指南

发布时间:2026/10/15 5:21:01 来源:尧图企业网站定制
1. 从一台磁盘写满的日志服务器说起凌晨两点监控告警响了——某台核心业务机的根分区使用率冲到98%。登上去一看/var/log/messages单个文件已经涨到十几GB/var/log/目录下还躺着七八个历史轮转文件。更麻烦的是这台机器上跑着二十多个应用每个应用都在往本地写日志谁也没做集中管理。运维同事只能先紧急清理然后一台一台机器去翻日志找问题效率低得让人抓狂。这个场景几乎每个运维人都遇到过。当服务器规模从几台涨到几十台、上百台本地日志的弊端就会集中爆发磁盘被吃满、日志分散在N台机器上排查困难、应用重启后日志丢失、审计合规要求日志必须集中留存。解决思路其实很成熟——搭一套集中式日志系统让所有机器把日志统一转发到一台日志服务器上。而在这个领域Rsyslog是绕不开的基础设施级工具。Rsyslog 是 Linux 系统上最常见的日志守护进程几乎所有的发行版都默认预装。它支持 UDP/TCP/TLS 多种传输协议、支持本地文件和远程转发、支持基于内容的过滤和模板化输出性能也足够扛住每秒数万条日志的写入。这篇文章我会从零讲清楚两件事服务端怎么配、客户端怎么把日志转过去以及中间那些文档里不会写、但实际部署时一定会踩的坑。不管你是刚接触 Linux 运维的新手还是想给现有日志架构做一次梳理的老手这篇内容都能直接拿去用。2. 先搞清楚 Rsyslog 的配置骨架再动手很多人配 Rsyslog 的方式是网上搜一段配置复制粘贴重启服务能跑就行。结果一旦出问题完全不知道从哪查起。所以在动手改配置之前有必要先把它的配置结构和工作模型理清楚后面所有的排错都会轻松很多。2.1 配置文件的分层加载机制Rsyslog 的主配置文件是/etc/rsyslog.conf但它并不是一个孤立的文件。现代发行版普遍采用主配置 片段目录的方式组织配置/etc/rsyslog.conf主配置定义全局参数、模块加载、以及 include 指令/etc/rsyslog.d/*.conf配置片段目录主配置里通常有一行$IncludeConfig /etc/rsyslog.d/*.conf把这里面的文件全部加载进来这个设计的好处是你不需要去动主配置文件只要在/etc/rsyslog.d/下新建一个.conf文件写自己的规则就行。升级系统时主配置被覆盖也不会影响你的自定义规则。我个人的习惯是给每个用途单独建文件比如10-server.conf放服务端监听配置20-forward.conf放转发规则数字前缀控制加载顺序方便排查。注意$IncludeConfig的加载顺序是按文件名字典序来的如果你有多个片段互相依赖命名前缀一定要规划好否则会出现规则明明写了却不生效的情况。2.2 模块、规则与动作的三段式模型Rsyslog 的配置逻辑可以拆成三层来理解理解了这三层配置就不再是玄学第一层是模块Module。Rsyslog 的功能是模块化的你要用哪个功能就得先加载对应模块。比如接收远程日志要加载imudp或imtcpim input module转发日志要加载omfwdom output module写数据库要加载ommysql。模块不加载对应的功能就是死的。第二层是规则Rule。规则由选择器selector 动作action组成。选择器决定哪些日志动作决定这些日志去哪。传统格式长这样# 选择器所有设施、所有优先级 动作写入指定文件 *.* /var/log/all.log第三层是动作Action。动作可以是写文件、转发到远程、执行脚本、写数据库。同一个日志可以匹配多条规则触发多个动作这就是为什么一条日志既能写本地又能转发远程。2.3 传统格式与 RainerScript 格式的取舍Rsyslog 有两套配置语法老的传统格式sysklogd 风格和新的 RainerScript 格式。传统格式简单直观适合写简单的过滤转发RainerScript 用if...then结构支持复杂条件判断、变量操作适合做精细化的路由。举个对比同样是把来自 192.168.1.0/24 网段、且包含 error 关键字的日志转发到远程传统格式很难表达而 RainerScript 一行就够if $fromhost-ip startswith 192.168.1. and $msg contains error then { action(typeomfwd targetlog-server port514 protocoltcp) }我的建议是简单转发用传统格式复杂路由用 RainerScript。两套语法可以在同一个配置文件里混用但不要在同一条规则里混容易出乱子。3. 服务端配置让日志服务器真正接得住服务端的目标很明确监听端口、接收客户端发来的日志、按来源或内容分类落盘。听起来简单但接得住和接得好是两回事下面这几步决定了你的日志服务器是能用还是好用。3.1 开启接收模块并选择传输协议默认情况下 Rsyslog 只处理本机日志不接收远程日志。要让它变成日志服务器第一步是加载输入模块并开启监听。在/etc/rsyslog.d/10-server.conf里写# 加载 UDP 和 TCP 接收模块 module(loadimudp) module(loadimtcp) # 开启监听UDP 和 TCP 都用 514 端口 input(typeimudp port514) input(typeimtcp port514)这里有个关键选择UDP 还是 TCP很多人图省事直接用 UDP因为它是 syslog 的传统默认协议。但 UDP 是无连接的网络一抖动日志就丢而且不保证顺序。TCP 有连接、有重传、有确认可靠性高得多。我的实际经验是生产环境一律用 TCPUDP 只在极少数对性能极度敏感、且能接受丢日志的场景下才考虑。对比项UDP 514TCP 514可靠性无连接可能丢包有连接可靠传输性能略高无握手开销略低但现代硬件无感顺序保证不保证保证防火墙友好度简单需要放行长连接推荐场景内网低价值日志生产环境首选3.2 用模板定义日志的落盘路径日志收进来之后往哪写如果所有客户端的日志都堆在一个文件里排查时根本没法区分来源。这时候就要用模板template把日志按来源 IP、主机名、程序名等维度拆分到不同文件。# 定义模板按来源主机名 程序名分目录存储 template(namePerHostPerApp typestring string/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log) # 定义模板按来源 IP 分目录 template(namePerHostIP typestring string/var/log/remote/%FROMHOST-IP%/%$YEAR%-%$MONTH%-%$DAY%.log)模板里可用的属性很多常用的几个%HOSTNAME%发送方主机名%FROMHOST-IP%发送方 IP%PROGRAMNAME%产生日志的程序名%syslogtag%日志标签%$YEAR%、%$MONTH%、%$DAY%年月日用于按天切分定义好模板后用动作把日志按模板落盘# 所有远程日志按主机程序分文件存储 *.* action(typeomfile dynaFilePerHostPerApp)dynaFile表示动态文件名Rsyslog 会根据模板为每条日志计算出实际路径。这里有个坑目录不会自动创建。如果/var/log/remote/下没有对应主机名的子目录日志会写失败。解决办法是在模板里用dynaFile配合createDirs参数*.* action(typeomfile dynaFilePerHostPerApp createDirson)createDirson让 Rsyslog 自动创建缺失的目录层级这个参数能省掉大量手工建目录的麻烦。3.3 按来源 IP 做访问控制日志服务器开放 514 端口后理论上任何能连上来的机器都能往里灌日志。如果这台服务器有公网 IP很快就会收到各种垃圾日志甚至恶意注入。所以访问控制是必须做的。Rsyslog 本身支持基于来源 IP 的过滤在规则里加判断即可# 只接收来自内网网段的日志 if $fromhost-ip startswith 10.0. or $fromhost-ip startswith 192.168. then { action(typeomfile dynaFilePerHostPerApp createDirson) } # 其他来源的日志直接丢弃不写任何动作即为丢弃但更稳妥的做法是在防火墙层面就限制只允许业务网段访问 514 端口。Rsyslog 层的过滤作为第二道防线。两层配合既防误配也防恶意。提示如果日志服务器需要跨网段接收务必确认中间没有 NAT 设备改写源 IP否则$fromhost-ip拿到的是 NAT 后的地址过滤规则会失效。3.4 服务端性能与磁盘规划日志服务器是典型的写多读少场景磁盘 I/O 是瓶颈。几个实操建议单独挂一块盘给/var/log/remote避免日志写满影响系统盘文件系统选 XFS对大量小文件追加写的性能比 ext4 更稳关闭 atime挂载时加noatime减少不必要的元数据写入配置 logrotate远程日志目录也要轮转否则单文件会无限增长logrotate 配置示例/var/log/remote/*/*.log { daily rotate 30 compress delaycompress missingok notifempty create 0640 syslog adm sharedscripts postrotate /usr/bin/systemctl reload rsyslog /dev/null 21 || true endscript }rotate 30保留 30 天compress压缩历史文件postrotate里 reload rsyslog 让它重新打开文件句柄。这里reload而不是restart是因为 reload 不会中断正在接收的日志连接。4. 客户端配置把日志稳稳地送出去客户端这边要做的事情是告诉 Rsyslog 哪些日志、通过什么协议、发到哪台服务器。看似简单但发出去和稳定地发出去、且不丢之间差着好几个配置细节。4.1 转发规则的基本写法在客户端的/etc/rsyslog.d/20-forward.conf里写转发规则。最基础的写法# 加载 TCP 输出模块现代版本 omfwd 已内置通常无需显式加载 # 把所有日志通过 TCP 转发到日志服务器 *.* action(typeomfwd target10.0.0.100 port514 protocoltcp)target填日志服务器的 IP 或域名port是服务端监听的端口protocol选tcp或udp。这一行会把本机所有设施的日志全部转发出去。但实际生产中我们往往不想转发全部日志。比如本机的 cron 日志、内核日志可能没必要集中而应用日志必须转发。这时候就要用选择器精确控制# 只转发 authpriv认证和 local0-local7自定义应用设施的日志 authpriv.* action(typeomfwd target10.0.0.100 port514 protocoltcp) local0.* action(typeomfwd target10.0.0.100 port514 protocoltcp) local1.* action(typeomfwd target10.0.0.100 port514 protocoltcp)很多应用比如 Nginx、部分 Java 服务会约定把日志打到local0到local7这些自定义设施上这样就能和系统日志区分开转发时也更好控制。4.2 断线重连与队列解决服务器挂了日志就丢这是客户端配置里最容易被忽略、但最重要的一环。默认情况下如果日志服务器不可达Rsyslog 会直接丢弃日志。网络抖动、服务端重启、磁盘满任何一个环节出问题日志就没了。解决办法是配置内存队列 磁盘辅助队列。当转发失败时日志先存到本地队列等连接恢复再补发# 定义队列参数 $ActionQueueType LinkedList # 链表队列性能好 $ActionQueueFileName fwdQueue # 磁盘队列文件名前缀 $ActionQueueMaxDiskSpace 1g # 磁盘队列最大占用 1GB $ActionQueueSaveOnShutdown on # 关机时保存队列 $ActionResumeRetryCount -1 # 无限重试直到成功 # 转发动作 *.* action(typeomfwd target10.0.0.100 port514 protocoltcp queue.typeLinkedList queue.filenamefwdQueue queue.maxdiskspace1g queue.saveonshutdownon action.resumeRetryCount-1)几个参数的含义和取舍queue.typeLinkedList链表队列入队出队效率高适合高吞吐queue.filename指定磁盘队列文件前缀队列溢出到磁盘时用queue.maxdiskspace磁盘队列上限超过后新日志会被丢弃所以要给足空间queue.saveonshutdownonRsyslog 关闭时把内存队列刷到磁盘避免重启丢日志action.resumeRetryCount-1-1 表示无限重试连接不上就一直试注意磁盘队列文件默认放在/var/spool/rsyslog/下要确保这个目录所在分区有足够空间。我见过因为/var分区太小导致队列写不进去、日志照样丢的案例。4.3 用 TLS 加密传输敏感日志如果日志里包含认证信息、用户数据等敏感内容明文传输就不合适了。Rsyslog 支持 TLS 加密配置分服务端和客户端两部分。服务端加载 TLS 驱动并配置证书module(loadimtcp StreamDriver.Namegtls StreamDriver.Mode1 StreamDriver.AuthModex509/name PermittedPeer[client1.example.internal, client2.example.internal]) input(typeimtcp port6514)客户端配置证书并连接action(typeomfwd targetlog-server port6514 protocoltcp StreamDrivergtls StreamDriverMode1 StreamDriverAuthModex509/name StreamDriverPermittedPeerslog-server.example.internal)TLS 配置涉及证书签发、CA 信任链、主机名匹配等细节较多建议单独找时间专门调试。核心原则是服务端和客户端的 AuthMode 要一致PermittedPeer 里的主机名必须和证书里的 CN/SAN 完全匹配否则握手会失败。4.4 客户端配置的验证方法配置写完别急着上线先在测试机上验证。几个实用的验证手段第一用logger命令手动造一条日志logger -p local0.info test message from client然后在服务端tail -f /var/log/remote/客户端主机名/local0.log看能不能收到。第二检查 Rsyslog 自身状态systemctl status rsyslog journalctl -u rsyslog -n 50如果配置有语法错误systemctl restart rsyslog会直接失败journalctl里能看到具体报错行号。第三用rsyslogd -N1做配置语法检查rsyslogd -N1 -f /etc/rsyslog.conf这个命令只检查配置语法不实际启动服务非常适合改完配置后先跑一遍。输出里如果有error或warning逐条排查。5. 那些文档里不写、但一定会踩的坑配置能跑通只是第一步真正让人头疼的是那些看起来正常、实际有问题的隐性故障。下面这几个坑我在不同项目里反复遇到过每一个都值得单独拎出来说。5.1 主机名重复导致日志互相覆盖Rsyslog 默认用%HOSTNAME%作为日志分类依据但很多批量部署的机器主机名是重复的比如都叫localhost或者模板化的server-01没改。结果就是多台机器的日志全写进同一个文件互相覆盖排查时完全分不清哪条来自哪台。根因主机名不是唯一标识IP 才是。解决方案模板里用%FROMHOST-IP%替代%HOSTNAME%或者两者组合template(namePerHostIP typestring string/var/log/remote/%FROMHOST-IP%/%PROGRAMNAME%.log)如果一定要用主机名就在客户端配置里显式指定一个唯一标识# 客户端设置本机标识 $LocalHostName client-0015.2 时间戳时区不一致日志服务器收到的日志时间戳可能是客户端本地时间也可能是 UTC取决于客户端配置。如果客户端分布在多个时区日志按时间排序就会乱套。排查方法在服务端随便挑几条日志对比%TIMESTAMP%和实际接收时间。解决方案统一要求所有客户端使用 UTC或者在服务端模板里用接收时间而非发送时间template(nameWithRecvTime typestring string%$NOW% %TIMESTAMP% %HOSTNAME% %syslogtag%%msg%\n)%$NOW%是服务端接收时刻%TIMESTAMP%是客户端发送时刻两个都记下来排查时区问题时一目了然。5.3 日志风暴打爆服务端某次大促期间一台应用服务器因为代码 bug 疯狂打日志每秒几万条涌向日志服务器直接把服务端的磁盘 I/O 打满其他机器的日志也跟着延迟。这就是典型的日志风暴。预防手段服务端配置imtcp的MaxSessions限制单机连接数用rateLimit.interval和rateLimit.burst做限流input(typeimtcp port514 rateLimit.interval5 rateLimit.burst10000)这表示 5 秒内最多接收 10000 条超过的部分丢弃。虽然会丢日志但保住了服务端不被拖垮两害相权取其轻。客户端侧也要做限流避免问题机器把整个链路拖慢5.4 防火墙放行了端口但日志还是不通这个坑特别隐蔽。明明firewall-cmd里放行了 514/tcp客户端也配了转发但服务端就是收不到。常见原因有三个一是 SELinux 拦截。Rsyslog 监听非标准端口时SELinux 会阻止。检查getenforce ausearch -m avc -ts recent如果看到 AVC 拒绝记录要么给端口打上正确的 SELinux 标签要么临时设为 permissive 验证。二是服务端只监听了 IPv6。netstat -tlnp | grep 514看一下如果只显示:::514而没有0.0.0.0:514说明只监听了 IPv6IPv4 客户端连不上。在input里显式指定address0.0.0.0。三是中间有安全组或网络 ACL 没放行。云环境下除了主机防火墙还有安全组这一层两层都要放行。5.5 日志轮转后 Rsyslog 还在写旧文件logrotate 轮转日志后如果 Rsyslog 没有重新打开文件句柄它会继续往已经被重命名的旧文件里写新文件一直是空的。这就是为什么 logrotate 配置里必须有postrotate那段 reload。但 reload 也有讲究用systemctl reload而不是restart。reload 发送 SIGHUP 信号Rsyslog 会重新打开所有文件句柄但不断开连接restart 会完全重启进程正在传输的日志可能丢失。如果发现轮转后日志还在写旧文件检查两点logrotate 的postrotate脚本是否执行成功以及 Rsyslog 是否有权限重新打开新文件新文件的属主和权限要对。6. 一套可直接复用的最小可用配置讲了这么多原理和坑最后给出一套我实际在用的最小可用配置服务端和客户端各一份拿去改改 IP 就能用。6.1 服务端完整配置/etc/rsyslog.d/10-server.conf# 加载接收模块 module(loadimudp) module(loadimtcp) # 监听端口显式绑定 IPv4 input(typeimudp port514 address0.0.0.0) input(typeimtcp port514 address0.0.0.0 rateLimit.interval5 rateLimit.burst20000) # 模板按来源IP和程序名分目录 template(namePerHostPerApp typestring string/var/log/remote/%FROMHOST-IP%/%PROGRAMNAME%.log) # 只接收内网日志自动创建目录 if $fromhost-ip startswith 10.0. or $fromhost-ip startswith 192.168. then { action(typeomfile dynaFilePerHostPerApp createDirson) }6.2 客户端完整配置/etc/rsyslog.d/20-forward.conf# 转发所有日志到服务端带磁盘队列兜底 *.* action(typeomfwd target10.0.0.100 port514 protocoltcp queue.typeLinkedList queue.filenamefwdQueue queue.maxdiskspace1g queue.saveonshutdownon action.resumeRetryCount-1)6.3 上线前的检查清单配置改完按这个清单逐项确认能避开九成以上的问题检查项命令/方法预期结果配置语法rsyslogd -N1无 error服务状态systemctl status rsyslogactive (running)端口监听ss -tlnp | grep 5140.0.0.0:514防火墙firewall-cmd --list-ports包含 514/tcp端到端测试logger -p local0.info test服务端收到队列目录ls /var/spool/rsyslog/有写权限磁盘空间df -h /var/log余量充足这套配置我在多个中小规模环境里跑过稳定运行没什么问题。规模再大比如上千台客户端就需要考虑引入 Kafka 之类的消息队列做缓冲或者上更专业的日志平台但那是另一个话题了。最后分享一个我踩过的坑改完客户端配置后一定要在服务端确认日志真的到了而不是只看客户端systemctl status显示 running 就以为万事大吉。Rsyslog 的转发失败很多时候是静默的客户端进程活得好好的日志却一条没发出去。养成改完必验证端到端的习惯能省掉无数个深夜排查的夜晚。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑