VPSKnow

Linux 系统日志分析与故障排查完全指南

中级
40分钟

日志是服务器的"黑匣子"——每一次攻击尝试、每一次服务崩溃、每一次 OOM 内存爆炸,都在日志里留下了痕迹。本指南将带您从零掌握 Linux 日志体系,学会用 awk/grep 提取关键信息,并通过真实案例演示五大高频故障的完整排查流程。

🗂️ Linux 日志体系全貌

Linux 有两套并行的日志系统,理解它们的关系是高效排错的基础:

📔

systemd Journal

journalctl 命令访问

现代 Linux(systemd 管理的服务)的统一日志中心。以二进制格式存储在 /var/log/journal/,支持结构化查询、按优先级过滤、跨重启查询。Ubuntu 16.04+、Debian 9+ 均默认使用。

📁

传统 syslog 文件

/var/log/ 目录文本文件

由 rsyslog/syslog-ng 写入的纯文本日志文件。可直接用 catgrepawk 处理,无需特殊工具,也是 Nginx、MySQL 等应用程序的默认输出目标。

💡 两套系统的关系: 默认配置下,systemd 日志既写入 Journal(journalctl 可查),也通过 rsyslog 转发到 /var/log/syslog排查时两个都要看:systemd 服务用 journalctl -u 服务名,攻击分析和流量统计用 grep/awk 处理文本文件。

日志文件速查手册

日志文件路径 适用发行版 记录内容
/var/log/syslog Debian/Ubuntu 系统主日志,内核消息、服务启停、cron 执行记录
/var/log/messages CentOS/RHEL 等同于 syslog,系统综合日志
/var/log/auth.log Debian/Ubuntu 所有认证事件:SSH 登录、sudo、PAM(排查入侵首选)
/var/log/secure CentOS/RHEL 等同于 auth.log
/var/log/kern.log 通用 内核消息:硬件错误、驱动崩溃、OOM killer 触发记录
/var/log/dmesg 通用 系统启动时的内核环形缓冲区,用 dmesg 命令读取
/var/log/boot.log 通用 系统启动过程中各服务的启动状态
/var/log/dpkg.log Debian/Ubuntu apt/dpkg 软件包安装、升级、删除的完整记录
/var/log/nginx/access.log Nginx 每一条 HTTP 请求记录:IP、时间、路径、状态码、耗时
/var/log/nginx/error.log Nginx Nginx 错误与警告:502、403、配置问题等
/var/log/mysql/error.log MySQL MySQL 启动错误、崩溃恢复、连接拒绝等
/var/log/fail2ban.log Fail2ban Fail2ban 触发封禁的 IP 和 jail 名称记录

📔 journalctl:systemd 统一日志中心

journalctl 是查询 systemd Journal 的核心命令。排查服务问题时,它应该是您打开的第一个工具——特别是 journalctl -b -1 -p err,这是排查意外重启后的首选命令。

journalctl 完整速查
# ── 基础查看 ────────────────────────────────────────────────────────────────
journalctl                          # 全部日志(从最旧开始,按 q 退出)
journalctl -r                       # 倒序显示(最新在最前,排查时最常用)
journalctl -f                       # 实时追踪(类似 tail -f,Ctrl+C 退出)
journalctl -n 100                   # 仅显示最近 100 行
journalctl --no-pager               # 不分页,直接输出(方便管道处理)

# ── 按服务过滤 ──────────────────────────────────────────────────────────────
journalctl -u nginx                 # 只看 nginx 服务的日志
journalctl -u nginx -u mysql        # 同时查看 nginx 和 mysql
journalctl -u nginx -f              # 实时追踪 nginx 日志
journalctl -u nginx --since today   # 今天的 nginx 日志

# ── 按时间过滤 ──────────────────────────────────────────────────────────────
journalctl --since "2026-03-16 08:00:00"
journalctl --since "2026-03-16 08:00" --until "2026-03-16 10:00"
journalctl --since "1 hour ago"
journalctl --since yesterday

# ── 按优先级过滤(0=emerg 1=alert 2=crit 3=err 4=warn 5=notice 6=info 7=debug)
journalctl -p err                   # 只看错误及以上级别
journalctl -p warning -u nginx      # nginx 的警告及以上
journalctl -p 0..3                  # emerg 到 err 级别

