一、先聊聊边缘监控这事
边缘节点的监控一直是让人头大的事。尤其当你手里只有一台内存1GB的小盒子,还得让它跑业务、跑网络,再塞一个Prometheus进去,动不动就内存耗尽。更要命的是边缘网络不稳定,节点经常睡大觉,监控数据说断就断。
我这次是在OpenYurt集群里搞监控。OpenYurt这个方案很聪明,它把Kubernetes的能力延伸到边缘,但默认的监控方案在边缘场景下有点水土不服。这篇文章就记录我怎么一点一点把Prometheus在OpenYurt边缘节点上“养”活的,希望给同样在边缘挣扎的朋友一点参考。
1.1 为什么边缘节点监控让人头疼
首先,边缘节点资源太少。普通机房服务器32GB内存起步,边缘设备可能就512MB,连安装agent都费劲。Prometheus本身是内存大户,一启动就吃掉几百MB,稍微拉点指标就卡死。
其次,网络不稳定。边缘节点经常离线,或者网络抖动严重。中心机房那套“定期拉取”的方式,在边缘完全行不通——你还没拉到数据,节点已经掉线了。
第三,运维困难。边缘节点分散在各地,没法像机房一样随时登录。配置错了,改一下要跑好几个小时。所以必须设计成高度自动化的方案。
1.2 本文技术栈和整体思路
为了不让大家看乱,本文所有示例统一使用“Shell 脚本 + kubectl”这套技术栈。也就是说,所有配置我都用 heredoc 写进Shell脚本里,再用kubectl打到集群里去,这样既能看到YAML配置,又能直接执行,一举两得。
整体思路是:把Prometheus做成轻量级,能省则省;数据优先推送到中心,不靠边缘节点保存;采集目标要能跟着节点池走,别把中心节点也卷进来。
二、OpenYurt和Prometheus的基本搭档
2.1 OpenYurt是什么
OpenYurt是阿里开源的一个边缘计算平台,它基于Kubernetes,但是做了很多边缘场景的增强。核心思想是“中心管控,边缘自治”,也就是中心坏了,边缘还能自己跑。它把节点分成一个个节点池,每个池子可以有不同的调度策略。
对监控来说,OpenYurt带来的好处是:我们可以按照节点池来配置采集范围,而不是死板地按集群全局配置。同时,OpenYurt提供了边缘Pod的“单元化”部署,让监控组件可以就近部署。
但问题是,OpenYurt默认的架构里,边缘节点的kubelet仍然连接中心的kube-apiserver,一旦断网,很多状态就同步不了。Prometheus如果去抓kubelet的metrics接口,很可能因为网络问题而失败。
2.2 Prometheus常规部署在边缘的坑
常规部署,就是把Prometheus当成普通Pod,放在中心集群里,让它去抓所有节点的指标。看上去没问题,但在边缘环境下,坑一个接一个。
第一个坑:抓取超时。中心到边缘的网络延迟,动不动就几百毫秒,Prometheus默认scrape_timeout是10秒,经常超时,数据采不到。
第二个坑:资源浪费。每个节点的kubelet都暴露了10250端口的metrics,Prometheus要同时采集所有节点,在中心机器上产生大量连接和goroutine,内存直接飙红。
第三个坑:数据存储。边缘数据应该就近处理,而不是全部回传中心,不然带宽根本扛不住。
所以,单纯的“中心抓取”模式必须改。
三、资源受限下的适配优化实战
3.1 第一步:压缩Prometheus的资源占用
要限制Prometheus的内存,最直接的办法是减少采集量,然后限制container的硬上限。我们可以在Prometheus的启动参数里调整,也可以在配置里改采集频率。
先看一个基础的配置文件。技术栈:Shell 脚本(kubectl + ConfigMap)。
# 技术栈:Shell 脚本
# 示例:一个精简过的Prometheus配置文件,适合边缘小内存节点
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitoring
data:
prometheus.yml: |
global:
scrape_interval: 60s # 采集间隔拉长到60秒,原来默认15秒,大幅减少连接和内存
scrape_timeout: 15s # 超时放宽到15秒,因为边缘网络慢
evaluation_interval: 60s # 告警规则评估也慢一点,省CPU
scrape_configs:
# 只采必需指标,去掉大量无关的target
- job_name: 'kubelet'
metrics_path: /metrics
scheme: https
tls_config:
insecure_skip_verify: true
static_configs:
- targets: ['127.0.0.1:10250'] # 注意:这里是示意,实际要用节点地址
relabel_configs:
- source_labels: [__address__]
target_label: instance
regex: '(.*):.*'
replacement: '${1}'
EOF
看到没,scrape_interval 从15秒改到60秒,一下子就少了四分之三的请求。内存占用也随之降下来。如果你有100个节点,原来每个节点每分钟抓4次,现在抓1次,效果非常明显。
然后,我们给Prometheus设置Pod资源上限。技术栈:Shell 脚本。
# 技术栈:Shell 脚本
# 示例:给Prometheus加资源限制,防止它把边缘节点内存吃光
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
containers:
- name: prometheus
image: prom/prometheus:v2.50.0
imagePullPolicy: IfNotPresent
args:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.retention.time=12h # 只保留12小时数据,别占太多磁盘
- --storage.tsdb.retention.size=2GB # 磁盘上限2GB,防止数据无限膨胀
- --storage.tsdb.min-block-duration=30m # 少量数据尽快落盘,防止内存累积
- --storage.tsdb.max-block-duration=1h
- --web.enable-lifecycle
resources:
requests:
memory: "256Mi" # 请求256MB,给调度器做参考
cpu: "100m"
limits:
memory: "512Mi" # 硬限制512MB,超出就重启,总比OOM杀进程强
cpu: "500m"
volumeMounts:
- name: config
mountPath: /etc/prometheus
- name: data
mountPath: /prometheus
volumes:
- name: config
configMap:
name: prometheus-config
- name: data
emptyDir: {} # 边缘环境建议用本地磁盘,别用网络存储
EOF
注意--storage.tsdb.min-block-duration这个参数很关键。Prometheus默认会先在内存里攒数据,攒够2小时才写盘。边缘设备内存小,等不到2小时就爆了。把最小block时间改成30分钟,数据能更快落盘,内存压力小很多。
3.2 第二步:用Remote Write把数据传到中心
边缘节点上的Prometheus只负责本地采集,不需要承担长期存储。我们可以用Remote Write功能,把数据推送到中心机房的Prometheus或者对象存储。这样边缘节点就算挂掉,数据也已经送出去了,不会丢。
这里有一个重要配置。技术栈:Shell 脚本。
# 技术栈:Shell 脚本
# 示例:配置Remote Write,边缘Prometheus把数据推送到中心集群
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-remote-write
namespace: monitoring
data:
prometheus.yml: |
global:
scrape_interval: 60s
scrape_timeout: 15s
remote_write:
- url: "http://prometheus-central.example.com:9090/api/v1/write"
queue_config:
capacity: 2500 # 队列长度,别太大,防止内存涨
max_shards: 4 # 并发分片数,边缘网络带宽有限,4个够了
min_shards: 1
max_samples_per_send: 500 # 每次发送的样本数,减少HTTP包大小
batch_send_deadline: 10s
# 如果中心需要认证,可以加basic_auth
basic_auth:
username: "edge"
password: "edgepass"
scrape_configs:
- job_name: 'node-exporter'
static_configs:
- targets: ['localhost:9100']
EOF
注意看max_shards和max_samples_per_send。默认值在中心机房没问题,但在边缘,网络慢,分片太多反而容易堆积内存。保守设置,宁可多等几秒,也不能把Prometheus压垮。
另外,Remote Write是异步的,它会把数据先放到内存队列里。如果中心一直连不上,队列会越积越长。所以一定要给Prometheus设置资源限制,否则一个断网就能把内存吃光。
3.3 第三步:针对OpenYurt节点池的采集配置
OpenYurt的节点池很厉害,我们可以按池子来配置采集目标。比如,有一个节点池叫“beijing”,里面跑的是摄像头识别服务,我们只需要监控这些节点的CPU、内存、磁盘就够了,没必要采集整个集群。
在Prometheus里,我们可以用kubernetes_sd_configs来做服务发现,然后通过__meta_kubernetes_node_label来筛选节点池。技术栈:Shell 脚本。
# 技术栈:Shell 脚本
# 示例:基于OpenYurt节点池标签的采集配置
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-node-pool
namespace: monitoring
data:
prometheus.yml: |
scrape_configs:
- job_name: 'node-exporter-opensource'
kubernetes_sd_configs:
- role: node
relabel_configs:
# 只保留属于某个节点池的节点,这里用标签 openyurt.io/pool 来匹配
- source_labels: [__meta_kubernetes_node_label_openyurt_io_pool]
regex: 'beijing'
action: keep
# 替换metrics路径,让Prometheus从node-exporter抓数据
- source_labels: [__meta_kubernetes_node_name]
target_label: __address__
replacement: '${1}.svc.cluster.local:9100'
EOF
注意kubernetes_sd_configs需要Prometheus有对应的RBAC权限,能够读取节点信息。如果你的Prometheus运行在边缘节点上,还要保证它能连上kube-apiserver。
如果不想用服务发现,也可以用file_sd_configs,直接配置一个目标文件。但那个文件只能写死节点IP,节点池扩容时更新麻烦。所以我更推荐用服务发现,配合OpenYurt的节点池标签,动态调整,非常方便。
四、踩坑记录与排查过程
4.1 坑一:节点掉线后数据断流
一开始,我部署好后,发现只要节点一离线,Prometheus就疯狂重试连接,每次重试都要等好几个超时周期,日志里全是context deadline exceeded。这其实不是数据断流,是Prometheus在等连接超时。节点离线本来就不该一直重试。
解决办法是把scrape_interval调大,同时把scrape_timeout缩短。比如,采集间隔60秒,超时10秒,这样即使节点离线,也只在每个周期内重试一次,不会持续占资源。另外,可以打开honor_timestamps: true,让数据带上原始时间戳,这样节点恢复后,数据能正确按时间排序。
4.2 坑二:TSDB碎片爆炸
第二件让我头疼的事,是Prometheus的TSDB文件碎片越来越多。边缘节点断电频繁,TSDB的block文件容易损坏。后来我查了文档,发现需要用tsdb命令来清理。如果你不想手动操作,可以在启动参数里加上--storage.tsdb.retention.size,限制总大小。当超过限制时,Prometheus会自动删除最老的block,而不是让碎片堆积。
另外,--storage.tsdb.wal-compression这个参数默认是启用的,但如果你用的版本比较老,可能没有开。WAL压缩能显著减少磁盘占用。建议显式地加上这个参数。先看看当前TSDB的block情况。技术栈:Shell 脚本。
# 技术栈:Shell 脚本
# 示例:查看TSDB中block的占用情况,判断碎片程度
kubectl -n monitoring exec deploy/prometheus -- ls -lh /prometheus/tsdb/
如果发现block数量特别多,并且单个block文件很小,就说明碎片化了。优化方案就是给Prometheus加上--storage.tsdb.min-block-duration=30m和--storage.tsdb.max-block-duration=1h,具体参数可以参考前文3.1节中的完整Deployment示例。另外,--storage.tsdb.retention.size=2GB一定要加上,这样当总数据量超过2GB时,旧block会被自动清理,整个目录就不会无限膨胀。
4.3 坑三:Docker指标采集权限问题
边缘节点上用Docker跑容器,想从/metrics采集Docker监控指标,结果报权限错误。这是因为Docker的socket文件权限是root:root,Prometheus进程不是root用户就访问不了。
解决办法有两种:一是把Prometheus的securityContext改成privileged,但这样不安全;二是挂载Docker socket时,把宿主机的用户组映射进去。我推荐第二种,更安全。但在快速验证时,为了方便,我直接用了root。技术栈:Shell 脚本。
# 技术栈:Shell 脚本
# 示例:通过hostPath挂载Docker socket,并配置securityContext
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
spec:
template:
spec:
securityContext:
runAsUser: 0 # 用root运行,方便读socket(示例,生产环境慎用)
containers:
- name: prometheus
image: prom/prometheus:v2.50.0
volumeMounts:
- name: docker-sock
mountPath: /var/run/docker.sock
volumes:
- name: docker-sock
hostPath:
path: /var/run/docker.sock
EOF
注意,用root运行有风险,如果你有更好的办法,比如给Prometheus挂一个专用的组,或者用sidecar把socket代理成http接口,那就更好了。我这个是拿来在测试环境快速验证的。
五、应用场景与优缺点分析
5.1 适合场景
这套优化方案特别适合以下几种情况:
- 边缘节点内存小于1GB,资源紧张,装不下完整版Prometheus。
- 边缘节点数量多,并且分散在多个地区,网络质量参差不齐。
- 要求既有本地缓存,又能把数据汇总到中心做统一分析。
- 已经在用OpenYurt做边缘集群管理,希望监控组件完全融入节点池调度。
5.2 技术优缺点
好处很明显:省内存,因为采集频率降了,还限制了存储;数据不丢,因为Remote Write能把数据推出去;配置灵活,能按节点池采集。
缺点也有:采集频率降低,意味着监控的粒度变粗,有些瞬时尖峰可能捕捉不到;Remote Write依赖网络,如果中心服务不可用,本地缓存压力会增大;Prometheus单机架构在高可用方面有短板,边缘节点上如果Prometheus挂掉,这一节点就失去监控。
另外,边缘节点如果长时间离线,本地Prometheus的Remote Write队列会堆积,内存和磁盘都可能被撑爆。要解决这个,需要调整queue_config,并且定期检查队列长度。
六、注意事项与总结
6.1 注意事项
第一,别把Prometheus的所有组件都堆在同一个边缘节点上。如果你既要采集,又要告警,还要远程写,那就需要多个进程,建议拆开,或者用轻量级的agent模式。
第二,OpenYurt的节点池标签命名有规则,一定要检查你的openyurt.io/pool标签确实存在。可以用kubectl get nodes --show-labels查一下。
第三,远程写入的中心端点必须稳定,最好前面加一组负载均衡,否则中心挂了,边缘数据全部积压。
第四,一定要设置好storage.tsdb.retention.size,否则磁盘慢慢被填满,节点会挂。
第五,升级Prometheus版本前,先看看TSDB格式有没有变化。边缘节点没法快速回滚,出问题很麻烦。
6.2 总结
把Prometheus弄到OpenYurt边缘节点上,核心就八个字:少采、快传、限存、自治。少采,是降低采集频率和指标数量;快传,是用Remote Write把数据及时推走;限存,是限制内存和磁盘占用;自治,是让边缘Prometheus在网络断开时也能独立工作。
我这一路踩了不少坑,但最终跑通的时候,看着只有256MB的节点上安稳运行的Prometheus,心里还是很舒坦的。资源受限不是绝路,只要好好调一调,Prometheus照样能在边缘发光发热。希望这篇文章能让你少走几步弯路。
评论
围绕“资源受限下的监控部署:Prometheus在OpenYurt边缘节点上的适配优化与踩坑记”参与讨论