腾讯云服务器日志排查:journalctl、dmesg 与系统日志怎么读
凌晨两点收到磁盘告警,登上去一看,/var/log 下某个应用日志单个文件已经涨到六十多 G——这种事我在腾讯云服务器上处理过不止一次。服务器排障的第一步往往不是打开集中式日志平台,而是先把本地日志读明白。 日志排查难的从来不是"找到日志文件在哪",而是先判断这件事被哪一层记录下来了:内核环缓冲、systemd journal、/var/log 下的传统文本文件,还是应用自己写的那份。层分错了,你在无关的噪声里翻上半小时也看不出名堂。下面按我平时上机的真实顺序,讲清楚腾讯云服务器日志排查在本地该怎么做,包括 journalctl、dmesg、logrotate 的实际用法,以及几个很多同行不会主动告诉你的坑。
先分清日志的四层,再决定翻哪个文件
Linux 上的日志不是"一个地方",而是四层,各自记的东西和有效期完全不同。
第一层是内核环缓冲(ring buffer)。内核自己产生的消息,比如硬件报错、网卡驱动、OOM Killer 的选择过程,先进这个环形缓冲区,dmesg 读的就是它,/dev/kmsg 是它的字符设备入口。它容量有限(默认通常几百 KB 到几 MB),重启就清空,旧消息被新消息覆盖,所以内核层的线索会过期不候,看到异常必须当场记下来。这个缓冲大小可以通过内核启动参数里的 log_buf_len 调大,但调大只是把覆盖推迟,解决不了留存问题——真正的留存还是得靠 journal 持久化,或者把 dmesg 定期落盘。
第二层是 systemd journal。现代系统(腾讯云上常见的 TencentOS、CentOS 7 及更新的版本、Ubuntu 16.04 之后都用 systemd)把内核、early boot、各服务的 stdout/stderr 统一收进 journal,用 journalctl 查询。默认情况下 journal 放在 /run/log/journal,也就是内存里,重启即丢;要留存得手动开持久化。
第三层是 /var/log 下的传统文本文件。rsyslog/syslog-ng 会把 journal 的内容转发到 /var/log/messages、/var/log/secure(Debian/Ubuntu 系叫 auth.log)、/var/log/cron 等。很多人以为这些文件是"原始来源",其实在 systemd 系统上它们往往是 journal 的一份副本。
第四层是应用日志。Nginx、MySQL、业务程序自己写的那份,格式、切分、保留策略全看应用配置,也是最容易失控、把磁盘写爆的一层。
分清这四层,排查时就能按症状直接定位:整机重启、驱动异常看第一层;服务启动失败、进程崩溃看第二层;登录、su/sudo、定时任务看第三层;接口报错、慢请求看第四层。
journalctl 的常用参数与持久化设置
journalctl 的参数记不住不用全背,先把下面这几个用熟:-u sshd 只看某个 unit,等价于过滤 _SYSTEMD_UNIT 字段;-b 只看本次启动,-b -1 看上一次启动——机器刚重启过、想知道"上次为什么挂",journalctl -b -1 -p err 基本是第一命令;--since "2026-10-06 09:00" --until "2026-10-06 09:30" 按时间窗切;-p err 只看 error 与更严重的级别(级别从 emerg 一路到 debug);-k 只看内核消息,效果接近 dmesg;-f 实时跟随,相当于 tail -f;-o json-pretty 输出结构化字段,方便配合 jq 过滤;--disk-usage 直接告诉你 journal 占了多少空间。
持久化必须手动开,这是最容易被忽略的一步。默认内存存放,重启就没。改 /etc/systemd/journald.conf,把 Storage 设为 persistent(或者 auto),再设 SystemMaxUse=2G、SystemMaxFileSize=200M 控制上限,然后 systemctl restart systemd-journald。auto 的含义是"有 /var/log/journal 目录就持久化,没有就只放内存",所以目录得自己 mkdir -p /var/log/journal 并执行 systemd-tmpfiles --create --prefix /var/log/journal。很多人以为自己一直在查历史日志,其实重启后 journal 早就清空了,这是排查时最容易被误导的一点。 空间不够时用 journalctl --vacuum-size=1G 或 --vacuum-time=7d 主动回收,别等它撑爆系统盘。
还有几个小技巧很省时间:journalctl -n 100 只看最后 100 行;--no-pager 不进分页器,方便直接重定向到文件带走;journalctl _PID=1234 按进程号精确捞;--identifier=myapp 按 syslog 标识过滤。几万行的日志里肉眼一定会漏,排查完用 grep -c 统计命中条数,比翻屏靠谱得多。
OOM Killer、硬件错误和 TCP 异常在内核日志里的样子
内核日志里有几类特征属于"教科书级",认得出就能秒判方向。
内存被耗尽触发 OOM Killer 时,dmesg -T 或 journalctl -k 里会出现 Out of memory: Killed process 12345 (java),以及 invoked oom-killer、oom-kill:constraint=... 这类行,还会打印各进程的 rss 和 oom_score_adj,告诉你为什么偏偏杀了这个进程。我经手过的一个客户,Java 应用隔几天就"自己重启",其实是被 OOM 杀了——-Xmx 设得比实例内存还大,没给堆外内存和系统缓存留余量。这类问题在容器里更隐蔽,要靠应用感知容器内存上限来规避。
硬件层面,内存可纠正/不可纠正错误会以 mce:、EDAC 开头;磁盘故障常见 blk_update_request: I/O error, dev vdb、EXT4-fs error、Buffer I/O error。腾讯云 CVM 的系统盘和数据盘都是云硬盘 CBS 映射的虚拟设备(vda、vdb),看到 I/O error 先别急着怀疑自己的代码,确认是不是底层盘异常,必要时提工单看云平台侧的盘状态更靠谱。
网络相关,TCP: request_sock_TCP: Possible SYN flooding on port 80 说明半连接队列被打满;nf_conntrack: table full, dropping packet 是连接跟踪表满了,常见于代理或 NAT 场景。另外 soft lockup、hung_task、blocked for more than 120 seconds 表示进程长时间拿不到 CPU 或卡在 I/O,往往指向存储或内核问题,而不是业务逻辑。
顺带说下日志级别的映射,很多人会被绕晕:内核和 syslog 从高到低是 emerg、alert、crit、err、warning、notice、info、debug,journalctl -p 接受的就是这套名字,也接受数字 0 到 7。你写 -p warning 时会把 err、crit 一起带出来,因为它按"严重程度不低于"过滤,不是精确匹配某一级。这个语义搞反,排查时就会漏掉关键的 crit 行。
日志把磁盘吃满:logrotate、journal 容量与轻量实例的现实
"日志把磁盘吃满"是我见过最高频的线上事故之一,定位却最简单:df -h 看到某个分区 100%,再用 du -sh /var/log/* | sort -h 一排序,元凶立刻现身。真正的问题是很多人不知道怎么让它别再发生。
logrotate 是标准答案。主配置 /etc/logrotate.conf,每个应用可以在 /etc/logrotate.d/ 下放自己的规则。关键参数:daily/weekly 按周期切,size 100M 按大小切,rotate 7 保留几份,compress 压缩旧文件,missingok 文件不在也不报错,copytruncate 用于不支持重新打开句柄的程序(先拷贝再清空,会有极短的丢日志窗口)。最容易踩的坑是 postrotate 里的重载没生效: Nginx 日志切了但进程还抓着旧文件句柄,磁盘空间不会释放,得在 postrotate ... nginx -s reopen ... endscript 里让它重新打开,或者干脆用 copytruncate。另外 logrotate 本身由 systemd timer 驱动(logrotate.timer),如果这个 timer 被禁用、或者机器长期关机错过了触发时刻,切分就不会执行——这跟 crontab 错过执行是同一个道理,用 systemctl list-timers 能看它下次什么时候跑。
journal 那边同理,用 SystemMaxUse 兜底,别让它在系统盘上无限膨胀。这里要点名一下轻量应用服务器:它的系统盘通常比 CVM 小,默认还没有独立数据盘,日志、应用、镜像全挤在同一块盘上。在轻量应用服务器上做日志必须一开始就设好切分和上限,否则磁盘写满是迟早的事。稳妥做法是把 /var/log 单独挂一块云硬盘 CBS,或者通过软链接把日志目录挪到数据盘,再配合 logrotate。
还有一个隐蔽点:有些服务把日志写进自己的目录,比如 /var/log/nginx/、/var/log/mysql/,这些往往不在系统默认规则覆盖范围内,得单独配。判断某份日志有没有被管,最简单的办法是 ls -l 看它的修改时间和体积,再去 /etc/logrotate.d/ 找有没有对应条目。配好后先用 logrotate -d /etc/logrotate.d/xxx 做 dry-run,只打印不执行,确认规则真能匹配上,再放到生产环境。把 logrotate 跑通之后,最好再配一条磁盘使用率告警,比如使用率超过 80% 就提前通知,比等到写满再手忙脚乱从容得多。
时间戳错位、不该入日志的信息,以及本地排查顺序
时间戳错位是另一个"看不见的坑"。宿主机和容器时区不一致时,容器里 date 显示 UTC,日志时间戳比北京时间差 8 小时,你按告警时间去翻,永远差那么一截。排查时先用 timedatectl 看系统时区,journalctl --utc 强制按 UTC 输出便于对齐,容器记得挂载 /etc/localtime 或设置 TZ 环境变量。日志时间戳被应用按本地时区格式化、而 journal 按 UTC 记录,两者混着看很容易误判先后顺序。
有些信息坚决不能进日志:明文密码、口令、访问密钥(SecretId/SecretKey)、完整的会话 token、身份证号、银行卡号、手机号全量。日志一旦被采集、投递到对象存储或被别人拉走,就是一次数据泄露。合规上,等保 2.0 对日志留存有明确要求,业务日志和审计日志要分开存放,别在业务日志里写敏感字段再指望"没人看"。需要关联信息时用脱敏或哈希后的标识代替。
本地排查的顺序,我自己的习惯是:先确认时间点——故障或告警发生的确切时刻,最好带时区;再看内核层 dmesg -T 有没有硬件、OOM、驱动异常;接着按 journalctl -b -p err 看服务崩溃与启动失败;然后翻对应应用的日志文件;最后才做复现和抓包。顺序反过来先看业务日志,往往会在表象里绕圈。 只有在本地这几步都做完、确认线索不够,才值得上集中式采集,比如把 journal 和业务