# ── 查看上次启动日志(排查死机/重启原因)──────────────────────────────────
journalctl -b                       # 本次启动的所有日志
journalctl -b -1                    # 上一次启动的日志
journalctl -b -1 -p err             # 上次启动中的错误日志(排查崩溃首选)
journalctl --list-boots             # 列出所有可查询的启动记录

# ── 磁盘占用管理 ────────────────────────────────────────────────────────────
journalctl --disk-usage             # 查看 journal 占用的磁盘空间
journalctl --vacuum-time=30d        # 删除 30 天前的日志
journalctl --vacuum-size=500M       # 将日志总量控制在 500MB 以内
journalctl -b -1 -p err
排查上次崩溃
意外重启后第一个要跑的命令
journalctl -u nginx -f
实时追踪服务
服务异常时实时观察日志流
journalctl --vacuum-time=30d
清理旧日志
释放被 journal 占用的磁盘

📁 传统日志文件速查手册

/var/log/ 下的传统文本日志在安全分析和攻击溯源方面依然不可替代——grep/awk 直接处理文本,速度极快且无需学习特殊工具。

auth.log:排查 SSH 暴力破解与入侵

/var/log/auth.log 分析命令
# ── auth.log:排查 SSH 暴力破解与入侵 ────────────────────────────────────────

# 查看最近的 SSH 认证失败记录
grep "Failed password" /var/log/auth.log | tail -50

# 统计攻击来源 IP(按攻击次数降序排列)
grep "Failed password" /var/log/auth.log \
  | awk '{print $(NF-3)}' \
  | sort | uniq -c | sort -rn \
  | head -20
# 输出示例:
#   3842 185.220.101.47    <- Tor 出口节点,字典攻击
#    127 45.33.32.156
#     89 103.21.244.12

# 查看成功登录记录(排查是否有未授权登录)
grep "Accepted" /var/log/auth.log | tail -20

# 查看 sudo 使用记录
grep "sudo" /var/log/auth.log | grep "COMMAND" | tail -20

# 实时监控认证日志
tail -f /var/log/auth.log | grep --line-buffered "Failed\|Accepted\|Invalid"

🚨 真实攻击溯源示例

# 统计攻击来源 IP,发现异常:

3842 185.220.101.47 ← Tor 出口节点,字典攻击 3842 次

127 45.33.32.156 ← Shodan 扫描器

# 查看成功登录记录(关键:确认无未授权登录)

Accepted publickey for ubuntu from 192.168.1.100 port 52341

# ✅ 只有自己的 IP 成功登录,3842 次攻击全部失败

kern.log / dmesg:排查 OOM 与硬件错误

/var/log/kern.log + dmesg
# ── kern.log:排查 OOM(内存不足杀进程)──────────────────────────────────────

# 搜索 OOM killer 触发记录
grep -i "oom\|out of memory\|kill process" /var/log/kern.log | tail -30
# 或用 journalctl:
journalctl -k | grep -i "oom\|out of memory" | tail -30

# OOM 日志示例:
# Mar 16 03:42:17 vps01 kernel: Out of memory: Kill process 12847 (php-fpm) score 892
# Mar 16 03:42:17 vps01 kernel: Killed process 12847 (php-fpm) total-vm:524288kB, anon-rss:418764kB

# 查看当前内存使用详情
free -h
# 查看各进程内存占用排行
ps aux --sort=-%mem | head -15

# ── dmesg:排查硬件错误 ──────────────────────────────────────────────────────
dmesg -T                               # 显示带时间戳的内核消息
dmesg -T | grep -i "error\|fail\|warn" | tail -30
dmesg -T | grep -i "disk\|sda\|nvme"  # 排查磁盘 I/O 错误
dmesg -T | grep -i "oom\|memory"       # 内存相关内核消息

# ── 查看系统崩溃/重启原因 ──────────────────────────────────────────────────
last reboot | head -10              # 最近的重启记录
last -x | grep shutdown | head -5   # 最近的关机记录

🔬 实战:awk/grep/sed/cut 日志分析四件套

这四个工具是 Linux 日志分析的核心利器,无需安装依赖,掌握它们能解决 90% 的日志分析需求:

