当前位置: 首页 > article >正文

监控k8s pod使用的CPU资源并实现异常重启

需求

在Grafana监控面板里,可看到部分服务的pod对CPU的使用高达3:
在这里插入图片描述
而该pod的部署脚本为:

spec:
  template:
    spec:
      containers:
        - name: data-fusion-server
          resources:
            limits:
              cpu: '2'
              memory: 4Gi
            requests:
              cpu: 10m
              memory: 500Mi
          livenessProbe:
            httpGet:
              path: /health
              port: tcp
              scheme: HTTP
            initialDelaySeconds: 30
            timeoutSeconds: 1
            periodSeconds: 5
            successThreshold: 1
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /health
              port: tcp
              scheme: HTTP
            initialDelaySeconds: 30
            timeoutSeconds: 1
            periodSeconds: 5
            successThreshold: 1
            failureThreshold: 3
          startupProbe:
            httpGet:
              path: /health
              port: tcp
              scheme: HTTP
            initialDelaySeconds: 80
            timeoutSeconds: 1
            periodSeconds: 5
            successThreshold: 1
            failureThreshold: 3

也就是说,k8s限制pod可使用的CPU资源为2个。

pod使用的CPU资源对于k8s限制的CPU资源。

可简单分两种情况:

  • pod就是需要占用3个CPU资源,比如上面这个截图里,CPU使用率一直都非常稳定(截图有缩略,只截了2小时)。此时开发应该告诉运维,修改resources.limits.cpu,提高此配额。当然,并不是说,开发不需要去深入思考,为啥pod占用如此高的CPU,但是不排除部分pod就是要比其他pod使用更多的CPU。
  • pod绝大多数时间内,CPU使用率都比较低(假设是0.9个CPU);因为某些暂时不清楚的异常情况,一段时间内(比如10分钟)突然飙升到使用3个CPU;经过10分钟后,CPU使用率还是迟迟不降下来。又或者10分钟后,CPU使用下降,此时需要判断是否有定时调度任务触发执行。

对于第二种情况,如果迟迟不下降,此时服务已经不正常,则大概率无法正常完成服务请求,如果没有正确的负载均衡策略,则打到此pod的用户请求将得不到正确的处理。

需求来了:检测到pod使用的CPU资源,如果异常,则触发自动重启(而不是开发或运维,偶然间发现pod异常,然后手动重启)。

当然,另一方面,如果pod使用的CPU异常飙高,开发人员需要去检查应用或代码哪里出现问题。

调研

主要就是请教ChatGPT,给出的答复如下:

通过资源限制间接实现自动重启

设置CPU使用的限制值(limits),当Pod的CPU使用超过这个值时,Kubernetes将限制CPU使用,这可能导致应用程序不可用或进入异常状态,从而触发健康检查并重启Pod:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: my-image
    resources:
      requests:
        cpu: "500m"  # 请求的CPU资源
      limits:
        cpu: "1000m"  # 最大可使用的CPU资源
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5

自定义监控与重启机制

如果需要更精确地监控CPU使用率并自动重启Pod,可以使用Prometheus和Kubernetes API结合实现。以下是实现步骤:

  1. 部署Prometheus和Alertmanager
    Prometheus用于监控Pod的CPU使用率,Alertmanager用于根据Prometheus报警规则触发动作。Prometheus Alert配置示例: 当某个Pod的CPU使用率超过90%时触发报警:
groups:
- name: pod-cpu-usage
  rules:
  - alert: HighCpuUsage
    expr: sum(rate(container_cpu_usage_seconds_total[1m])) by (pod) / sum(kube_pod_container_resource_limits_cpu_cores) by (pod) > 0.9
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "Pod {{ $labels.pod }} CPU usage is above 90%"
      description: "Pod {{ $labels.pod }} is using more than 90% of its allocated CPU resources."

【备注】:上面这个配置脚本里的expr表达式有问题,下文会给出纠正。

  1. 配置 Alertmanager 动作
    通过Alertmanager的webhook配置触发一个自动化脚本,使用Kubernetes API重启对应的Pod:
receivers:
- name: "restart-pod"
  webhook_configs:
  - url: "http://your-webhook-service/restart-pod"
  1. 实现 Webhook 服务
    使用脚本监听Alertmanager的webhook请求,并调用Kubernetes API重启Pod:
from flask import Flask, request
import subprocess

app = Flask(__name__)

