概述

本文介绍一个轻量级的 Kubernetes 资源监控脚本,定期检测节点和 Pod 的 CPU、内存使用情况,超过阈值时通过企业微信 Webhook 发送告警通知。

核心功能:

  • 🔍 节点资源监控(CPU/内存)
  • 📦 Pod 资源监控(CPU/内存)
  • 📢 企业微信告警推送
  • ⚙️ 阈值可配置
  • 🔄 定时任务执行

适用场景:

  • 轻量级资源监控
  • 快速告警通知
  • 开发测试环境
  • 补充监控方案

脚本完整代码

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/env bash

# ========== 配置区 ==========

# 企业微信 Webhook URL
webhookurl=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY_HERE

# 命名空间(通过参数传入)
if [ -z "$1" ]; then
echo "Usage: $0 <namespace>"
exit 1
else
nameSpace=$1
fi

# 告警阈值配置
cpuVPT=85 # 节点 CPU 限制值(%)
memVPT=85 # 节点内存限制值(%)
podCpuVPT=500 # Pod CPU 限制值(m,millicore)
podMemVPT=500 # Pod 内存限制值(Mi)

# ========== 变量初始化 ==========

# 存储超阈值的节点和 Pod
overVPTNodes=()
overVPTPods=()

# 告警信息模板
nodeViewMsg=""
podViewMsg=""

# 节点告警消息模板
nodeMsg="### <font color='warning'>机器资源警告: </font>\n
> node: <font color='info'>"'$NODE_NAME'"</font>\n
> cpu: <font color='comment'>"'$NODE_CPU'"</font>\n
> 内存: <font color='comment'>"'$NODE_MEM'"</font>"

# Pod 告警消息模板
podMsg="### <font color='warning'>Pod资源警告: </font>\n
> namespace: <font color='info'>"'$NAME_SPACE'"</font>\n
> pod: <font color='info'>"'$POD_NAME'"</font>\n
> cpu: <font color='comment'>"'$POD_CPU'"</font>\n
> 内存: <font color='comment'>"'$POD_MEM'"</font>"

# ========== 节点监控 ==========

