在Linux系统管理领域,监控是运维工作的基石。我们通常会关注CPU、内存、磁盘I/O等常规指标,但系统深处还隐藏着许多容易被忽视却可能引发严重问题的"暗坑"。这些监控盲区往往在系统出现异常时才会被发现,而此时可能已经造成了业务中断或性能劣化。
我管理过上百台Linux服务器,见过太多因为监控不全面导致的"惊喜"。比如某次数据库集群突然响应变慢,排查两小时才发现是某个冷门内核参数导致TCP连接泄漏;还有一次线上服务异常,最终定位到是没人关注的inotify实例耗尽。这些经历让我意识到:真正的专业运维,不仅要监控那些显而易见的指标,更要关注那些"你以为不需要监控"的系统角落。
Linux内核的slab分配器用于管理内核对象的内存分配,但它的使用情况很少被纳入常规监控。当内核模块或驱动存在内存泄漏时,slab内存会缓慢增长,最终可能导致系统OOM。
查看slab内存使用情况:
bash复制# 按内存占用排序显示slab信息
cat /proc/meminfo | grep Slab
sudo slabtop -o | head -20
关键监控项应包括:
经验:我们发现某次Kubernetes节点频繁OOM的根源是docker-containerd进程的dentry缓存泄漏,通过持续监控Slab: SUnreclaim指标才定位到问题。
透明大页(Transparent HugePages)本是为提升内存访问性能的设计,但在某些场景下反而会导致性能下降:
bash复制# 检查THP状态
cat /sys/kernel/mm/transparent_hugepage/enabled
# 监控大页使用
cat /proc/meminfo | grep -i huge
需要特别关注:
现代应用大量使用inotify机制监控文件变化,但系统默认限制往往被低估:
bash复制# 查看当前使用情况
cat /proc/sys/fs/inotify/max_user_instances
find /proc/*/fd -lname anon_inode:inotify 2>/dev/null | wc -l
典型症状包括:
解决方案:
bash复制# 临时调整限制
echo 8192 > /proc/sys/fs/inotify/max_user_instances
# 永久生效需修改/etc/sysctl.conf
除了常规的lsof检查,更精准的监控方法是追踪fd分配:
bash复制# 按进程统计fd数量
ls -1 /proc/*/fd 2>/dev/null | awk -F/ '{print $3}' | uniq -c | sort -n
# 监控系统级fd使用
cat /proc/sys/fs/file-nr
关键指标:
常规监控会关注ESTABLISHED状态连接,但其他状态同样重要:
bash复制# 按状态统计TCP连接
ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c
# 监控半连接队列
netstat -s | grep -i listen
需要特别警惕:
容器环境下常出现网络命名空间泄漏:
bash复制# 查看网络命名空间数量
ls -1 /var/run/netns | wc -l
ip netns list | wc -l
诊断方法:
bash复制# 查找未清理的网络命名空间
sudo nsenter --net=/var/run/netns/<nsname> ip a
# 批量清理孤儿命名空间
for ns in $(ip netns list | awk '{print $1}'); do
if ! ps -p $(ip netns pids $ns) >/dev/null; then
ip netns del $ns
fi
done
当系统内存紧张时,页缓存回收可能引发I/O风暴:
bash复制# 监控页缓存回收活动
vmstat 1 | awk '{print $7,$8}' # bi/bo
cat /proc/vmstat | grep pgsteal
关键现象:
LVM/设备映射器的统计信息常被忽略:
bash复制# 查看dm设备统计
dmsetup status
# 监控延迟
cat /sys/block/dm-*/stat
需要注意:
许多系统服务会修改ulimit,但重启后失效:
bash复制# 检查关键限制
cat /proc/$(pidof <service>)/limits
# 对比系统默认值
ulimit -a
常见问题:
临时调整的内核参数可能在重启后恢复默认:
bash复制# 查看当前生效参数
sysctl -a
# 检查哪些参数与配置文件不一致
sysctl --system
特别容易出问题的参数:
使用eBPF可以捕获传统工具无法观测的系统行为:
bash复制# 监控文件描述符分配
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'
# 跟踪TCP重传
sudo bpftrace -e 'kprobe:tcp_retransmit_skb { @[pid,comm] = count(); }'
关键eBPF探针:
示例node_exporter文本收集器配置:
bash复制# /etc/node_exporter/custom_metrics.prom
# HELP inotify_instances Current inotify instances count
# TYPE inotify_instances gauge
inotify_instances $(find /proc/*/fd -lname anon_inode:inotify 2>/dev/null | wc -l)
# HELP slab_unreclaimable Unreclaimable slab memory in bytes
# TYPE slab_unreclaimable gauge
slab_unreclaimable $(grep Slab /proc/meminfo | awk '{print $2}')
某次凌晨收到服务器连接数告警,但常规监控显示连接数正常。通过以下步骤定位:
检查所有状态的TCP连接:
bash复制ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c
发现大量FIN_WAIT2状态连接
追踪问题进程:
bash复制ss -antp | grep FIN-WAIT-2
定位到某Java应用的PID
分析应用配置:
bash复制jcmd <pid> VM.flags | grep -i timeout
确认连接超时设置与系统tcp_fin_timeout不匹配
最终解决方案是调整应用的idle连接超时时间,并添加对FIN_WAIT2状态的监控。
用户报告磁盘使用率95%但du统计不到大文件:
检查已删除但未释放的文件:
bash复制lsof +L1 | grep deleted
发现某日志文件被删除但进程仍持有
确认空间分配:
bash复制df -h
tune2fs -l /dev/sda1 | grep Block
发现小文件占满inode
长期解决方案:
bash复制# 添加inode监控
df -i | grep -v tmpfs >> /etc/node_exporter/custom_metrics.prom
# 设置日志轮转策略
logrotate -f /etc/logrotate.d/myapp
对每个系统应记录正常状态下的指标范围:
bash复制# 记录典型内存使用模式
cat /proc/meminfo > memory_baseline.txt
# 保存正常连接数分布
ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c > tcp_state_baseline.txt
使用统计学方法检测偏离基线的行为:
python复制# 示例:基于历史数据的Z-score检测
def detect_anomaly(current, historical, threshold=3):
mean = np.mean(historical)
std = np.std(historical)
z_score = (current - mean) / std
return abs(z_score) > threshold
建议包含以下关键面板:
bash复制# 快速系统健康检查
dstat -tcmnd --fs --socket --tcp --vm --proc
# 实时进程资源监控
pidstat -d -u -r -p ALL 1
# 内核环形缓冲区消息
dmesg -T -l emerg,alert,crit,err,warn
bash复制#!/bin/bash
# 每日系统健康检查
check_inotify() {
local used=$(find /proc/*/fd -lname anon_inode:inotify 2>/dev/null | wc -l)
local max=$(cat /proc/sys/fs/inotify/max_user_instances)
echo "inotify: $used/$max instances"
}
check_slab() {
echo "Slab usage: $(grep -E 'Slab|SReclaimable|SUnreclaimable' /proc/meminfo)"
}
check_tcp_states() {
echo "TCP states:"
ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c
}
# 执行所有检查
check_inotify
check_slab
check_tcp_states
在实际运维中,我发现最有效的方法是建立"监控的监控"机制——即定期检查监控系统本身是否覆盖了所有关键维度。每次事故复盘后,我们都会评估:这个异常是否可以通过更全面的监控提前发现?这种持续改进的思维,才是应对Linux系统"暗坑"的最高级策略。