@app.route('/restart-pod', methods=['POST'])
def restart_pod():
	data = request.json
	pod_name = data['alerts'][0]['labels']['pod']
	namespace = "default"  # 根据实际情况修改
	subprocess.run(["kubectl", "delete", "pod", pod_name, "-n", namespace])
	return "Pod restarted", 200

if __name__ == "__main__":
	app.run(host="0.0.0.0", port=5000)

部署Webhook服务并配置为Alertmanager的webhook。

使用Kubernetes Operator

编写自定义Kubernetes Operator,实时监控Pod的CPU使用率,并在达到阈值时通过Kubernetes API(调用delete或restart操作)重启Pod。

实践

方案一:通过资源限制间接实现自动重启

原理:当Pod使用的CPU超过limits限制的数值时,Kubernetes将限制CPU使用,这可能导致应用程序不可用或进入异常状态,从而触发健康检查并重启Pod。

经过实践,限制resources.limits.cpu,并降低livenessProbe.timeoutSeconds的时间(最小可调整到1,即1s)。探针并不能监测到pod失活。也就是说,通过resources.limits.cpu + livenessProbe探针 + restartPolicy: Always,来监测pod已使用的CPU资源,并实现pod自动重启,并不可行,至少是不可靠(不精确)的。另外,限制服务可使用的资源,可能影响服务稳定性。

方案二:自定义监控与重启机制

Prometheus Alert

编写Prometheus Alert配置文件restart-pod.yaml

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  labels:
    # labels与Prometheus CRD中match ruleSelector -> matchLabels保持一致。
    release: kube-prom-stack
  name: kube-state-metrics
spec:
  groups:
  - name: pod-cpu-usage
    rules:
    - alert: HighCpuUsage
      expr: sum by (pod) (rate(container_cpu_usage_seconds_total[1m])) / sum by (pod) (kube_pod_container_resource_limits{resource="cpu"}) > 0.9
      for: 1m
      labels:
        severity: critical
      annotations:
        summary: "Pod {{ $labels.pod }} CPU usage is above 90%"
        description: "Pod {{ $labels.pod }} is using more than 90% of its allocated CPU resources."

然后执行命令:kubectl apply -f restart-pod.yaml -n observe

实际上,这里踩了不少坑。

前提知识:Prometheus Alert有3种状态(如下图):Inactive、Pending、Firing。
在这里插入图片描述

  • Inactive:
  • Pending:
  • Firing

通过kubectl apply命令部署告警规则,打开Prometheus Alert页面,能找到自定义的规则pod-cpu-usage,但是一直处于Inactive状态。而实际上,测试环境好几个pod的CPU使用已经大于k8s限制值。

排查,还是使用Prometheus提供的功能,点击Graph,输入ChatGPT给出的expr表达式sum(rate(container_cpu_usage_seconds_total[1m])) by (pod) / sum(kube_pod_container_resource_limits_cpu_cores) by (pod) > 0.9
在这里插入图片描述
查询不到数据。

经过排查,根本就不存在kube_pod_container_resource_limits_cpu_cores这个Metric,存在kube_pod_container_resource_limits这个Metric,更进一步,存在kube_pod_container_resource_limits{resource="cpu"}这个Metric。

最后的查询效果是这样的:
在这里插入图片描述
kubectl apply部署修改后的restart-pod.yaml,即可看到有几个pod处于Pending状态:
在这里插入图片描述
另外,Prometheus Graph页面比较简单,也可用Grafana来实现,即,可以在Grafana上面验证Prometheus Alert脚本里的expr表达式正确与否。

Alertmanager

接下来就是修改Alertmanager,这里先给出alertmanager.yaml文件配置:

global:
  resolve_timeout: 5m
inhibit_rules:
- equal:
  - namespace
  - alertname
  source_matchers:
  - severity = critical
  target_matchers:
  - severity =~ warning|info
- equal:
  - namespace
  - alertname
  source_matchers:
  - severity = warning
  target_matchers:
  - severity = info
- equal:
  - namespace
  source_matchers:
  - alertname = InfoInhibitor
  target_matchers:
  - severity = info
- target_matchers:
  - alertname = InfoInhibitor
receivers:
- name: "restart-pod"
  webhook_configs:
  - url: "http://33.44.55.66:5000/restart-pod"
route:
  group_by:
  - namespace
  group_interval: 5m
  group_wait: 30s
  receiver: "restart-pod"
  repeat_interval: 12h
  routes:
  - matchers:
    - alertname = "Watchdog"
    receiver: "restart-pod"
