服务器探针

使用的源码地址
- ServerStatus中文版
- ServerStatus-HostLoc

其中客户端和web主要在ServerStatus-HostLoc的基础上稍作修改,因为ServerStatus中文版中有月流量统计功能,所以使用它做服务端

原理

整个系统共包括三个部分:

  1. 客户端
  2. 服务器
  3. web

客户端

这个是运行在想要监控的各个服务器上的,主要功能是采集各个客户端(服务器)上的。运行状态,将这些内容发送给服务端。
因为这两个版本略有差别,所以我将客户端ip连接数的key值,修改成了load_1(在服务端这个key值应该接收到的内容是1分钟内的平均负载),然后将ip连接数获取位置的tcp6改为tcp(ipv6 or ipv4 ?)

服务端

服务端运行在你想要部署你的网站的那台服务器上,主要功能是接受客户端发送来的数据,生成json格式的数据,然后把这些数据放到web网站的文件夹内。
一台主机既可以做服务端又可以当客户端

web

这个就是我们能看到的监控页面了,这个其实是个没有后端的静态的页面,主要的动态效果靠前端js实现,主要功能是使用前端js解析服务端生成的放到文件夹内的json数据,然后在前端页面上生成可视化页面
这里我主要使用的ServerStatus-HostLoc的web页面,添加了个顶部图片,服务器信息只保留卡片的形式显示,在卡片上增加了月流量统计,
使用的时候将web服务器(nginx or caddy)的 root 设置为web文件夹的路径就好了,一般都放在 /home/wwwroot/xxx/

Telegram bot

自己主要加了自主获取服务器状态和内存占用超过90%自动给我发消息
库链接 python-telegram-bot

自主获取服务器状态

效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
def get_table(i):
ss = PrettyTable()
ss.border = False
ss.header = False
ss.add_column('',
[
"节点名",
"位置",
"在线时间",
"IP连接数",
"网络 ↓|↑",
"月流量 ↓|↑",
"总流量 ↓|↑",
"处理器",
"内存",
"内存",
"交换分区",
"交换分区",
"硬盘",
"硬盘"
],
)
if i["online4"] is False and i["online6"] is False:
ss.add_column(
"",
[
"%s" % i["name"],
"%s" % i["location"],
'-',
'-',
'-',
'-',
'-',
'-',
'-',
'-',
'-',
'-',
'-',
'-'
]
)
else:
ss.add_column(
"",
[
"%s" % i["name"],
"%s" % i["location"],
"%s" % i["uptime"],
"%s" % (i["load_1"]),
"%.2fM|%.2fM" % (float(i["network_rx"]) / 1000 / 1000, float(i["network_tx"]) / 1000 / 1000),
"%.2fG|%.2fG" % (float(i["last_network_in"]) / 1024 / 1024 / 1024, float(i["last_network_out"]) / 1024 / 1024 / 1024),
"%.2fG|%.2fG" % (float(i["network_in"]) / 1024 / 1024 / 1024, float(i["network_out"]) / 1024 / 1024 / 1024),
"%d%%" % (i["cpu"]),
"%.2fG/%.2fG" % (float(i["memory_used"]) / 1024 / 1024, float(i["memory_total"]) / 1024 / 1024),
"%d%%" % (float(i["memory_used"]) / i["memory_total"] * 100),
"%.2fG/%.2fG" % (float(i["swap_used"]) / 1024 / 1024, float(i["swap_total"]) / 1024 / 1024),
"%d%%" % (0 if i["swap_total"]==0 else float(i["swap_used"]) / i["swap_total"] * 100),
"%.2fG/%.2fG" % (float(i["hdd_used"]) / 1024, float(i["hdd_total"]) / 1024),
"%d%%" % (float(i["hdd_used"]) / i["hdd_total"] * 100),
]
)
return ss.get_string().replace(' ',' ')



def server_status(update: Update, context: CallbackContext) :
if not validate(update):
return

r = requests.get(
url='http://ip:port/json/stats.json',
headers={
"User-Agent": "ServerStatus/20181203",
}
)
jsonR = r.json()
for i in jsonR["servers"]:
update.message.reply_text('服务器信息\n\n'+get_table(i))

#命令设置
status_handler = CommandHandler('status', server_status)
dispatcher.add_handler(status_handler)

定时报警

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def check_server_status(context: CallbackContext):
r = requests.get(
url='http://ip:port/json/stats.json',
headers={
"User-Agent": "ServerStatus/20181203",
}
)
jsonR = r.json()
for i in jsonR["servers"]:
if i["online4"] is False and i["online6"] is False :
context.bot.send_message(chat_id=user_id,text='服务器警报\n\n'+get_table(i))
elif float(i["memory_used"]) / i["memory_total"] >0.9:
context.bot.send_message(chat_id=user_id,text='服务器警报\n\n'+get_table(i))

#定时器设置
updater.job_queue.run_repeating(check_server_status, interval=1800, first=10)

最终效果 ServerStatus