小型服务器在同时运行 AI 服务、终端会话、备份任务和其他常驻程序时,偶尔可能出现响应迟缓,甚至完全失去连接。

服务器卡死后再查看日志,往往只能知道系统曾经发生过异常,却难以直观看到卡死前的内存、Swap、CPU 压力、I/O 压力和进程状态。

一种简单有效的办法,是制作一个固定的命令行监控脚本,在单个终端页面中集中显示关键状态,并每隔几秒自动刷新。

本文介绍一种不依赖额外监控平台的实现方式,适用于 Debian、Ubuntu、Arch Linux 等使用 systemd 的 Linux 系统。


监控内容

这个监控页面主要显示以下信息:

  • 系统运行时间和负载
  • 内存与 Swap 使用情况
  • CPU、内存和 I/O 压力
  • 指定 systemd 用户服务的运行状态
  • 内存占用最高的进程
  • 处于不可中断等待状态的进程
  • 服务当前内存、历史峰值和任务数量

相比单独运行 topfreepssystemctl,单页监控更适合观察系统是否正在逐渐接近卡死状态。


创建固定脚本

以下示例将脚本安装到:

/usr/local/bin/server-monitor

默认监控一个名为:

example.service

的 systemd 用户服务。

实际使用时,可以通过环境变量指定需要监控的服务名称。

在具有管理员权限的终端中执行:

set -euo pipefail

f=/usr/local/bin/server-monitor

if [ -e "$f" ]; then
    old="${f}.$(date -r "$f" +%Y%m%d-%H%M%S)"

    if [ -e "$old" ]; then
        echo "Backup target already exists: $old" >&2
        exit 1
    fi

    mv -- "$f" "$old"
    cp -a -- "$old" "$f"
fi

cat > /tmp/server-monitor.new <<'EOF'
#!/usr/bin/env bash

set -u

interval="${SERVER_MONITOR_INTERVAL:-2}"
service_name="${MONITORED_SERVICE:-example.service}"

if [ "${1:-}" != "--snapshot" ]; then
    exec watch \
        -n "$interval" \
        -d \
        -t \
        -- "$0" --snapshot
fi

echo "========== SYSTEM MONITOR =========="
date
uptime

echo
echo "========== MEMORY / SWAP =========="
free -h
swapon --show

echo
echo "========== PRESSURE =========="
for resource in cpu memory io; do
    printf "%-8s" "${resource}:"
    tr '\n' ' ' < "/proc/pressure/$resource"
    echo
done

echo
echo "========== MONITORED SERVICE =========="
echo "Service: $service_name"

systemctl --user is-active "$service_name" 2>/dev/null || true

systemctl --user show "$service_name" \
    -p MainPID \
    -p MemoryCurrent \
    -p MemoryPeak \
    -p MemoryHigh \
    -p MemoryMax \
    -p TasksCurrent \
    -p ActiveState \
    -p SubState 2>/dev/null || true

echo
echo "========== TOP MEMORY PROCESSES =========="
ps -eo pid,user,stat,%cpu,%mem,rss,etimes,comm \
    --sort=-rss |
    head -n 12

echo
echo "========== BLOCKED PROCESSES =========="
ps -eo state,pid,ppid,wchan:24,comm |
    awk 'NR == 1 || $1 ~ /^D/'

echo
echo "Refresh: ${interval} seconds    Exit: Ctrl+C"
EOF

install \
    -o root \
    -g root \
    -m 0755 \
    /tmp/server-monitor.new \
    "$f"

rm -f /tmp/server-monitor.new

bash -n "$f"
ls -l --time-style=long-iso "$f"
sha256sum "$f"

这段安装命令会完成以下操作:

  1. 如果旧脚本已经存在,先按旧文件自身的修改时间建立备份。
  2. 将新脚本写入临时文件。
  3. 使用 install 设置正确的属主和执行权限。
  4. 使用 bash -n 检查脚本语法。
  5. 输出文件属性和 SHA256 校验值。

运行监控页面

假设需要监控的用户服务名为:

ai-gateway.service

可以这样启动:

MONITORED_SERVICE=ai-gateway.service server-monitor

页面默认每两秒刷新一次。

退出时按:

Ctrl+C

修改刷新间隔

刷新间隔通过 SERVER_MONITOR_INTERVAL 环境变量控制。

每秒刷新一次:

SERVER_MONITOR_INTERVAL=1 \
MONITORED_SERVICE=ai-gateway.service \
server-monitor

每五秒刷新一次:

SERVER_MONITOR_INTERVAL=5 \
MONITORED_SERVICE=ai-gateway.service \
server-monitor