templates:
- /etc/alertmanager/config/*.tmpl

主要就是新增一个receivers。

背景知识:Alertmanager是保密字典。

如果对k8s + Helm很熟悉的话,可以在k8s环境下通过命令行来修改。

我不太熟悉,于是通过KubeSphere来操作,点击【配置】-【保密字典】,搜索alertmanager(注意:KubeSphere大小写敏感,搜索Alertmanager将搜索不到结果):
在这里插入图片描述
点击进去,页面是这样,可看到一个alertmanager.yaml配置项:
在这里插入图片描述
点击右上角的展示【隐藏和显示】按钮,可以看到明文,也就是上面结构。

将明文复制出来,在Sublime Text里编辑,增加receivers配置,最后需要将修改后的alertmanager.yaml文本内容,以Base64编码,一个不错的Base64编码-解码在线工具

然后点击alertmanager-kube-prom-stack-alertmanager,编辑YAML,替换更新后的Base64编码内容:
在这里插入图片描述
注意换行和空格:
在这里插入图片描述

Webhook

最后就是调试Python脚本,先给出最终的版本:

from flask import Flask, request
import subprocess
import logging
import jsonpath
logger = logging.getLogger(__name__)

app = Flask(__name__)

@app.route('/restart-pod', methods=['POST'])
def restart_pod():
    logging.basicConfig(filename='restart-pod.log', level=logging.INFO)
    data = request.json
    pods_name = jsonpath.jsonpath(data, '$.alerts[*].labels.pod')
    logger.info(pods_name)
    # 非法JSON解析为bool?
    if isinstance(pods_name, bool):
        logger.info("ignored")
        return "ignored", 200
    namespace = "test-tesla" # 按需修改
    for pod_name in pods_name:
        subprocess.run(["kubectl", "delete", "pod", pod_name, "-n", namespace])
        logger.info("kubectl delete pod:%s", pod_name)
        return "Pod restarted", 200

if __name__ == "__main__":
    app.run(host="33.44.55.66", port=5000)

这里遇到的问题:

  • ModuleNotFoundError: No module named ‘flask’
  • 不好调试,引入logging模块
  • 解析JSON响应
  • 引入JsonPath模块
  • 获取到的pod不止一个,也就是上面截图里看到的,有3个pod处于Pending状态,因此引入for循环
  • 其他报错,如:for pod_name in pods_name: TypeError: 'bool' object is not iterable

后台进程运行Python脚本的命令为:nohup python restart-pod-webhook.py > tmp.log

遗留问题

通过JsonPath表达式'$.alerts[*].labels.pod'解析responseBody,不知道为啥会解析到bool类型数据。

方案三:Kubernetes Operator

编写自定义Kubernetes Operator,实时监控Pod的CPU使用率,并在达到阈值时通过Kubernetes API重启 Pod。
有不低的门槛,需要熟悉K8S Operator框架,了解Go语法,熟悉k8s提供的API。

参考

  • ChatGPT
  • Google

http://www.kler.cn/a/469475.html

相关文章:

  • Midjourney 应用:框架总结
  • Go语言的数据库交互
  • 1/7 C++
  • JAVA创建绘图板JAVA构建主窗口鼠标拖动来绘制线条
  • 【Linux】传输层协议UDP
  • 【UI自动化测试】selenium八种定位方式
  • Python爬虫基础——认识网页结构(各种标签的使用)
  • Redis 基础篇
  • LabVIEW专栏十、工厂模式
  • Python的各种各样基础
  • IDEA 撤销 merge 操作(详解)
  • 安装和配置MySQL教程
  • 科研绘图系列:R语言单细胞数据常见的可视化图形
  • [jsoncpp]JSON序列化与反序列化
  • 基于 Python Django 的社区爱心养老系统
  • 位置编码--RoPE
  • 单细胞组学大模型(7)--- GenePT,一个可以在本地部署和使用的单细胞转录组大模型
  • 【设计模式-1】软件设计模式概述
  • k8s修改存储目录-介绍
  • Docker 安装Elasticsearch搜索引擎 搜索优化 词库挂载 拼音分词 插件安装
  • Linux 防火墙:守护系统安全的坚固防线
  • 今日头条ip属地根据什么显示?不准确怎么办
  • 渗透测试--Web基础漏洞利用技巧
  • 浅谈棋牌游戏开发流程七:反外挂与安全体系——守护游戏公平与玩家体验
  • C# 设计模式(行为型模式):解释器模式
  • ✅binlog、redolog和undolog区别?