
手里 VPS 越多,越怕半夜出事:内存被某个进程吃满、日志把磁盘写爆、流量被刷超了停机——等你早上打开面板,服务已经挂了几个小时。
解决办法很简单:写个小脚本,每 5 分钟自动"体检"一次,指标超标就往你手机上推一条 Telegram 消息。本文给你一套完整可用的方案:零依赖(只用 Python 标准库),复制粘贴就能跑。
前置知识
- 会用 SSH 登录自己的 VPS(任意发行版,本文以 Debian/Ubuntu 为例)
- 有个 Telegram 账号,能收到消息即可
整体思路
巡检脚本(每5分钟 cron 触发)
├─ 读 /proc/meminfo → 内存使用率
├─ shutil.disk_usage → 磁盘使用率
├─ os.getloadavg → 系统负载
├─ 读 /proc/net/dev → 出站流量(累计到每天)
└─ 任一指标超标 → 调用 Telegram Bot API 推送告警
为什么不用现成的监控面板(如 Netdata、哪吒探针)?那些是好工具,但本文方案部署成本最低:一个文件、一条 cron,5 分钟搞定,而且推送到手机不需要你主动打开面板去看。
第一步:创建一个 Telegram 机器人
- 在 Telegram 里搜索 @BotFather,发送
/newbot,按提示起个名字,拿到一串 token(形如123456789:AAHxxxx...)。 - 给你刚创建的机器人随便发一条消息(比如"你好")。
- 在浏览器打开
https://api.telegram.org/bot<你的token>/getUpdates(把<你的token>换成上一步的),在返回的 JSON 里找到"chat":{"id":后面的数字,那就是你的 chat_id。
机器人只是个"传话筒",它只能给你发消息,看不到你的其他聊天,安全性不用担心。
第二步:完整脚本
把下面代码保存为 /usr/local/bin/vps_health_check.py,然后把开头的 TOKEN 和 CHAT_ID 换成你自己的:
#!/usr/bin/env python3
"""VPS 健康巡检:内存/磁盘/负载/每日出站流量超标时经 Telegram 推送告警。"""
import json, os, shutil, time, urllib.parse, urllib.request
TOKEN = "<你的Bot Token>" # 换成你自己的
CHAT_ID = "<你的Chat ID>" # 换成你自己的
STATE_FILE = "/var/tmp/vps_health_check_state.json"
ALERT_COOLDOWN = 3600 # 同一指标 1 小时内只报一次,避免刷屏
MEM_THRESHOLD = 90 # 内存使用率 %
DISK_THRESHOLD = 85 # 磁盘使用率 %
TRAFFIC_DAILY_LIMIT_GB = 300 # 每日出站流量上限(GB)
def read_mem():
info = {}
with open("/proc/meminfo") as f:
for line in f:
k, v = line.split(":", 1)
info[k.strip()] = int(v.split()[0]) # kB
used = info["MemTotal"] - info["MemAvailable"]
return used / info["MemTotal"] * 100
def read_disk():
du = shutil.disk_usage("/")
return du.used / du.total * 100
def read_tx_bytes():
total = 0
with open("/proc/net/dev") as f:
for line in f:
if ":" not in line:
continue
iface, rest = line.split(":", 1)
if iface.strip() == "lo":
continue # 排除本地回环
total += int(rest.split()[8]) # 第 9 列是发送字节数
return total
def load_state():
try:
with open(STATE_FILE) as f:
return json.load(f)
except (OSError, ValueError):
return {}
def save_state(state):
with open(STATE_FILE, "w") as f:
json.dump(state, f)
def send_telegram(text):
url = f"https://api.telegram.org/bot{TOKEN}/sendMessage"
data = urllib.parse.urlencode({"chat_id": CHAT_ID, "text": text}).encode()
req = urllib.request.Request(url, data=data, method="POST")
with urllib.request.urlopen(req, timeout=15) as r:
return r.status
def main():
now, state, alerts = time.time(), load_state(), []
mem = read_mem()
if mem >= MEM_THRESHOLD and now - state.get("mem_at", 0) > ALERT_COOLDOWN:
alerts.append(f"内存使用率 {mem:.1f}%(阈值 {MEM_THRESHOLD}%)")
state["mem_at"] = now
disk = read_disk()
if disk >= DISK_THRESHOLD and now - state.get("disk_at", 0) > ALERT_COOLDOWN:
alerts.append(f"根分区使用率 {disk:.1f}%(阈值 {DISK_THRESHOLD}%)")
state["disk_at"] = now
load1, _, _ = os.getloadavg()
cpus = os.cpu_count() or 1
if load1 > cpus * 2 and now - state.get("load_at", 0) > ALERT_COOLDOWN:
alerts.append(f"1 分钟平均负载 {load1:.2f}({cpus} 核,阈值 {cpus * 2})")
state["load_at"] = now
tx = read_tx_bytes() # 每日出站流量:跨天自动清零重计
today = time.strftime("%Y-%m-%d")
if state.get("day") != today:
state = {"day": today, "base_tx": tx}
daily_gb = (tx - state.get("base_tx", tx)) / 1024 ** 3
if daily_gb >= TRAFFIC_DAILY_LIMIT_GB and not state.get("traffic_alerted"):
alerts.append(f"今日出站流量 {daily_gb:.1f} GB(上限 {TRAFFIC_DAILY_LIMIT_GB} GB)")
state["traffic_alerted"] = True
state["day"] = today
state.setdefault("base_tx", tx)
if alerts:
host = os.uname().nodename
msg = "🚨 VPS 健康告警(" + host + ")\n" + "\n".join("• " + a for a in alerts)
send_telegram(msg)
print("sent:", msg)
else:
print(f"OK: mem={mem:.1f}% disk={disk:.1f}% load1={load1:.2f} daily_tx={daily_gb:.2f}GB")
save_state(state)
if __name__ == "__main__":
main()
几个设计上的小心思,值得单独说一下:
- 告警冷却:内存一爆表,每 5 分钟就推一条会把你吵死。
ALERT_COOLDOWN让同一指标 1 小时内只报一次。 - 流量按天累计:
/proc/net/dev的计数器是开机累计的,脚本把每天零点的基准值存进状态文件,跨天自动清零重计。 - 状态文件:
/var/tmp/vps_health_check_state.json记录上次报警时间和流量基准,重启机器也不丢。
第三步:加到 cron,每 5 分钟跑一次
chmod +x /usr/local/bin/vps_health_check.py
# 先手动跑一遍测试(应输出 OK 开头的一行,且 Telegram 不应收到消息)
python3 /usr/local/bin/vps_health_check.py
# 测试通过后加入 cron
(crontab -l 2>/dev/null; echo "*/5 * * * * /usr/bin/python3 /usr/local/bin/vps_health_check.py") | crontab -
想验证告警通道是否通畅?临时把 MEM_THRESHOLD 改成 1,手动跑一次,手机应该立刻收到一条告警;确认收到后再改回 90。
排错
| 现象 | 排查 |
|---|---|
| 收不到消息 | 检查 token/chat_id 是否填对;在 VPS 上 curl https://api.telegram.org/bot<token>/getMe 看能否连通(国内机房直连 Telegram 可能不通,海外 VPS 一般没问题) |
| 流量计数每天清零不准 | 重启会清零 /proc/net/dev,脚本以"开机后首次运行"为基准,这是预期行为 |
| 想加 CPU 使用率监控 | /proc/stat 读两次算差值即可,稍复杂;负载(load average)通常已够用 |
写在最后
这套方案我自己的 VPS 上跑了很久,最大的价值不是"监控"本身,而是把"人去看面板"变成了"出事才找我"。内存泄漏、日志爆盘、被刷流量,这三类最常见的 VPS 翻车,基本都能第一时间发现。
进阶玩法:把多个 VPS 的告警推到同一个机器人,消息里自带主机名(脚本里已经带了 os.uname().nodename),一台手机管十台机器也不乱。