🔍 grep 关键词过滤

从大量日志中快速找到包含特定关键词的行,支持正则表达式。

grep "ERROR" /var/log/nginx/error.log 找所有 ERROR 行
grep -v "GET /favicon" access.log -v 反向过滤(排除某类日志)
grep -c "Failed" auth.log -c 只统计匹配行数
grep -r "exception" /var/log/app/ -r 递归搜索目录
grep -E "404|500|502" access.log -E 扩展正则,多关键词
✂️ awk 字段提取与统计

按列提取字段,进行统计计数。Nginx 日志分析的核心工具。

awk '{print $1}' access.log 打印第 1 列(IP 地址)
awk '{print $9}' access.log | sort | uniq -c 统计状态码分布
awk '$9==502' access.log 过滤状态码为 502 的行
awk '{sum+=$10} END{print sum}' access.log 累加第 10 列(流量统计)
awk 'NR>=100 && NR<=200' access.log 只打印第 100-200 行
✏️ sed 文本替换与提取

流式文本编辑,常用于日志格式转换和敏感信息脱敏。

sed -n '100,200p' access.log -n + p:打印指定行范围
sed 's/password=[^ ]*/password=***/' auth.log 脱敏密码字段
sed '/healthcheck/d' access.log 删除含关键词的行
sed -n '/03:00/,/04:00/p' syslog 提取时间范围内的日志
sed 's/\[.*\]//' error.log 删除方括号内的时间戳
🗡️ cut 按分隔符切列

按指定分隔符快速提取某一列,比 awk 简单直接。

cut -d' ' -f1 access.log -d 指定分隔符,-f 指定列号
cut -d'"' -f2 access.log 提取引号内的请求行
cut -d':' -f1 /etc/passwd 提取系统用户名列表
cut -c1-15 syslog -c 按字符位置,提取时间戳
cut -d' ' -f1,4,9 access.log 同时提取第 1、4、9 列

🌐 实战:Nginx 日志深度分析

Nginx 访问日志是运维工作中频率最高的日志分析对象——网站变慢了、被攻击了、出现 502 了,第一个要看的都是它。

理解 Nginx 日志格式

log_format 字段说明
# Nginx 默认日志格式字段说明(/etc/nginx/nginx.conf 中 log_format)
# 字段顺序:IP  -  用户  [时间]  "方法 路径 协议"  状态码  字节数  "Referer"  "UA"
#
# 示例行:
# 185.220.101.47 - - [16/Mar/2026:03:42:17 +0000] "GET /wp-login.php HTTP/1.1" 404 162 "-" "Mozilla/5.0"
# 103.21.244.12  - - [16/Mar/2026:03:42:19 +0000] "POST /xmlrpc.php HTTP/1.1" 403 144 "-" "python-requests/2.28"

# ── 推荐开启 JSON 格式日志(更易于程序化分析)────────────────────────────────
# 在 nginx.conf http{} 块中添加(注意 Astro 不解析此处,直接使用即可):
#
# log_format json_combined escape=json
#   '{'
#     '"time":"$time_iso8601",'
#     '"remote_addr":"$remote_addr",'
#     '"method":"$request_method",'
#     '"uri":"$request_uri",'
#     '"status":$status,'
#     '"request_time":$request_time,'
#     '"upstream_time":"$upstream_response_time"'
#   '}';
# access_log /var/log/nginx/access.log json_combined;

access.log 常用分析命令

/var/log/nginx/access.log 分析
# ── Nginx access.log 常用分析命令 ──────────────────────────────────────────

# 1. 统计访问量最高的 10 个 IP
awk '{print $1}' /var/log/nginx/access.log \
  | sort | uniq -c | sort -rn | head -10

# 2. 统计访问最多的 URL(排除静态资源)
awk '{print $7}' /var/log/nginx/access.log \
  | grep -vE '\.(css|js|png|jpg|ico|woff)$' \
  | sort | uniq -c | sort -rn | head -20

# 3. 统计各 HTTP 状态码数量(判断服务整体健康)
awk '{print $9}' /var/log/nginx/access.log \
  | sort | uniq -c | sort -rn
# 若 502 大量出现 -> PHP-FPM/后端服务崩溃
# 若 499 大量出现 -> 客户端主动断开(CC 攻击特征)