# 获取节点状态(跳过标题行,提取名称、CPU%、内存%)
nodesStatus=$(kubectl top nodes | awk '{if (NR>1){print $1,$3,$5}}' | tr "\r\n" "|" | tr " " ",")
nodesStatus=(${nodesStatus//|/ })

# 检查节点资源使用情况
# 格式:NAME,CPU%,MEMORY%
# 示例:k3s-node1,2%,38%
for ((i = 0; i < ${#nodesStatus[@]}; i++)); do
# 去掉百分号
node=${nodesStatus[i]//%/}
node=(${node//,/ })

# 判断 CPU 和内存是否超过阈值
if [[ "${node[1]}" =~ ^[0-9]+$ ]] && [[ "${node[2]}" =~ ^[0-9]+$ ]]; then
if [ "${node[1]}" -ge ${cpuVPT} ] || [ "${node[2]}" -ge ${memVPT} ]; then
overVPTNodes+=(${nodesStatus[i]})
fi
fi
done

# ========== Pod 监控 ==========

# 获取指定命名空间下的 Pod 状态
podsStatus=$(kubectl top pod -n "${nameSpace}" | awk '{if (NR>1){print $1,$2,$3}}' | tr "\r\n" "|" | tr " " ",")
podsStatus=(${podsStatus//|/ })

# 检查 Pod 资源使用情况
# 格式:NAME,CPU(cores),MEMORY(bytes)
for ((i = 0; i < ${#podsStatus[@]}; i++)); do
# 去掉单位(m 和 Mi)
pod=${podsStatus[i]//m/}
pod=${pod//Mi/}
pod=(${pod//,/ })

# 判断 CPU 和内存是否超过阈值
if [ "${pod[1]}" -ge ${podCpuVPT} ] || [ "${pod[2]}" -ge ${podMemVPT} ]; then
echo "当前 Pod 资源已超过阈值 -- ${pod[0]}"
overVPTPods+=(${podsStatus[i]})
fi
done

# ========== 构建告警消息 ==========

# 构建节点告警消息
for ((i = 0; i < ${#overVPTNodes[@]}; i++)); do
node=${overVPTNodes[i]}
nodes=(${node//,/ })
nodeViewMsg=$nodeViewMsg"\n\n"$(echo ${nodeMsg} | \
sed "s/\$NODE_NAME/${nodes[0]}/g" | \
sed "s/\$NODE_CPU/${nodes[1]}/g" | \
sed "s/\$NODE_MEM/${nodes[2]}/g")
done

# 构建 Pod 告警消息
for ((i = 0; i < ${#overVPTPods[@]}; i++)); do
pod=${overVPTPods[i]}
pods=(${pod//,/ })
podViewMsg=$podViewMsg"\n\n"$(echo ${podMsg} | \
sed "s/\$NAME_SPACE/${nameSpace}/g" | \
sed "s/\$POD_NAME/${pods[0]}/g" | \
sed "s/\$POD_CPU/${pods[1]}/g" | \
sed "s/\$POD_MEM/${pods[2]}/g")
done

# 截取前 4000 字符(企业微信 Webhook 有长度限制)
podViewMsg=${podViewMsg:0:4000}

# ========== 发送告警 ==========

# 发送节点告警
if [[ -n $nodeViewMsg ]]; then
curl --location --request POST "${webhookurl}" \
--header 'Content-Type: application/json' \
--data '{
"msgtype":"markdown",
"markdown":{
"content":"'"${nodeViewMsg}"'"
}
}'
fi

# 发送 Pod 告警
if [[ -n $podViewMsg ]]; then
curl --location --request POST "${webhookurl}" \
--header 'Content-Type: application/json' \
--data '{
"msgtype":"markdown",
"markdown":{
"content":"'"${podViewMsg}"'"
}
}'
fi

使用说明

前置条件

1. 安装 kubectl

1
2
3
4
5
# 验证 kubectl 可用
kubectl version --client

# 确保有集群访问权限
kubectl get nodes

2. 获取企业微信 Webhook URL

1
2
3
4
1. 登录企业微信管理后台
2. 进入「应用与小程序」→「自建」
3. 创建机器人,获取 Webhook URL
4. 格式:https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY

配置脚本

1. 保存脚本

1
2
3
4
# 创建脚本文件
vi /opt/scripts/k8s-monitor.sh

# 复制脚本内容并保存

2. 修改配置

1
2
3
4
5
6
7
8
# 修改 Webhook URL
webhookurl=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_REAL_KEY

# 调整阈值(根据实际情况)
cpuVPT=85 # 节点 CPU 阈值 85%
memVPT=85 # 节点内存阈值 85%
podCpuVPT=500 # Pod CPU 阈值 500m
podMemVPT=500 # Pod 内存阈值 500Mi

3. 添加执行权限

1
chmod +x /opt/scripts/k8s-monitor.sh

手动执行

1
2
3
4
5
6
7
8
# 监控指定命名空间
/opt/scripts/k8s-monitor.sh default

# 监控生产环境
/opt/scripts/k8s-monitor.sh production

# 测试脚本(查看输出)
bash -x /opt/scripts/k8s-monitor.sh default

定时任务配置

方案1:使用 Crontab

1
2
3
4
5
6
7
8
9
10
11
# 编辑 crontab
crontab -e

# 每 5 分钟执行一次(监控 default 命名空间)
*/5 * * * * /opt/scripts/k8s-monitor.sh default >> /var/log/k8s-monitor.log 2>&1

# 每 10 分钟执行一次(监控 production 命名空间)
*/10 * * * * /opt/scripts/k8s-monitor.sh production >> /var/log/k8s-monitor-prod.log 2>&1

# 查看定时任务
crontab -l

方案2:使用 Kubernetes CronJob

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
apiVersion: batch/v1
kind: CronJob
metadata:
name: k8s-monitor
namespace: kube-system
spec:
schedule: "*/5 * * * *" # 每 5 分钟
jobTemplate:
spec:
template:
spec:
serviceAccountName: k8s-monitor-sa
containers:
- name: monitor
image: bitnami/kubectl:latest
command:
- /bin/sh
- -c
- |
# 脚本内容(省略,与上面相同)
restartPolicy: OnFailure
---
# ServiceAccount 配置
apiVersion: v1
kind: ServiceAccount
metadata:
name: k8s-monitor-sa
namespace: kube-system
---
# RBAC 权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: k8s-monitor-role
rules:
- apiGroups: [""]
resources: ["nodes", "pods"]
verbs: ["get", "list"]
- apiGroups: ["metrics.k8s.io"]
resources: ["nodes", "pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: k8s-monitor-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: k8s-monitor-role
subjects:
- kind: ServiceAccount
name: k8s-monitor-sa
namespace: kube-system

告警示例

节点告警

企业微信消息效果:

1
2
3
4
5
6
7
8
9
### 机器资源警告: 
> node: k3s-node1
> cpu: 92%
> 内存: 88%

### 机器资源警告:
> node: k3s-node2
> cpu: 86%
> 内存: 75%

Pod 告警

1
2
3
4
5
6
7
8
9
10
11
### Pod资源警告: 
> namespace: production
> pod: nginx-deployment-abc123
> cpu: 850m
> 内存: 1024Mi

### Pod资源警告:
> namespace: production
> pod: mysql-statefulset-0
> cpu: 450m
> 内存: 2048Mi

脚本解析

核心逻辑

1. 数据获取

1
2
3
4
5
6
# kubectl top nodes 输出格式:
# NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
# k3s-node1 400m 2% 1500Mi 38%

# 提取关键信息:名称、CPU%、内存%
kubectl top nodes | awk '{if (NR>1){print $1,$3,$5}}'

2. 数据转换

1
2
3
4
5
# 原始:k3s-node1 2% 38%\nk3s-node2 6% 70%
# 步骤1:换行符 → | 分隔符
# 步骤2:空格 → , 分隔符
# 步骤3:| → 空格,转为数组
# 结果:["k3s-node1,2%,38%", "k3s-node2,6%,70%"]

3. 阈值判断

1
2
3
4
5
6
7
# 去除百分号,判断是否为数字
if [[ "${node[1]}" =~ ^[0-9]+$ ]]; then
# 判断是否超过阈值
if [ "${node[1]}" -ge ${cpuVPT} ]; then
# 触发告警
fi
fi

4. 消息构建

1
2
3
4
5
# 使用 sed 替换模板变量
echo ${nodeMsg} | \
sed "s/\$NODE_NAME/${nodes[0]}/g" | \
sed "s/\$NODE_CPU/${nodes[1]}/g" | \
sed "s/\$NODE_MEM/${nodes[2]}/g"

关键技巧

数组操作:

1
2
3
4
5
6
7
# 追加元素
array+=(element)

# 遍历数组
for ((i = 0; i < ${#array[@]}; i++)); do
echo ${array[i]}
done

字符串处理:

1
2
3
4
5
6
7
# 去除字符
string=${string//%/} # 去除 %
string=${string//m/} # 去除 m
string=${string//Mi/} # 去除 Mi

# 分割字符串
arr=(${string//,/ }) # 按 , 分割

正则匹配:

1
2
3
4
# 判断是否为数字
if [[ "$var" =~ ^[0-9]+$ ]]; then
echo "是数字"
fi

优化建议

功能增强

1. 添加日志记录

1
2
3
4
5
6
7
# 在脚本开头添加
LOG_FILE="/var/log/k8s-monitor.log"

# 记录执行日志
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始监控" >> ${LOG_FILE}
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 节点超阈值: ${#overVPTNodes[@]}" >> ${LOG_FILE}
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Pod超阈值: ${#overVPTPods[@]}" >> ${LOG_FILE}

2. 添加错误处理

1
2
3
4
5
6
7
# kubectl 命令失败处理
nodesStatus=$(kubectl top nodes 2>&1)
if [ $? -ne 0 ]; then
echo "Error: kubectl top nodes failed"
echo "$nodesStatus"
exit 1
fi

3. 支持多命名空间

1
2
3
4
5
6
7
# 监控多个命名空间
namespaces=("default" "production" "staging")

for ns in "${namespaces[@]}"; do
echo "Monitoring namespace: $ns"
# 执行监控逻辑
done

4. 告警去重

1
2
3
4
5
6
7
8
9
10
11
# 使用文件记录已告警的资源
ALERT_FILE="/tmp/k8s-alerts.txt"

# 检查是否已告警
if grep -q "${node[0]}" ${ALERT_FILE}; then
# 跳过(避免重复告警)
continue
fi

# 记录告警
echo "${node[0]} $(date +%s)" >> ${ALERT_FILE}

5. 清理过期告警记录

1
2
3
4
5
6
7
8
9
# 清理 1 小时前的告警记录
current_time=$(date +%s)
while IFS= read -r line; do
alert_time=$(echo $line | awk '{print $2}')
if [ $((current_time - alert_time)) -lt 3600 ]; then
echo $line >> ${ALERT_FILE}.tmp
fi
done < ${ALERT_FILE}
mv ${ALERT_FILE}.tmp ${ALERT_FILE}

性能优化

1. 减少 kubectl 调用

1
2
3
4
5
6
# 一次性获取所有数据
kubectl top nodes > /tmp/nodes.txt
kubectl top pods -n ${nameSpace} > /tmp/pods.txt

# 从文件读取
nodesStatus=$(cat /tmp/nodes.txt | awk ...)

2. 并行处理

1
2
3
4
5
# 并行监控多个命名空间
for ns in "${namespaces[@]}"; do
monitor_namespace $ns &
done
wait

最佳实践

生产环境建议

建议说明
阈值调整根据实际情况调整阈值
日志轮转配置日志轮转,避免占满磁盘
错误处理添加完善的错误处理逻辑
告警去重避免重复告警骚扰
定期清理清理临时文件和过期告警

监控方案对比

方案优点缺点适用场景
本脚本轻量、快速部署功能有限开发/测试环境
Prometheus + Grafana功能强大、可视化部署复杂生产环境
云厂商监控托管服务、免运维成本较高云上环境

注意事项

限制与不足:

1
2
3
4
5
⚠️ 仅监控资源使用,不监控服务健康
⚠️ 告警消息长度限制(4000字符)
⚠️ 依赖 kubectl top(需要 metrics-server)
⚠️ 无告警历史记录和统计
⚠️ 无告警升级和降级机制

改进方向:

1
2
3
4
5
✅ 集成更专业的监控系统
✅ 添加更多监控维度
✅ 实现告警管理功能
✅ 提供可视化界面
✅ 支持更多通知渠道

总结

适用场景:

1
2
3
4
✅ 快速搭建轻量级监控
✅ 开发测试环境告警
✅ 补充现有监控方案
✅ 学习 Shell 脚本编程

核心价值:

  • 🚀 快速部署,5分钟上线
  • 💰 零成本,纯脚本实现
  • 🔧 易维护,代码简单清晰
  • 📢 及时告警,企业微信推送

最终建议:

本脚本适合作为临时或补充方案。生产环境建议使用 Prometheus + Grafana + Alertmanager 等成熟监控系统。