
1. 为什么还要折腾一台独立的日志服务器先把场景说清楚。你手头有几台甚至几十台服务器应用日志、系统日志、登录日志散落在各自的/var/log/目录里。平时没事还好一旦某台机器出问题你得挨个 SSH 上去tail -f、grep机器一多这种方式基本等于自虐。更麻烦的是如果某台机器被入侵或者磁盘写满本地日志很可能被覆盖、被删除事后想追溯都找不到证据。日志服务器要解决的就是这个问题把所有机器的日志集中收集到一台机器上统一存储、统一检索、统一管理。这样即使某台业务机挂了日志还在即使有人想删本地日志远端那份还在。这就是所谓的“集中式日志管理”。标题里的方案是CentOS7 Rsyslog MySQL。拆开看CentOS7 是操作系统底座Rsyslog 是日志收集和转发的主力MySQL 负责把日志结构化落库方便后续用 SQL 查询和做报表。这套组合的优势在于——全部是系统自带或成熟稳定的组件不依赖复杂的大数据生态一台普通配置的机器就能跑起来特别适合中小规模的环境比如十几台到上百台服务器的场景。这篇文章适合谁看如果你是会一点 Linux 基础、能敲命令、看得懂配置文件但没搭过日志服务器的运维或开发那正好。我会把每一步为什么这么做、参数怎么算、坑在哪里都讲清楚你照着抄作业就能落地。如果你已经搭过也可以看看我在排查和优化部分踩过的坑说不定能帮你省几个小时。需要提前说明的是这套方案的核心思路是“客户端用 Rsyslog 转发服务端用 Rsyslog 接收并写入 MySQL”。中间不引入额外的消息队列或采集器链路短、故障点少但也意味着服务端 Rsyslog 的压力会大一些后面我会讲怎么评估和优化。2. 整体架构设计与选型考量2.1 数据流向长什么样先把整条链路在脑子里跑一遍后面配置才不会晕。数据流是这样的业务机上的应用或系统产生日志写入本地/var/log/下的文件同时 Rsyslog 作为系统日志服务会读取这些日志或者应用直接通过 syslog 协议发送然后根据配置的规则通过 TCP 或 UDP 把日志转发到日志服务器的 514 端口。日志服务器上的 Rsyslog 监听这个端口收到日志后调用 MySQL 输出模块把日志内容拆解成字段插入到数据库表中。这里有个关键点Rsyslog 本身既能当客户端也能当服务端同一套软件两种角色配置不同而已。这也是它比很多专用采集器轻量的原因。2.2 为什么选 TCP 而不是 UDPRsyslog 转发默认走 UDP配置简单但 UDP 是无连接的网络一抖动日志就丢了而且不保证顺序。对于要长期留存、可能要作为依据的日志丢日志是不可接受的。所以我强烈建议走 TCP。TCP 模式下Rsyslog 会维护连接有重试机制日志可靠性高很多。代价是服务端要处理更多连接状态资源消耗略高但这个代价完全值得。配置上客户端写表示 TCP服务端监听时也要显式指定 TCP 模块。注意如果日志量特别大比如每秒上万条单条 TCP 连接可能成为瓶颈这时候可以考虑多开几个端口或者用 RELP 协议Rsyslog 的可靠传输协议但那是进阶话题本文先聚焦基础方案。2.3 为什么落库用 MySQL 而不是直接写文件直接写文件当然可以/var/log/remote/下按主机名分目录简单直接。但文件方案的痛点在于检索你想查“昨天下午 3 点到 4 点之间某台机器上所有包含 error 的日志”用 grep 得遍历一堆文件慢且容易漏。落库之后一条 SQL 就搞定还能做聚合统计、生成报表、对接可视化工具。MySQL 的选型理由也很实际几乎每台服务器上都装过运维熟悉生态成熟中小数据量每天几百万条以内完全扛得住。当然如果日志量到了每天上亿条MySQL 的写入会成为瓶颈那时候该考虑 Elasticsearch 之类的方案但那是另一个量级的故事了。2.4 表结构设计的取舍日志表怎么设计直接决定了后续查询好不好用。最偷懒的做法是一张表两个字段时间 原始日志文本。能存但查询体验差。我的建议是至少拆出这几个字段接收时间、来源主机、设施facility、优先级priority、进程名、日志正文。这里有个权衡字段拆得越细写入时解析开销越大但查询越方便。对于大多数场景拆到“主机 时间 正文”这个粒度就够用了设施和优先级可以作为可选字段。后面实操部分我会给出一张兼顾性能和查询便利的表结构。3. 服务端环境准备与 MySQL 初始化3.1 系统基础配置假设你已经装好了一台 CentOS7最小化安装即可。先做几件基础的事关闭 SELinux或者配置放行但为了减少干扰测试环境建议先关、配置好静态 IP、确认时间同步正常。时间同步特别重要日志的时间戳如果各机器不一致集中之后根本没法按时间线排查问题。CentOS7 自带 chronyd确认它开着systemctl status chronyd timedatectl如果时间不对先timedatectl set-timezone Asia/Shanghai把时区设对再启动 chronyd 同步。关闭 SELinux 的临时和永久方法setenforce 0 sed -i s/^SELINUXenforcing/SELINUXdisabled/ /etc/selinux/config提示生产环境如果安全要求高不建议直接关 SELinux而是配置相应的策略放行 Rsyslog 和 MySQL 的端口与文件访问。但策略配置比较繁琐本文以功能实现为主先关掉减少变量。3.2 安装并初始化 MySQLCentOS7 默认源里的 MySQL 版本较老建议用官方源或者 MariaDB。这里以 MariaDB 为例兼容性好、安装简单yum install -y mariadb-server mariadb systemctl start mariadb systemctl enable mariadb mysql_secure_installationmysql_secure_installation会引导你设置 root 密码、删除匿名用户、禁止 root 远程登录等按提示走一遍安全性提升不少。接下来创建日志专用的数据库和用户。这里有个经验不要用 root 账号让 Rsyslog 连数据库权限太大一旦配置泄露风险高。单独建一个只有插入权限的账号CREATE DATABASE syslog CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER sysloguserlocalhost IDENTIFIED BY YourStrongPass123; GRANT INSERT ON syslog.* TO sysloguserlocalhost; FLUSH PRIVILEGES;注意这里只给了INSERT权限因为 Rsyslog 只需要写日志不需要读、改、删。最小权限原则能少给就少给。3.3 设计日志表结构建表语句如下我把它拆成了几个关键字段USE syslog; CREATE TABLE SystemEvents ( ID INT UNSIGNED NOT NULL AUTO_INCREMENT, ReceivedAt DATETIME NOT NULL, DeviceReportedTime DATETIME, FromHost VARCHAR(60), Facility SMALLINT, Priority SMALLINT, SysLogTag VARCHAR(60), Message TEXT, PRIMARY KEY (ID), INDEX idx_received (ReceivedAt), INDEX idx_host (FromHost) ) ENGINEInnoDB;字段解释一下ReceivedAt是服务端收到日志的时间DeviceReportedTime是日志本身携带的时间可能因为客户端时间不准而有偏差FromHost是来源主机Facility和Priority是 syslog 标准的设施和优先级数值SysLogTag一般是进程名Message是正文。索引建在ReceivedAt和FromHost上因为最常见的查询就是“某台机器某段时间的日志”。Message用 TEXT 类型不建索引因为全文索引在 MySQL 里对日志这种场景性价比不高真要做全文检索还是得上专门的工具。注意如果日志量很大单表会迅速膨胀到几千万行查询变慢。可以考虑按天分表或者定期归档旧数据。这个后面优化部分再展开。4. Rsyslog 服务端配置详解4.1 确认 Rsyslog 版本和模块支持CentOS7 自带的 Rsyslog 版本一般是 8.x已经支持 MySQL 输出模块。先确认rsyslogd -v输出里会列出编译时启用的模块找一下有没有ommysql。如果没有需要安装rsyslog-mysql包yum install -y rsyslog-mysql装完之后模块文件通常在/usr/lib64/rsyslog/ommysql.so。这个路径后面配置里要用到。4.2 加载模块与配置数据库连接Rsyslog 的配置在 CentOS7 下推荐放在/etc/rsyslog.d/目录里主配置文件/etc/rsyslog.conf会自动 include 这个目录下的.conf文件。我们新建一个/etc/rsyslog.d/mysql.confmodule(loadommysql) module(loadimtcp) input(typeimtcp port514)这三行的含义加载 MySQL 输出模块、加载 TCP 输入模块、监听 514 端口的 TCP 连接。注意imtcp是接收模块ommysql是输出模块一进一出。然后是动作规则把收到的日志写进数据库action(typeommysql serverlocalhost dbsyslog uidsysloguser pwdYourStrongPass123 templateMySQLTemplate)这里引用了MySQLTemplate需要先定义它。模板决定了日志怎么拆字段template(nameMySQLTemplate typestring stringINSERT INTO SystemEvents (ReceivedAt, DeviceReportedTime, FromHost, Facility, Priority, SysLogTag, Message) VALUES (%timereported:::date-mysql%, %timegenerated:::date-mysql%, %fromhost-ip%, %syslogfacility%, %syslogpriority%, %syslogtag%, %msg%) )模板里的%xxx%是 Rsyslog 的属性替换date-mysql会把时间格式化成 MySQL 认识的YYYY-MM-DD HH:MM:SS。%fromhost-ip%取的是来源 IP比主机名更可靠因为主机名可能解析不了。提示模板字符串里的单引号是 SQL 语法的一部分别漏了。另外%msg%里如果包含单引号会导致 SQL 语法错误这是个经典坑后面排查部分会讲怎么处理。4.3 防火墙与端口放行服务端要放行 514 端口的 TCPfirewall-cmd --permanent --add-port514/tcp firewall-cmd --reload如果客户端走 UDP还要加--add-port514/udp但前面说了我们走 TCP所以只放 TCP 即可。配置完成后重启 Rsyslogsystemctl restart rsyslog systemctl status rsyslog看状态是否 active有没有报错。如果启动失败多半是配置语法问题用rsyslogd -N1检查配置。5. 客户端配置与日志转发5.1 客户端最小配置客户端也是 CentOS7同样编辑/etc/rsyslog.d/下新建一个转发配置比如forward.conf*.* 192.168.1.100:514这一行的意思是所有设施、所有优先级的日志都通过 TCP 转发到 192.168.1.100 的 514 端口。是 TCP是 UDP别写错。但这样会把所有日志都发过去包括本机自己的 cron、mail 等量可能很大。更精细的做法是按设施过滤比如只转发local0到local7这些应用自定义的设施local0.* 192.168.1.100:514 local1.* 192.168.1.100:514具体转发哪些取决于你的应用用哪个设施打日志。系统日志auth、kern 等要不要转发看你的审计需求。5.2 客户端队列配置防止丢日志网络不可能永远通畅如果日志服务器重启或者网络抖动客户端还在拼命发日志就丢了。Rsyslog 支持磁盘队列把发不出去的日志先缓存到本地磁盘恢复后继续发action(typeomfwd target192.168.1.100 port514 protocoltcp queue.typeLinkedList queue.filenamefwdRule1 queue.maxdiskspace1g queue.saveonshutdownon action.resumeRetryCount-1)queue.typeLinkedList启用内存队列queue.filename指定磁盘队列文件名queue.maxdiskspace限制磁盘占用上限防止把客户端磁盘写满queue.saveonshutdownon表示关机时保存队列action.resumeRetryCount-1表示无限重试。这套配置下来即使日志服务器宕机几小时客户端也能把日志暂存本地恢复后补发。这是生产环境必备的可靠性配置。5.3 客户端时间与主机名规范前面强调过时间同步客户端也要确保 chronyd 正常运行。另外主机名最好规范比如web-01、db-02这种方便在日志服务器上按主机过滤。如果主机名乱起日志集中后你会分不清哪台是哪台。改主机名hostnamectl set-hostname web-01改完记得在/etc/hosts里加上对应记录避免解析问题。6. 联调验证与数据核对6.1 从客户端发一条测试日志配置都就绪后在客户端上手动发一条日志验证链路logger -p local0.info hello from client test 12345logger是系统自带的工具能往 syslog 里写日志。这条命令会以local0设施、info优先级写一条消息。然后到服务端查数据库SELECT * FROM SystemEvents ORDER BY ID DESC LIMIT 5;如果能看到这条日志说明整条链路通了。看不到就按下面的顺序排查客户端 Rsyslog 是否重启、防火墙是否放行、服务端 Rsyslog 是否监听 514、数据库连接是否正常。6.2 用 tcpdump 确认数据是否到达如果数据库里没有先确认日志有没有到服务端。在服务端抓包tcpdump -i any -nn port 514 -c 10然后在客户端再发一条日志看 tcpdump 有没有输出。有输出说明网络层通了问题在 Rsyslog 或数据库没输出说明网络或防火墙有问题。6.3 查看 Rsyslog 自身日志Rsyslog 处理过程中的错误会记到系统日志里journalctl -u rsyslog -n 50或者看/var/log/messages。常见的错误包括模块加载失败、数据库连接被拒、SQL 语法错误等。根据错误信息对症下药。7. 常见问题与排查技巧实录7.1 日志里有单引号导致插入失败这是最常见的坑。日志正文里如果包含单引号比如its an error拼出来的 SQL 就变成VALUES (its an error)语法直接错。Rsyslog 的模板替换默认不做转义。解决办法有两个。一是用%msg:::drop-last-lf%配合属性替换选项但转义单引号 Rsyslog 原生支持有限。更稳妥的做法是改用ommysql的预处理或者在模板里用%msg:::sp-if-no-1st-sp%这类选项。实测下来最省心的方案是升级到较新的 Rsyslog 版本它对 SQL 转义支持更好或者干脆把 Message 字段的插入改成用参数化方式——但 Rsyslog 模板不支持参数化。一个折中方案在模板里把单引号替换掉虽然损失了原始信息但至少不报错。或者接受这个限制在应用层避免日志里出现裸单引号。这个坑我踩过排查了半天才发现是日志内容的问题。7.2 数据库连接数被打满Rsyslog 默认可能为每条日志开一个数据库连接日志量大时 MySQL 的max_connections很快被打满报Too many connections。解决办法是在 action 里配置连接池和批量提交action(typeommysql serverlocalhost dbsyslog uidsysloguser pwdYourStrongPass123 templateMySQLTemplate action.resumeRetryCount-1 queue.typeLinkedList queue.size10000 queue.highwatermark8000 queue.lowwatermark2000 queue.discardmark9500 queue.timeoutEnqueue10)关键是队列配置让日志先在内存队列里攒着批量写库减少连接开销。同时调大 MySQL 的max_connections比如设到 500。7.3 日志量太大导致磁盘爆满MySQL 数据目录会随着日志增长迅速膨胀。假设每台机器每天产生 100MB 日志50 台机器就是 5GB/天一个月 150GB。磁盘不够就麻烦了。应对策略定期清理旧数据。可以写个定时任务每天删除 30 天前的记录DELETE FROM SystemEvents WHERE ReceivedAt DATE_SUB(NOW(), INTERVAL 30 DAY);但直接 DELETE 大量数据会锁表建议分批删或者用分区表按天分区直接 DROP PARTITION秒级完成。分区表是更优雅的方案建表时按ReceivedAt做 RANGE 分区。7.4 常见问题速查表现象可能原因排查方向数据库无数据网络不通tcpdump 抓包数据库无数据防火墙拦截检查 514 端口数据库无数据Rsyslog 未监听ss -tlnp插入报错SQL 语法错误看 journalctl 日志连接被拒账号密码错手动 mysql 登录测试连接数满未配队列加队列配置调大 max_connections磁盘满数据未清理加定时清理或分区7.5 独家避坑经验几个文档里不会写但实际很重要的点。第一先在小规模验证再铺开别一上来就改几十台客户端出问题不好回滚。第二客户端队列的磁盘空间要留够queue.maxdiskspace设太小队列满了照样丢日志。第三MySQL 的字符集要用 utf8mb4否则中文日志会乱码。第四定期检查 Rsyslog 的队列状态用rsyslogd -N1或者看/var/spool/rsyslog/下的队列文件大小队列持续积压说明服务端处理不过来。8. 性能优化与后续扩展方向8.1 写入性能优化MySQL 的写入瓶颈主要在磁盘 IO。几个优化手段把innodb_flush_log_at_trx_commit设为 2牺牲一点持久性换性能日志场景可接受、调大innodb_buffer_pool_size、用 SSD。另外 Rsyslog 端用批量提交减少事务次数。如果单台 MySQL 扛不住可以考虑分库分表或者引入消息队列做缓冲。但那是日志量到每天千万级才需要考虑的事大多数场景单机 MySQL 足够。8.2 查询与可视化数据落库后查询就自由了。常用的查询比如“查某台机器最近的错误日志”SELECT ReceivedAt, SysLogTag, Message FROM SystemEvents WHERE FromHost 192.168.1.50 AND Message LIKE %error% ORDER BY ReceivedAt DESC LIMIT 100;再进一步可以对接 Grafana 之类的可视化工具把日志量、错误率做成图表。MySQL 作为数据源Grafana 直接连就行。8.3 安全加固建议日志里可能包含敏感信息数据库要做好访问控制。除了前面说的最小权限账号还要限制数据库只监听本地或者内网。Rsyslog 的 514 端口也不要暴露到公网只在内网开放。如果客户端和服务端跨网段考虑加一层加密传输Rsyslog 支持 TLS配置稍复杂但安全性高。我在实际使用中发现日志服务器最容易被忽视的就是容量规划。上线前一定要估算日志量按“单机日增量 × 机器数 × 保留天数”算出所需磁盘再留一倍余量。我见过太多因为磁盘写满导致日志服务挂掉的案例事后追溯全泡汤。另外定期做一次恢复演练也很重要确认备份的日志真的能查、能用别等到真出事才发现备份是坏的。