# 4. 找出响应时间最慢的请求(需日志含 request_time 字段)
awk '{print $NF, $7}' /var/log/nginx/access.log \
  | sort -rn | head -20

# 5. 统计某个 IP 今天的请求次数
grep "^1.2.3.4 " /var/log/nginx/access.log | wc -l

# 6. 实时监控 502 错误
tail -f /var/log/nginx/access.log | grep --line-buffered " 502 "

# 7. 分析某个被封 IP 都访问了哪些路径
BANNED_IP="185.220.101.47"
grep "^${BANNED_IP} " /var/log/nginx/access.log \
  | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
状态码 含义 大量出现时的排查方向
200 成功 正常,无需关注
403 禁止访问 大量 403 到敏感路径 = 漏洞扫描,考虑 Fail2ban 封锁
404 未找到 大量 404 到 wp-login.php 等 = 漏洞扫描
499 客户端主动断开 大量 499 = CC 攻击特征,客户端发完请求立即断开
502 网关错误 后端服务(PHP-FPM/应用)崩溃,查后端日志
503 服务不可用 后端过载或全部失效,检查后端状态
504 网关超时 后端响应太慢,检查慢查询或应用代码性能

🐢 实战:MySQL 慢查询日志

数据库查询慢是导致网站响应迟缓的最常见原因之一,慢查询日志记录了每一条执行超过阈值时间的 SQL,以及扫描了多少行——通过它能精准定位缺少索引的问题查询。

MySQL 慢查询日志配置与分析
# ── 开启 MySQL 慢查询日志(在线热开启,无需重启)────────────────────────────
# 登录 MySQL 后执行:
# SET GLOBAL slow_query_log = ON;
# SET GLOBAL long_query_time = 1;                    -- 超过 1 秒记录
# SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
# SET GLOBAL log_queries_not_using_indexes = ON;     -- 记录未用索引的查询

# 或在 /etc/mysql/mysql.conf.d/mysqld.cnf 中持久化:
# [mysqld]
# slow_query_log      = 1
# slow_query_log_file = /var/log/mysql/slow.log
# long_query_time     = 1
# log_queries_not_using_indexes = 1

# ── 分析慢查询日志 ──────────────────────────────────────────────────────────
# 使用 mysqldumpslow(MySQL 官方自带工具)
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# -s t  按总执行时间排序
# -t 10 只显示 Top 10

# 慢查询日志示例:
# Time: 2026-03-16T03:42:17.123456Z
# Query_time: 4.523891  Lock_time: 0.000102  Rows_sent: 1  Rows_examined: 2847361
# SELECT * FROM orders WHERE user_id = 12345;
# 扫描 284 万行只返回 1 行 -> 缺少 user_id 索引!

# 用 pt-query-digest(分析更详细)
apt install percona-toolkit -y
pt-query-digest /var/log/mysql/slow.log | head -100

🚨 五大高频故障排查全流程

以下是 VPS 运维中最常遇到的五类故障,每个都附有完整的排查命令序列——按顺序执行即可系统性定位问题根因:

💥

故障①:服务器突然重启 / OOM 内存爆炸

现象:VPS 突然断线,重连后 uptime 只有几分钟,不知道发生了什么

排查命令序列
# 故障场景①:服务器突然重启,不知道原因

# 步骤1:确认是否意外重启
last reboot | head -5

# 步骤2:查看上次启动前的最后日志
journalctl -b -1 -p err --no-pager | tail -50
# 关注:
#   "Out of memory: Kill process"  -> OOM,内存耗尽
#   "Kernel panic"                 -> 内核崩溃
#   "EXT4-fs error"                -> 磁盘文件系统损坏

# 步骤3:若是 OOM,找出是哪个进程消耗了内存
journalctl -b -1 -k | grep -i "oom\|killed process"
# 示例输出:
# kernel: Killed process 8472 (java) total-vm:4194304kB, anon-rss:3876000kB
# -> Java 进程用了约 3.8GB 物理内存后被 OOM killer 终止

# 步骤4:防止下次 OOM
# 策略A:为服务设置内存上限(在 systemd service 文件 [Service] 段中):
# MemoryMax=512M
# MemorySwapMax=0

