日志是服务器的"黑匣子"——每一次攻击尝试、每一次服务崩溃、每一次 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 写入的纯文本日志文件。可直接用 cat、grep、awk 处理,无需特殊工具,也是 Nginx、MySQL 等应用程序的默认输出目标。
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 # 全部日志(从最旧开始,按 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 📁 传统日志文件速查手册
/var/log/ 下的传统文本日志在安全分析和攻击溯源方面依然不可替代——grep/awk 直接处理文本,速度极快且无需学习特殊工具。
auth.log:排查 SSH 暴力破解与入侵
# ── 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 与硬件错误
# ── 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 日志格式
# 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 常用分析命令
# ── 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 后执行:
# 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: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:交互式多文件日志浏览器 ──────────────────────────────────────────
# 安装
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 将所有服务器的日志汇聚到一台"日志中心",可以在一处搜索全部机器的历史记录。
# ── 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 # ── 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=200M 和 MaxRetentionSec=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 nginx、error.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,即使服务器全盘清除也有历史可查。