一般情况下,两秒刷新一次已经足够,同时不会产生明显额外负载。


查看一次静态快照

脚本也支持只输出一次当前状态,不进入自动刷新模式:

MONITORED_SERVICE=ai-gateway.service \
server-monitor --snapshot

这种方式适合:

  • 保存到日志
  • 通过 SSH 临时检查
  • 与定时任务配合
  • 将状态输出发送给其他诊断程序

例如保存当前状态:

MONITORED_SERVICE=ai-gateway.service \
server-monitor --snapshot \
> /tmp/system-monitor.txt

页面中的关键指标

Load average

uptime 会显示类似内容:

load average: 1.87, 3.75, 2.76

三个数值分别代表过去:

  • 1 分钟
  • 5 分钟
  • 15 分钟

的平均系统负载。

负载是否过高需要结合 CPU 核心数判断。四核心系统长时间维持在 4 左右,通常表示 CPU 接近满负载;明显超过核心数,则可能出现任务排队。


Available memory

free -h 中最值得关注的是:

available

它比单纯查看 free 更有参考意义。

Linux 会主动使用空闲内存作为文件缓存,因此 free 很低并不一定代表内存不足。真正接近危险状态时,通常会出现:

  • available 持续下降
  • Swap 使用量快速增加
  • memory pressure 上升
  • 服务内存持续膨胀

Swap

Swap 是系统内存不足时的缓冲区。

Swap 使用量为零并不代表配置无效,只表示当前物理内存充足。需要警惕的是 Swap 持续快速增长,同时系统响应越来越慢。


Pressure Stall Information

脚本读取:

/proc/pressure/cpu
/proc/pressure/memory
/proc/pressure/io

这些文件属于 Linux 的 Pressure Stall Information,简称 PSI。

输出中的:

avg10
avg60
avg300

分别表示过去 10 秒、60 秒和 300 秒内,任务因资源不足而等待的比例。

例如:

memory: some avg10=0.00

表示最近十秒基本没有任务因内存压力而停顿。

如果 memory 或 io 的 avg10 持续明显升高,通常比单纯查看 CPU 使用率更能提前发现系统卡顿风险。


MemoryCurrent 与 MemoryPeak

systemd 可以统计整个服务控制组的资源占用:

MemoryCurrent
MemoryPeak

其中:

  • MemoryCurrent:服务当前使用的总内存
  • MemoryPeak:本次启动以来达到过的最高内存

这里统计的是服务及其子进程的总和,不只是主进程。

数值默认以字节显示。例如:

MemoryCurrent=1073741824

约等于 1 GiB。


D 状态进程

脚本最后会筛选状态为 D 的进程:

ps -eo state,pid,ppid,wchan:24,comm |
awk 'NR == 1 || $1 ~ /^D/'

D 表示不可中断睡眠,通常与磁盘、网络文件系统、块设备或其他内核 I/O 等待有关。

短暂出现一个 D 状态进程并不一定异常,但如果大量进程长时间停留在 D 状态,系统可能会表现为:

  • SSH 卡顿
  • 命令无法结束
  • 服务无法停止
  • 负载极高但 CPU 使用率不高
  • 最终完全失去响应

因此,这一部分对于调查服务器卡死尤其重要。


白色高亮字符的含义

脚本使用了:

watch -d

其中 -d 会高亮本次刷新与上一次刷新之间发生变化的字符。

时间、CPU 使用率、计数器和进程状态不断变化,因此页面中会出现零散的高亮区域。这不是乱码,也不是异常,而是 watch 的变化提示功能。

不需要高亮时,可以编辑脚本并删除:

-d

监控系统级服务

示例脚本默认使用:

systemctl --user

因此适用于用户级 systemd 服务。

如果需要监控系统级服务,例如:

docker.service

则需要把脚本中的两处:

systemctl --user

改为:

systemctl

然后运行:

MONITORED_SERVICE=docker.service server-monitor

总结

这个脚本不会替代 Prometheus、Grafana、Netdata 等完整监控平台,但它具有几个明显优势:

  • 不需要安装额外软件
  • 通过 SSH 即可使用
  • 所有关键状态集中在一个页面
  • 自动刷新
  • 可以快速发现内存膨胀、Swap 增长和 I/O 阻塞
  • 特别适合小型服务器、家庭服务器和树莓派类设备

在服务器运行重要任务时,保持一个这样的监控终端,可以更早发现资源耗尽和系统卡死的迹象,也能为后续故障分析保留更明确的观察依据。

Leave a Reply

Your email address will not be published. Required fields are marked *