# 策略B:增加 swap 空间
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
💿

故障②:磁盘空间耗尽,服务写入失败

现象:数据库写入失败、Nginx 无法记录日志、Docker 无法启动新容器

排查命令序列
# 故障场景②:磁盘空间满了,服务写入失败

# 步骤1:确认磁盘使用情况
df -h

# 步骤2:找出占用空间最大的目录
du -sh /* 2>/dev/null | sort -rh | head -15
du -sh /var/log/* 2>/dev/null | sort -rh | head -10

# 步骤3:常见空间杀手与清理方法

# A. 日志文件膨胀(最常见)
ls -lh /var/log/nginx/*.log
truncate -s 0 /var/log/nginx/access.log   # 清空但保留文件

# B. apt 缓存堆积
du -sh /var/cache/apt/
apt clean && apt autoremove -y

# C. Docker 垃圾
docker system df
docker system prune -af --volumes   # 危险!删除所有未使用镜像和数据卷

# D. 旧内核(Ubuntu 常见)
dpkg --list | grep linux-image
apt autoremove --purge -y

# E. 全局大文件查找(超过 100MB)
find / -type f -size +100M 2>/dev/null | xargs ls -lh | sort -k5 -rh | head -20

# 步骤4:df 有空间但 du 统计不到(已删除但被占用的文件)
lsof | grep deleted | head -20
systemctl restart nginx mysql   # 重启服务释放幽灵文件占用
🔐

故障③:SSH 无法连接,被锁在门外

现象:SSH 连接超时或被拒绝,服务器黑屏,不知道从何下手

排查命令序列
# 故障场景③:SSH 无法连接,排查与恢复

# ── 诊断阶段(从本地机器执行)───────────────────────────────────────────────

# 测试1:服务器是否在线
ping -c 5 服务器IP

# 测试2:SSH 端口是否开放
nc -zv 服务器IP 22

# 测试3:SSH 连接详细调试
ssh -vvv user@服务器IP -p 22

# ── 常见原因与解决(通过 VPS 控制台 VNC/Console 操作)───────────────────────

# 原因A:防火墙误封 SSH 端口
ufw allow 22/tcp && ufw reload
# 或:iptables -I INPUT -p tcp --dport 22 -j ACCEPT

# 原因B:Fail2ban 误封了您的 IP
fail2ban-client status sshd                    # 查看被封 IP 列表
fail2ban-client set sshd unbanip 您的IP        # 解封

# 原因C:SSH 服务崩溃
systemctl status sshd
systemctl restart sshd
journalctl -u sshd -n 50

# 原因D:磁盘满导致 sshd 无法写入 pid 文件
# 先清理磁盘(见故障②),再重启 sshd

# ── 预防措施 ────────────────────────────────────────────────────────────────
# 修改防火墙前先设置自动回滚保险:
echo "ufw disable" | at now + 5 minutes
# 5 分钟后自动关闭 UFW,确保不会永久锁死
⚠️

故障④:网站出现 502 Bad Gateway

现象:访问网站显示 502,Nginx 在运行但网页打不开

排查命令序列
# 故障场景④:网站出现 502 Bad Gateway

# 步骤1:确认 Nginx 是否在运行
systemctl status nginx

# 步骤2:查看 Nginx error.log(502 的直接原因)
tail -50 /var/log/nginx/error.log
# 常见错误:
#   "connect() failed (111: Connection refused)"    -> 后端服务没在监听
#   "no live upstreams while connecting"            -> 所有 upstream 都挂了
#   "upstream timed out (110: Connection timed out)"-> 后端响应超时

# 步骤3:检查后端服务状态
systemctl status php8.3-fpm
journalctl -u php8.3-fpm -n 30 --no-pager

# 步骤4:确认后端端口是否在监听
ss -tlnp | grep ':9000'   # PHP-FPM 默认端口
ss -tlnp | grep ':3000'   # Node.js 常见端口

# 步骤5:检查系统资源(可能是 OOM 触发了 PHP-FPM 被杀)
free -h && journalctl -k | grep -i "oom" | tail -10
🔥

故障⑤:CPU 突然飙升至 100%

现象:服务器响应极慢,SSH 卡顿,top 显示 CPU 接近满载

排查命令序列
# 故障场景⑤:CPU 突然飙升至 100%

# 步骤1:快速定位占用 CPU 最多的进程
top -b -n 1 | head -20
# 或:
ps aux --sort=-%cpu | head -15

# 步骤2:查看该进程在做什么(以 PID 12345 为例)
ls -la /proc/12345/fd            # 打开的文件描述符
cat /proc/12345/cmdline | tr '\0' ' '  # 完整启动命令

# 步骤3:若是 Nginx worker 占满 CPU(CC 攻击特征)
tail -100 /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -5
# 找到攻击 IP 后立即封禁:
ufw deny from 攻击IP

# 步骤4:检查是否有挖矿程序(CPU 100% 且进程名可疑)
ps aux | grep -v "\[" | sort -k3 -rn | head -10
# 若发现陌生进程,检查其网络连接:
ss -tlnp | grep PID
netstat -anp | grep PID

# 步骤5:检查 cron 任务是否触发高负载
crontab -l
cat /etc/cron.d/*

📊 进阶:GoAccess 与 lnav 可视化工具

纯命令行分析功能强但不直观。GoAccess 提供流量全局概览,lnav 提供跨文件交互式排查——两者互补,覆盖不同场景:

📈

GoAccess

实时 Nginx/Apache 流量可视化仪表板,支持终端 TUI 和独立 HTML 报告。能直观看到访客地图、Top URL、Top IP、状态码分布,无需搭建 ELK 等复杂系统。

🔎

lnav

交互式多文件日志浏览器,自动按时间合并多个日志、高亮错误行、支持正则搜索和 SQL 查询。排查跨服务的复杂故障极为高效。

GoAccess 安装与使用

GoAccess
# ── GoAccess:Nginx 日志实时可视化仪表板 ──────────────────────────────────

# 安装(Ubuntu/Debian)
apt install goaccess -y

# 终端实时查看(TUI 模式)
goaccess /var/log/nginx/access.log --log-format=COMBINED

# 生成独立 HTML 报告
goaccess /var/log/nginx/access.log \
  --log-format=COMBINED \
  -o /tmp/nginx-report.html
# scp 到本地查看:scp user@服务器IP:/tmp/nginx-report.html ~/Desktop/

# 分析历史压缩日志(支持通配符合并分析)
zcat /var/log/nginx/access.log.*.gz | \
  goaccess - --log-format=COMBINED -o /tmp/history-report.html

# 常用面板(终端模式按数字键切换):
#   1 - 访客统计(Unique Visitors)
#   2 - 请求路径(Requested Files)
#   4 - 404 错误(Not Found)
#   5 - 来源国家(GeoLocation)
#   8 - 时间分布(Visit Time)

lnav 安装与使用

lnav
# ── lnav:交互式多文件日志浏览器 ──────────────────────────────────────────

# 安装
apt install lnav -y

# 同时查看多个日志文件(按时间交错合并显示)
lnav /var/log/auth.log /var/log/syslog /var/log/nginx/error.log

# 打开 systemd journal
lnav /var/log/journal/

# ── lnav 内部快捷键 ──────────────────────────────────────────────────────────
# /pattern    正则搜索
# n / N       下一个/上一个匹配
# e / E       跳到下一个/上一个错误行(自动识别 ERROR/WARN)
# q           退出
# TAB         切换文件面板
# :           打开命令模式(可执行 SQL 查询!)

# lnav SQL 查询示例(: 提示符后输入):
# SELECT cs_ip, count(*) as cnt FROM access_log GROUP BY cs_ip ORDER BY cnt DESC LIMIT 10;
# SELECT * FROM syslog WHERE log_level = 'error' ORDER BY log_time DESC LIMIT 50;

🏗️ 进阶:rsyslog 集中式日志汇聚

当您管理多台 VPS 时,逐台 SSH 查日志效率极低。通过 rsyslog 将所有服务器的日志汇聚到一台"日志中心",可以在一处搜索全部机器的历史记录。

/etc/rsyslog.conf(日志汇聚服务端)
# ── rsyslog 服务端配置(日志汇聚中心)────────────────────────────────────

# 编辑 /etc/rsyslog.conf,开启 TCP 监听(比 UDP 更可靠):
# module(load="imtcp")
# input(type="imtcp" port="514")

# 按来源主机分目录存储日志(/etc/rsyslog.d/remote.conf):
# template(name="RemoteLogs" type="string"
#   string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log")
# *.* ?RemoteLogs

# 重启服务并开放端口
systemctl restart rsyslog
ufw allow 514/tcp
/etc/rsyslog.conf(各 VPS 节点客户端)
# ── rsyslog 客户端配置(各 VPS 节点)──────────────────────────────────────
# 在 /etc/rsyslog.conf 末尾添加(将 LOG_SERVER_IP 替换为日志服务器 IP):

# TCP 转发(推荐,不丢日志):
# *.* @@LOG_SERVER_IP:514

# 重启使配置生效
systemctl restart rsyslog

# 验证:在日志服务器上查看是否收到客户端日志
# tail -f /var/log/remote/client-hostname/syslog.log
方案 复杂度 搜索能力 适用规模
rsyslog 转发 ⭐ 极简 grep/awk 文本搜索 1-10 台
Loki + Grafana ⭐⭐ 中等 LogQL 标签查询,有 UI 5-50 台
Graylog ⭐⭐⭐ 较复杂 全文搜索,告警集成 10-100 台
ELK Stack ⭐⭐⭐⭐ 复杂 全文索引,Kibana 可视化 50 台以上

对于 1-5 台 VPS 的个人站长,rsyslog 是零成本、零运维的最优解。

常见问题解答

journalctl 和 /var/log/syslog 记录的内容一样吗?该看哪个?

大部分内容相同,但不完全一样。默认配置下,systemd Journal 的日志会通过 rsyslog 转发写入 /var/log/syslog,因此高度重合。差别:Journal 额外记录了进程的完整元数据(PID、UID、cgroup),syslog 文本文件不含这些;某些服务直接写 stdout/stderr 且不经 syslog,只在 Journal 有记录;而应用程序(Nginx、MySQL)则直接写文本文件,Journal 不含这些。推荐策略:排查 systemd 服务(Nginx、sshd)用 journalctl -u 服务名;分析攻击流量、统计日志用 grep/awk 处理文本文件。两者互补,不必选一。

/var/log/journal 占用大量磁盘空间,如何安全清理?

先查占用:journalctl --disk-usage。清理方式:① 按时间:journalctl --vacuum-time=30d;② 按大小:journalctl --vacuum-size=200M;③ 永久限制:编辑 /etc/systemd/journald.conf,设置 SystemMaxUse=200MMaxRetentionSec=30day,然后 systemctl restart systemd-journald--vacuum 只删除已关闭的归档,不影响当前写入中的日志,完全安全。对内存有限的 VPS,推荐将 SystemMaxUse 设为 100-200MB。

如何判断 auth.log 里的 "Accepted" 是合法登录还是已被入侵?

排查步骤:① grep "Accepted" /var/log/auth.log,重点关注来源 IP 是否是您自己的;② last | head -20 查看最近登录记录含时间和 IP;③ 若已开启密钥认证(PasswordAuthentication no),出现 Accepted publickey 的陌生 IP 意味着该 IP 拥有您的私钥,需立即轮换密钥并强制所有会话下线(pkill -u 用户名 sshd);④ grep "Accepted password" 若看到密码登录成功且您已禁用密码认证,说明配置未生效,立即执行 sshd -T | grep passwordauthentication 验证。

Nginx error.log 大量出现 "no live upstreams",如何彻底解决?

排查路径:① 确认后端服务状态:systemctl status php8.3-fpm,若停止重启即可;② 确认监听端口:ss -tlnp | grep :9000,确认 upstream 配置的地址端口与实际一致;③ 若用 Unix Socket(如 /run/php/php8.3-fpm.sock),确认 socket 文件存在且权限正确;④ 查 PHP-FPM 日志:journalctl -u php8.3-fpm -n 50,常见原因:OOM 触发进程被杀、pm.max_children 过小导致进程耗尽。根治:根据可用内存合理设置 pm.max_children(每个 PHP worker 约 50-80MB),并用 Systemd MemoryMax 限制上限。

dmesg 出现磁盘 I/O 错误,是硬盘要坏了吗?如何判断?

不同错误含义不同:Buffer I/O error on device 可能是坏道或控制器问题;EXT4-fs error 是文件系统损坏,通常由突然断电引发;SCSI error: return code = 0x08000002 是 SCSI 控制器超时,VPS 上常见,可能是虚拟化平台共享存储问题而非物理磁盘坏。诊断smartctl -a /dev/sda(需安装 smartmontools)查看 SMART 数据,重点看 Reallocated_Sector_Ct(坏扇区重分配数,非零则有坏道)和 Current_Pending_Sector。若在 VPS 上出现 I/O 错误,立即做全量备份,然后联系商家迁移,不要等待。

truncate 清空 Nginx 日志后会影响正在运行的 Nginx 吗?

truncate -s 0 /var/log/nginx/access.log 不会影响正在运行的 Nginx。truncate 只清空内容但不改变 inode,Nginx 持有的文件描述符仍然有效,会继续向该文件写入新日志。相比之下,rm access.log 会造成 Nginx 持续向已删除文件("幽灵文件")写入,df 显示磁盘空间不减少,直到 Nginx 重启才释放——这是常见的磁盘清理陷阱。最规范的方式是使用 logrotate,轮转后发送 USR1 信号让 Nginx 重新打开日志文件(见第 11 篇 automation-scripts 中的 logrotate 配置)。

GoAccess 生成的 HTML 报告放到网站上公开访问有没有安全风险?

有明显隐私风险——报告含所有访客 IP 和访问路径,不应公开。推荐做法:① IP 白名单:Nginx 限制只有您自己的 IP 才能访问报告路径;② HTTP Basic Auth:为报告页面添加 auth_basic 密码保护;③ 最简单:输出到 /tmp/,用 scp 下载到本地查看,或通过 SSH 隧道(ssh -L 8080:127.0.0.1:7890 server)在本地安全访问。实时 WebSocket 模式绑定到 127.0.0.1,再用 SSH 端口转发访问是最安全的实时方案。

如何自动化日志分析,让异常主动通知我而不是手动去查?

结合第 11 篇(automation-scripts)的 Webhook 通知函数,写 Cron 定时日志巡检脚本:① 每 5 分钟统计最近 5 分钟内的 502 数量,超过阈值推送企业微信告警;② 每分钟 journalctl -k --since "1 min ago" | grep -i oom,发现 OOM 立即告警;③ 每分钟监控 auth.log 中 "Accepted" 记录,来源 IP 不在白名单则立即推送。更完善的方案:部署 Grafana + Loki——Loki 是轻量级日志聚合系统,1GB 内存的 VPS 也能运行,通过告警规则实现分钟级异常检测并 Webhook 通知,比 ELK 轻量得多。

本篇学完后,下一步应该如何继续?与后续篇章有什么关联?

按 30 篇路径,第 12 篇(本篇,logs-and-troubleshooting)→ 第 13 篇(server-control-panels)→ 第 14 篇(web-server-setup)是从系统运维到应用部署的关键跨越。关联逻辑:第 13 篇的可视化面板(1Panel/宝塔)会把本篇的 Nginx 日志分析做成图形界面,后台即可查看流量统计和错误趋势;第 14 篇的 Nginx 原生搭建中,本篇学到的 journalctl -u nginxerror.log 分析和 502 排查,将成为调试虚拟主机配置的核心技能。至此,整个"系统与安全"系列(第 7-12 篇)构成完整体系:磁盘管理 → 系统调优 → 防火墙 → 安全加固 → 自动化 → 日志分析,掌握后无惧任何系统级故障。

服务器被入侵后日志可能被删除,如何保护日志完整性?

获取 root 权限的攻击者可轻易清除本地日志。多层防护策略:① 实时转发到远程日志服务器(见本篇 rsyslog 集中日志)——日志写入远程后本地清除无法抹掉远端记录;② chattr 不可变属性chattr +a /var/log/auth.log(只能追加,不能删除),即使 root 也无法直接清空(需先 chattr -a);③ 云端日志服务:将 Nginx 日志实时写入 AWS CloudWatch Logs 或 Cloudflare Logpush,存储在黑客无法触及的第三方;④ 定期备份归档:配合 rclone 将压缩后的旧日志备份到 Cloudflare R2,即使服务器全盘清除也有历史可查。