基于Prometheus智能化监控告警系统

  在k8s集群中部署prometheus、grafana、alertmanager,并且配置prometheus的动态、静态服务发现,实现对容器、物理节点、service、pod等资源指标监控,并在Grafana的web界面展示prometheus的监控指标,然后通过配置自定义告警规则,通过alertmanager实现qq、钉钉、微信报警。

一、Prometheus简介

  Prometheus是一个开源的服务监控系统和时序数据库,最初是 SoundCloud 构建的开源系统监控和报警工具,是一个独立的开源项目,于2016年加入了 CNCF 基金会,作为继 Kubernetes 之后的第二个托管项目。目前已经成为kubernetes生态圈中的核心监控系统,而且越来越多的项目都提供了对prometheus的原生支持。在kubernetes容器管理系统中,通常会搭配prometheus进行监控,同时也支持多种exporter采集数据,还支持pushgateway进行数据上报,Prometheus性能足够支撑上万台规模的集群。

1. Prometheus特点

1)多维度数据模型

  每一个时间序列数据都由metric度量指标名称和它的标签labels键值对集合唯一确定。

  metric度量指标名称指定监控目标系统的测量特征。(如:http_requests_total- 接收http请求的总计数)

  labels开启了Prometheus的多维数据模型:对于相同的度量名称,通过不同标签列表的结合,会形成特定的度量维度实例。(所有包含度量名称为/api/tracks的http请求,打上method=POST的标签,则形成了具体的http请求)

  查询语言在这些度量和标签列表的基础上进行过滤和聚合。改变任何度量上的任何标签值,则会形成新的时间序列图。

  样本:在时间序列中的每一个点称为一个样本(sample),样本由以下三部分组成:

(1)指标(metric):指标名称和描述当前样本特征的 labelsets;
(2)时间戳(timestamp):一个精确到毫秒的时间戳;
(3)样本值(value): 一个 folat64 的浮点型数据表示当前样本的值。  

  表示方式:通过如下表达方式表示指定指标名称和指定标签集合的时间序列

<metric name>{<label name>=<label value>, ...}

  例如,指标名称为 api_http_requests_total,标签为 method="POST" 和 handler="/messages" 的时间序列可以表示为:

api_http_requests_total{method="POST", handler="/messages"}

2)灵活的查询语言(PromQL)

  可以对采集的metrics指标进行加法,乘法,连接等操作。

3)可以直接在本地部署,不依赖其他分布式存储;

4)通过基于HTTP的pull方式采集时序数据;

5)可以通过中间网关pushgateway的方式把时间序列数据推送到prometheus server端;

6)可通过服务发现或者静态配置来发现目标服务对象(targets)

7)有多种可视化图像界面,如Grafana等

8)高效的存储,每个采样数据占3.5 bytes左右,300万的时间序列,30s间隔,保留60天,消耗磁盘大概200G。

9)做高可用,可以对数据做异地备份,union集群,部署多套prometheus,pushgateway上报数据

2. Prometheus组件介绍

  Prometheus提供了通用的数据模型和便捷的数据采集、存储和查询接口。其核心组件Prometheus Server定期从静态配置的监控目标或者基于服务发现自动配置的目标中拉取数据,新拉取到的数据大于配置内存缓存区时,数据将持久化到存储设备中,包括远程云端存储系统。

1)Prometheus Server: 用于收集和存储时间序列数据

2)Client Library: 客户端库,检测应用程序代码,当Prometheus抓取实例的HTTP端点时,客户端库会将所有跟踪的metrics指标的当前状态发送到prometheus server端

3)Exporters: prometheus支持多种exporter,通过exporter可以采集metrics数据,然后发送到prometheus server端,所有向promtheus server提供监控数据的程序都可以被称为exporter

4)Alertmanager: 从 Prometheus server 端接收到 alerts 后,会进行去重,分组,并路由到相应的接收方,发出报警,常见的接收方式有:电子邮件,微信,钉钉, slack等

5)Grafana:监控仪表盘,可视化监控数据

6)pushgateway: 各个目标主机可上报数据到pushgateway,然后prometheus server统一从pushgateway拉取数据

  上图所示的各个组件中,每个被监控的目标(主机、应用程序等)都可通过专用的exporters程序提供输出监控数据的接口,并等待Prometheus server周期性的数据抓取操作。若存在告警规则,则抓取到数据后会检查并根据规则进行计算,满足告警条件即会生成告警,并发送到Alertmanager完成告警汇总和分发等操作。被监控目标有主动推送数据的需求时,可部署Pushgateway组件接收并临时存储数据,并等待Prometheus Server完成数据采集。

  其中Prometheus Server组件由Retrieval,Storage,PromQL三部分组成:

1)Retrieval负责在活跃的target主机上抓取监控指标数据

2)Storage存储主要是把采集到的数据存储到磁盘中

3)PromQL是Prometheus提供的查询语言模块

3. Prometheus工作流程

1)Prometheus server可定期从活跃的(up)目标主机上(target)拉取监控指标数据,目标主机的监控数据可通过配置静态job或者服务发现的方式被prometheus server采集到,这种方式默认的pull方式拉取指标;也可通过pushgateway把采集的数据上报到prometheus server中;还可通过一些组件自带的exporter采集相应组件的数据

2)Prometheus server把采集到的监控指标数据保存到本地磁盘或者数据库;

3)Prometheus采集的监控指标数据按时间序列存储,通过配置告警规则,把触发的告警发送到Alertmanager

4)Alertmanager通过配置告警接收方,发送告警到邮件,微信或者钉钉等

5)Prometheus 自带的web ui界面提供PromQL查询语言,可查询监控数据

6)Grafana可接入prometheus数据源,把监控数据以图形化形式展示出

4. Prometheus和zabbix对比分析

 二、Prometheus的四种数据类型

1. Counter 计数器类型 

1)Counter 用于累计值,例如记录请求次数、任务完成数、错误发生次数。

2)一直增加,不会减少

3)重启进程后,会被重置

  例如:http_response_total{method="GET",endpoint="/api/tracks"} 100

     http_response_total{method="GET",endpoint="/api/tracks"} 160

  Counter 类型数据可以让用户方便的了解事件产生的速率的变化,在PromQL内置的相关操作函数可以提供相应的分析。

  比如以HTTP应用请求量来进行说明:
1)通过rate()函数获取HTTP请求量的增长率

rate(http_requests_total[5m])

2)查询当前系统中,访问量前10的HTTP地址

topk(10, http_requests_total)

2. Gauge测量器类型

1)Gauge是常规数值,例如温度变化、内存使用变化

2)可变大,可变小

3)重启进程后,会被重置

  例如:memory_usage_bytes{host="master-01"} 100
     memory_usage_bytes{host="master-01"} 50
                  memory_usage_bytes{host="master-01"} 80

  对于 Gauge 类型的监控指标,通过 PromQL 内置函数 delta() 可以获取样本在一段时间内的变化情况。

  例如,计算 CPU 温度在两小时内的差异:

dalta(cpu_temp_celsius{host="zeus"}[2h])

  还可以通过PromQL 内置函数 predict_linear() 基于简单线性回归的方式,对样本数据的变化趋势做出预测。

  例如,基于 2 小时的样本数据,来预测主机可用磁盘空间在 4 个小时之后的剩余情况:

predict_linear(node_filesystem_free{job="node"}[2h], 4 * 3600) < 0

3. histogram

histogram是柱状图,在Prometheus系统的查询语言中,有三种作用:

1)在一段时间范围内对数据进行采样(通常是请求持续时间或响应大小等),并将其计入可配置的存储桶(bucket)中. 后续可通过指定区间筛选样本,也可以统计样本总数,最后一般将数据展示为直方图。

2)对每个采样点值累计和(sum)

3)对采样点的次数累计和(count)

  度量指标名称: [basename]_上面三类的作用度量指标名称

1)[basename]_bucket{le="上边界"}, 这个值为小于等于上边界的所有采样点数量

2)[basename]_sum

3)[basename]_count

小结:如果定义一个度量类型为Histogram,则Prometheus会自动生成三个对应的指标。

  为什么需要用histogram柱状图?

  在大多数情况下人们都倾向于使用某些量化指标的平均值,例如 CPU 的平均使用率、页面的平均响应时间。这种方式的问题很明显,以系统 API 调用的平均响应时间为例:如果大多数 API 请求都维持在 100ms 的响应时间范围内,而个别请求的响应时间需要 5s,那么就会导致某些 WEB 页面的响应时间落到中位数的情况,而这种现象被称为长尾问题。

  为了区分是平均的慢还是长尾的慢,最简单的方式就是按照请求延迟的范围进行分组。例如,统计延迟在 0~10ms 之间的请求数有多少,而 10~20ms 之间的请求数又有多少。通过这种方式可以快速分析系统慢的原因。 

  Histogram 类型的样本会提供三种指标(假设指标名称为 <basename>):
  样本的值分布在 bucket 中的数量,命名为 <basename>_bucket{le="<上边界>"}:这个值表示指标值小于等于上边界的所有样本数量。

例如:在总共2次请求当中。http 请求响应时间 <=0.5 秒 的请求次数为0,采集到的数据表示为:

io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.5",} 0.0

   在总共2次请求当中。http 请求响应时间 <=1.0秒 的请求次数为0,数据表示为:

io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="1.0",} 0.0

    在总共2次请求当中。http 请求响应时间 <=2.5 秒 的请求次数为0

io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="2.5",} 0.0

    在总共2次请求当中。http 请求响应时间 <=5.0 秒 的请求次数为0

io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="5.0",} 0.0

    在总共2次请求当中。http 请求响应时间 <=7.5 秒 的请求次数为2

io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="7.5",} 2.0

    在总共2次请求当中。http 请求响应时间 <=10 秒 的请求次数为 2

io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="10",} 2.0 

  所有样本值的大小总和,命名为 <basename>_sum

例如:发生的2次 http 请求总的响应时间为 13.107670803000001 秒

io_namespace_http_requests_latency_seconds_histogram_sum{path="/",method="GET",code="200",} 13.107670803000001

  样本总数值,命名为 <basename>_count

例如:当前一共发生了 2 次 http 请求

io_namespace_http_requests_latency_seconds_histogram_count{path="/",method="GET",code="200",} 2.0

  注:bucket 可以理解为是对数据指标值域的一个划分,划分的依据应该基于数据值的分布。注意后面的采样点是包含前面的采样点的,假设 xxx_bucket{...,le="0.01"} 的值为 10,而 xxx_bucket{...,le="0.05"} 的值为 30,那么意味着这 30 个采样点中,有 10 个是小于 0.01s的,其余 20 个采样点的响应时间是介于0.01s 和 0.05s之间的。可以通过 histogram_quantile() 函数来计算 Histogram 类型样本的分位数。分位数可能不太好理解,可以理解为分割数据的点。如,假设样本的 9 分位数(quantile=0.9)的值为 x,即表示小于 x 的采样值的数量占总体采样值的 90%。Histogram 还可以用来计算应用性能指标值(Apdex score)。 

4. summary

  与 Histogram 类型类似,用于表示一段时间内的数据采样结果(通常是请求持续时间或响应大小等),但它直接存储了分位数(通过客户端计算,然后展示出来),而不是通过区间来计算。它也有三种作用:

1)对于每个采样点进行统计,并形成分位图。(如:正态分布一样,统计低于60分不及格的同学比例,统计低于80分的同学比例,统计低于95分的同学比例)

2)统计班上所有同学的总成绩(sum)

3)统计班上同学的考试总人数(count)

  带有度量指标的[basename]的summary 在抓取时间序列数据有如命名

1)观察时间的φ-quantiles (0 ≤ φ ≤ 1), 显示为[basename]{分位数="[φ]"}

2)[basename]_sum, 是指所有观察值的总和

3)[basename]_count, 是指已观察到的事件计数值

  样本值的分位数分布情况,命名为 <basename>{quantile="<φ>"}
例如:12 次 http 请求中有 50% 的请求响应时间是 3.052404983s

io_namespace_http_requests_latency_seconds_summary{path="/",method="GET",code="200",quantile="0.5",} 3.052404983

    12 次 http 请求中有 90% 的请求响应时间是 8.003261666s

io_namespace_http_requests_latency_seconds_summary{path="/",method="GET",code="200",quantile="0.9",} 8.003261666

  所有样本值的大小总和,命名为 <basename>_sum
例如:12次 http 请求的总响应时间为 51.029495508s

io_namespace_http_requests_latency_seconds_summary_sum{path="/",method="GET",code="200",} 51.029495508

  样本总数,命名为 <basename>_count
例如:当前一共发生了 12 次 http 请求

io_namespace_http_requests_latency_seconds_summary_count{path="/",method="GET",code="200",} 12.0

  总结一下 Histogram 与 Summary 的异同:
它们都包含了 <basename>_sum 和 <basename>_count 指标。Histogram 需要通过 <basename>_bucket 来计算分位数,而 Summary 则直接存储了分位数的值。

例如,记录下列数据样本如下:

prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.5"} 0.012352463
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.9"} 0.014458005
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.99"} 0.017316173
prometheus_tsdb_wal_fsync_duration_seconds_sum 2.888716127000002
prometheus_tsdb_wal_fsync_duration_seconds_count 216

  从上面的样本中可以得知当前Promtheus Server进行wal_fsync操作的总次数为216次,耗时2.888716127000002s。其中中位数(quantile=0.5)的耗时为0.012352463,9分位数(quantile=0.9)的耗时为0.014458005s。

三、Prometheus监控目标

  Prometheus可以监控:Databases,Hardware related,Messaging systems,Storage,HTTP,APIs,Logging,Other monitoring systems,Miscellaneous,Software exposing Prometheus metrics等。 

  其中,Prometheus对kubernetes的监控:对于Kubernetes而言,可以把当中所有的资源分为几类:

• 基础设施层(Node):集群节点,为整个集群和应用提供运行时资源

• 容器基础设施(Container):为应用提供运行时环境

• 用户应用(Pod):Pod中会包含一组容器,它们一起工作,并且对外提供一个(或者一组)功能

• 内部服务负载均衡(Service):在集群内,通过Service在集群暴露应用功能,集群内应用和应用之间访问时提供内部的负载均衡

• 外部访问入口(Ingress):通过Ingress提供集群外的访问入口,从而可以使外部客户端能够访问到部署在Kubernetes集群内的服务

  因此,如果要构建一个完整的监控体系,应该考虑,以下5个方面:

1)集群节点状态监控:从集群中各节点的kubelet服务获取节点的基本运行状态;

2)集群节点资源用量监控:通过Daemonset的形式在集群中各个节点部署Node Exporter采集节点的资源使用情况;

3)节点中运行的容器监控:通过各个节点中kubelet内置的cAdvisor中获取个节点中所有容器的运行状态和资源使用情况;

4)如果在集群中部署的应用程序本身内置了对Prometheus的监控支持,那么还应该找到相应的Pod实例,并从该Pod实例中获取其内部运行状态的监控指标

5)对k8s本身的组件做监控:apiserver、scheduler、controller-manager、kubelet、kube-proxy

  任何被监控的目标都需要事先纳入到监控系统中才能进行时序数据采集、存储、告警即相关的展示等,监控目标既可以通过配置信息以静态形式指定,也可以让Prometheus通过服务发现机制动态管理(增删等),对于变动频繁的系统环境(如云容器环境)来说,这种动态管理机制尤为有用。在kubernetes集群及相关的环境中,除了配置的从pod对象中的容器应用获得资源指标数据以外,Prometheus还支持通过多个监控目标采集kubernetes监控架构体系中所谓的"非核心指标数据":

1. 监控代理程序

  监控代理程序,如node_exporter,收集标准的主机指标数据,包括平均负载、CPU、Memory、Disk、Network及诸多其他维度的数据,独立的指标可能多达上千个。

2. kubelet(cAdvisor)

  kubelet(cAdvisor)收集容器指标数据,它们也是所谓的kubernetes“核心指标”,每个容器的相关指标数据主要有CPU利用率(user和system)及限额、文件系统读/写限额、内存利用率及限额、网络报文发送/接收/丢弃速率等。

3. API Server

  API Server收集API Server的性能指标数据,包括控制工作队列的性能、请求速率与延迟时长、etcd缓存工作队列及缓存性能、普通进程状态(文件描述符、内存、CPU等)、Golang状态(GC、内存和线程等)。

4. etcd

  etcd收集etcd存储集群的相关指标数据,包括领导节点及领域变动速率、提交/应用/挂起/错误的提案次数、磁盘写入性能、网络与gRPC计数器等。

5. kube-state-metrics

  kube-state-metrics此组件能够派生出kubernetes相关的多个指标数据,主要是资源类型相关的计数器和元数据信息,包括指定类型的对象总数、资源限额、容器状态(ready/restart/running/terminated/waiting)以及Pod资源的标签系列等。

  Prometheus能够直接把kubernetes API Server作为服务发现系统使用进而动态发现和监控集群中的所有可被监控的对象。这里需要说明的是,Pod资源需要添加下列注解信息才能被Prometheus系统自动发现并抓取其内建的指标数据。

1)prometheus.io/scrape:用于标识是否需要被采集指标数据,布尔型值,true或false

2)prometheus.io/path:抓取指标数据时使用的URL路径,一般为/metrics

3)prometheus.io/port:抓取指标数据时使用的套接字端口,如8080

  另外,仅期望Prometheus为后端生成自定义指标时仅部署Prometheus Server即可,它甚至也不需要数据持久功能。但若要配置完整功能的监控系统,管理员还需要在每个主机上部署node_exporter、按需部署其他类型的exporter以及Alertmanager。

四、部署Prometheus监控系统

  部署一个完整的Prometheus监控环境,需要部署Prometheus Server、Alertmanager、node_exporter和kube-state-metrics四个组件。下面分别介绍四个组件的部署过程。

1. 部署node_exporter

  Prometheus通过HTTP周期性抓取指标数据,监控目标上用于接收并响应数据抓取请求的组件成为export。目前Prometheus项目及社区提供了众多现成可用的exporter,分别用于监控操作系统、数据库、硬件设备、消息队列、存储系统等类型的多种目标对象。

  注:Prometheus的常用exporter列表位于:https://prometheus.io/docs/instrumenting/exporters/

  在监控目标系统上,Exporter将收集的数据转化为文本格式,并通过HTTP对外暴露相应的接口,它不会局限于任何编程语言或实现方式,只需要它能够接收来自prometheus server端的数据抓取请求,并以特定格式的数据响应,因此它尤其适用于容器环境。

(1)Exporter的响应内容以行为单位,其中,以“#HELP”开头的是注解信息用于提供指标说明,以“#TYPE”开头的注解行用于指明当前指标的类型,它可以是counter、gauge、histogram或summary其中之一;空白行将被忽略。

(2)每一个非注解行都是一个键值类型的数据,其键即是指标,相应的值是当前指标本次响应的float类型的数据;若返回的值有两个,则最后一个会被当做时间戳;响应值必须要用双引号进行引用,否则即为格式错误。

1). node_exporter介绍

  prometheus为监控类UNIX操作系统提供了一个专用的node_exporter程序,它能够收集多种系统级指标,如conntrack、cpu、diskstats、meminfo、filesystem、netstat和socketstats等数十种。node_exporter运行为守护进程监听于节点上的9100端口,通过URL路径/metrics提供指标数据,Prometheus Server可在配置文件中使用静态配置监控每一个运行node_exporter的目标主机,也能够使用基于文件、Consul、DNS、Kubernetes等多种服务发现机制动态添加监控对象。

2). 安装node_exporter

  在集群中各节点上部署并运行node_exporter,它将以DaemonSet控制器对象于各节点中运行一个相应的pod资源。

  创建名称空间

[root@k8s-master1 ~]# mkdir prometheus
[root@k8s-master1 ~]# cd prometheus/
[root@k8s-master1 prometheus]# kubectl create ns monitor-sa
namespace/monitor-sa created
[root@k8s-master1 prometheus]# kubectl get ns monitor-sa -o wide
NAME         STATUS   AGE
monitor-sa   Active   10s

  拉取node-exporter镜像,https://hub.docker.com/r/prom/node-exporter/tags,使用v0.16.0版本

docker pull prom/node-exporter:v0.16.0

  编写资源清单配置文件node-exporter.yaml

[root@k8s-master1 prometheus]# vim node-exporter.yaml
You have new mail in /var/spool/mail/root
[root@k8s-master1 prometheus]# cat node-exporter.yaml
apiVersion: apps/v1
kind: DaemonSet  #可以保证k8s集群的每个节点都运行完全一样的pod
metadata:
  name: node-exporter
  namespace: monitor-sa
  labels:
    name: node-exporter
spec:
  selector:
    matchLabels:
     name: node-exporter
  template:
    metadata:
      labels:
        name: node-exporter
    spec:
      hostPID: true  #hostNetwork、hostIPC、hostPID都为True时,表示这个Pod里的所有容器,会直接使用宿主机的网络,直接与宿主机进行IPC(进程间通信)通信,可以看到宿主机里正在运行的所有进程
      hostIPC: true
      hostNetwork: true  #加入了hostNetwork:true会直接将宿主机的9100端口映射出来,从而不需要创建service 在宿主机上就会有一个9100的端口
      containers:
      - name: node-exporter
        image: prom/node-exporter:v0.16.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9100
        resources:
          requests:
            cpu: 0.15   #这个容器运行至少需要0.15核cpu
        securityContext:
          privileged: true   #开启特权模式
        args:
        - --path.procfs  #配置挂载宿主机(node节点)的路径
        - /host/proc    
        - --path.sysfs  #配置挂载宿主机(node节点)的路径
        - /host/sys
        - --collector.filesystem.ignored-mount-points
        - '"^/(sys|proc|dev|host|etc)($|/)"'    #通过正则表达式忽略某些文件系统挂载点的信息收集
        volumeMounts:
        - name: dev
          mountPath: /host/dev
        - name: proc
          mountPath: /host/proc
        - name: sys
          mountPath: /host/sys
        - name: rootfs
          mountPath: /rootfs  #将主机/dev、/proc、/sys这些目录挂在到容器中,这是因为我们采集的很多节点数据都是通过这些文件来获取系统信息的
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"
        effect: "NoSchedule"
      volumes:
        - name: proc
          hostPath:
            path: /proc
        - name: dev
          hostPath:
            path: /dev
        - name: sys
          hostPath:
            path: /sys
        - name: rootfs
          hostPath:
            path: /

  通过kubectl apply更新node-exporter.yaml文件

[root@k8s-master1 prometheus]# kubectl apply -f node-exporter.yaml
daemonset.apps/node-exporter created
[root@k8s-master1 prometheus]# kubectl get pods -n monitor-sa
NAME                  READY   STATUS    RESTARTS   AGE
node-exporter-k4wsq   1/1     Running   0          27s
node-exporter-x84r5   1/1     Running   0          27s
node-exporter-zrwvh   1/1     Running   0          27s

  node_exporter相关的各pod对象共享使用其所在节点的网络名称空间,它直接监听于相关地址的9100端点,因此,可直接对其中某一个节点发起请求测试:

[root@k8s-master1 prometheus]# curl http://10.0.0.131:9100/metrics
# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 6
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.9.6"} 1
....

  node-export默认的监听端口是9100,可以看到当前主机获取到的所有监控数据 

[root@k8s-master1 prometheus]# curl http://10.0.0.132:9100/metrics |grep node_cpu_seconds
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0# HELP node_cpu_seconds_total Seconds the cpus spent in each mode.
# TYPE node_cpu_seconds_total counter
node_cpu_seconds_total{cpu="0",mode="idle"} 1859.63
node_cpu_seconds_total{cpu="0",mode="iowait"} 50.02
node_cpu_seconds_total{cpu="0",mode="irq"} 0
node_cpu_seconds_total{cpu="0",mode="nice"} 0
node_cpu_seconds_total{cpu="0",mode="softirq"} 3.32
node_cpu_seconds_total{cpu="0",mode="steal"} 0
node_cpu_seconds_total{cpu="0",mode="system"} 65.86
node_cpu_seconds_total{cpu="0",mode="user"} 23.98
node_cpu_seconds_total{cpu="1",mode="idle"} 1851.69
node_cpu_seconds_total{cpu="1",mode="iowait"} 55.93
node_cpu_seconds_total{cpu="1",mode="irq"} 0
node_cpu_seconds_total{cpu="1",mode="nice"} 0
node_cpu_seconds_total{cpu="1",mode="softirq"} 3.33
node_cpu_seconds_total{cpu="1",mode="steal"} 0
node_cpu_seconds_total{cpu="1",mode="system"} 71.98
node_cpu_seconds_total{cpu="1",mode="user"} 30.7
node_cpu_seconds_total{cpu="2",mode="idle"} 1882.95
node_cpu_seconds_total{cpu="2",mode="iowait"} 31.27
node_cpu_seconds_total{cpu="2",mode="irq"} 0
node_cpu_seconds_total{cpu="2",mode="nice"} 0
node_cpu_seconds_total{cpu="2",mode="softirq"} 4.53
node_cpu_seconds_total{cpu="2",mode="steal"} 0
node_cpu_seconds_total{cpu="2",mode="system"} 63.41
node_cpu_seconds_total{cpu="2",mode="user"} 25.93
node_cpu_seconds_total{cpu="3",mode="idle"} 1860.56
node_cpu_seconds_total{cpu="3",mode="iowait"} 44.13
node_cpu_seconds_total{cpu="3",mode="irq"} 0
node_cpu_seconds_total{cpu="3",mode="nice"} 0
node_cpu_seconds_total{cpu="3",mode="softirq"} 2.28
node_cpu_seconds_total{cpu="3",mode="steal"} 0
node_cpu_seconds_total{cpu="3",mode="system"} 69.66
node_cpu_seconds_total{cpu="3",mode="user"} 27.27
100 64053  100 64053    0     0   511k      0 --:--:-- --:--:-- --:--:--  516k 

其中,#HELP:解释当前指标的含义,上面表示在每种模式下node节点的cpu花费的时间,以s为单位

          #TYPE:说明当前指标的数据类型,上面是counter类型

          node_cpu_seconds_total{cpu="0",mode="idle"} 1859.63:cpu0上idle进程占用CPU的总时间为1859.63s,CPU占用时间是一个只增不减的度量指标,从类型中也可以看出node_cpu的数据类型是counter(计数器)。counter计数器:只是采集递增的指标。

[root@k8s-master1 prometheus]# curl http://10.0.0.133:9100/metrics |grep node_load
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 81025  100 81025    0     0   829k      0 --:--:-- --:--:-- --:--:--  832k
# HELP node_load1 1m load average.
# TYPE node_load1 gauge
node_load1 1.05
# HELP node_load15 15m load average.
# TYPE node_load15 gauge
node_load15 0.7
# HELP node_load5 5m load average.
# TYPE node_load5 gauge
node_load5 0.83

其中,node_load1该指标反映了10.0.0.133主机在最近一分钟以内的负载情况,系统的负载情况会随系统资源的使用而变化,因此node_load1反映的是当前状态,数据可能增加也可能减少,从注释中可以看出当前指标类型为gauge(标准尺寸)。gauge标准尺寸:统计的指标可增加可减少。

2. 部署Prometheus Server

  Prometheus Server是整个监控系统的核心,它通过各exporter周期性采集指标数据,存储于本地的TSDB(Time Series Database)后端存储系统中,并通过PromQL向客户端提供查询接口。

1)创建Service Account,并对其进行rbac授权

[root@k8s-master1 prometheus]#kubectl create serviceaccount monitor -n monitor-sa
serviceaccount/monitor created
[root@k8s-master1 prometheus]# kubectl get sa monitor -n monitor-sa
NAME      SECRETS   AGE
monitor   1         90s

  将sa账号monitor通过clusterrolebing绑定到clusterrole上

[root@k8s-master1 prometheus]# kubectl create clusterrolebinding monitor-clusterrolebinding -n monitor-sa --clusterrole=cluster-admin  --serviceaccount=monitor-sa:monitor
clusterrolebinding.rbac.authorization.k8s.io/monitor-clusterrolebinding created
[root@k8s-master1 prometheus]# kubectl get clusterrolebinding monitor-clusterrolebinding
NAME                         ROLE                        AGE
monitor-clusterrolebinding   ClusterRole/cluster-admin   20s

2)创建prometheus数据存储目录

  计划数据存储在k8s-node1,在k8s-node1节点上创建存储目录:

[root@k8s-node1 ~]# mkdir -p /prometheus/data
[root@k8s-node1 ~]# chmod -R 777 /prometheus/data

3)创建存储卷资源

  创建一个configmap存储卷,用来存放prometheus配置信息

[root@k8s-master1 prometheus]# vim prometheus-cfg.yaml
[root@k8s-master1 prometheus]# cat prometheus-cfg.yaml
---
kind: ConfigMap
apiVersion: v1
metadata:
  labels:
    app: prometheus
  name: prometheus-config
  namespace: monitor-sa
data:
  prometheus.yml: |
    global:
      scrape_interval: 15s   #采集目标主机监控据的时间间隔
      scrape_timeout: 10s    # 数据采集超时时间,默认10s
      evaluation_interval: 1m  #触发告警检测的时间,默认是1m
    scrape_configs:   #scrape_configs:配置数据源,称为target,每个target用job_name命名。又分为静态配置和服务发现
    - job_name: 'kubernetes-node'
      kubernetes_sd_configs:  #使用的是k8s的服务发现
      - role: node    #使用node角色,它使用默认的kubelet提供的http端口来发现集群中每个node节点
      relabel_configs:    #重新标记
      - source_labels: [__address__]   #配置的原始标签,匹配地址
        regex: '(.*):10250'     #匹配带有10250端口的url
        replacement: '${1}:9100'  #把匹配到的ip:10250的ip保留
        target_label: __address__   #新生成的url是${1}获取到的ip:9100
        action: replace
      - action: labelmap  #匹配到下面正则表达式的标签会被保留,如果不做regex正则的话,默认只是会显示instance标签
        regex: __meta_kubernetes_node_label_(.+)
    - job_name: 'kubernetes-node-cadvisor'   # 抓取cAdvisor数据,是获取kubelet上/metrics/cadvisor接口数据来获取容器的资源使用情况
      kubernetes_sd_configs:
      - role:  node
      scheme: https
      tls_config:
        ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
      bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
      relabel_configs:
      - action: labelmap   #把匹配到的标签保留
        regex: __meta_kubernetes_node_label_(.+)   #保留匹配到的具有__meta_kubernetes_node_label的标签
      - target_label: __address__             #获取到的地址:__address__="10.0.0.131:10250"
        replacement: kubernetes.default.svc:443   #把获取到的地址替换成新的地址kubernetes.default.svc:443
      - source_labels: [__meta_kubernetes_node_name]  #把原始标签中__meta_kubernetes_node_name值匹配到  
        regex: (.+)   
        target_label: __metrics_path__    #获取__metrics_path__对应的值
        replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor   #把metrics替换成新的值api/v1/nodes/{k8s-master1/k8s-node1/k8s-node2}/proxy/metrics/cadvisor,${1}是__meta_kubernetes_node_name获取到的值,新的url就是https://kubernetes.default.svc:443/api/v1/nodes/{k8s-master1/k8s-node1/k8s-node2}/proxy/metrics/cadvisor
    - job_name: 'kubernetes-apiserver'
      kubernetes_sd_configs:
      - role: endpoints  #使用k8s中的endpoint服务发现,采集apiserver 6443端口获取到的数据
      scheme: https
      tls_config:
        ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
      bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
      relabel_configs:
      - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
        action: keep   #采集满足条件的实例,其他实例不采集
        regex: default;kubernetes;https  #正则匹配到的默认空间下的service名字是kubernetes,协议是https的endpoint类型保留下来
    - job_name: 'kubernetes-service-endpoints'
      kubernetes_sd_configs:
      - role: endpoints
      relabel_configs: 
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]  
        action: keep
        regex: true 
        # 重新打标仅抓取到的具有 "prometheus.io/scrape: true" 的annotation的端点,意思是说如果某个service具有prometheus.io/scrape = true annotation声明则抓取,annotation本身也是键值结构,所以这里的源标签设置为键,而regex设置值true,当值匹配到regex设定的内容时则执行keep动作也就是保留,其余则丢弃。       
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
        action: replace
        target_label: __scheme__
        regex: (https?)
        #重新设置scheme,匹配源标签__meta_kubernetes_service_annotation_prometheus_io_scheme也就是prometheus.io/scheme annotation,如果源标签的值匹配到regex,则把值替换为__scheme__对应的值
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
        # 应用中自定义暴露的指标,也许暴露的API接口不是/metrics这个路径,那么可以在这个POD对应的service中做一个"prometheus.io/path = /mymetrics" 声明,上面的意思就是把声明的这个路径赋值给__metrics_path__,其实就是让prometheus来获取自定义应用暴露的metrices的具体路径,不过这里写的要和service中做好约定,如果service中这样写 prometheus.io/app-metrics-path: '/metrics' 那么这里就要__meta_kubernetes_service_annotation_prometheus_io_app_metrics_path这样写。

      - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
        action: replace
        target_label: __address__
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
        # 暴露自定义的应用的端口,就是把地址和在service中定义的 "prometheus.io/port = <port>" 声明做一个拼接,然后赋值给__address__,这样prometheus就能获取自定义应用的端口,然后通过这个端口再结合__metrics_path__来获取指标,如果__metrics_path__值不是默认的/metrics那么就要使用上面的标签替换来获取真正暴露的具体路径。
      - action: labelmap  #保留下面匹配到的标签
        regex: __meta_kubernetes_service_label_(.+)
      - source_labels: [__meta_kubernetes_namespace]
        action: replace  #替换__meta_kubernetes_namespace变成kubernetes_namespace
        target_label: kubernetes_namespace
      - source_labels: [__meta_kubernetes_service_name]
        action: replace   
        target_label: kubernetes_name
[root@k8s-master1 prometheus]# kubectl apply -f prometheus-cfg.yaml
configmap/prometheus-config created
[root@k8s-master1 prometheus]# kubectl get configmap prometheus-config -n monitor-sa
NAME                DATA   AGE
prometheus-config   1      45s

  通过kubectl describe命令查看创建的configmap详细信息:

[root@k8s-master1 prometheus]# kubectl describe configmap prometheus-config -n monitor-sa
Name:         prometheus-config
Namespace:    monitor-sa
Labels:       app=prometheus
Annotations:  <none>

Data
====
prometheus.yml:
----
global:
  scrape_interval: 15s
  scrape_timeout: 10s
  evaluation_interval: 1m
scrape_configs:
- job_name: 'kubernetes-node'
  kubernetes_sd_configs:
  - role: node
  relabel_configs:
  - source_labels: [__address__]
    regex: '(.*):10250'
    replacement: '${1}:9100'
    target_label: __address__
    action: replace
  - action: labelmap
    regex: __meta_kubernetes_node_label_(.+)
- job_name: 'kubernetes-node-cadvisor'
  kubernetes_sd_configs:
  - role:  node
  scheme: https
  tls_config:
    ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
  relabel_configs:
  - action: labelmap
    regex: __meta_kubernetes_node_label_(.+)
  - target_label: __address__
    replacement: kubernetes.default.svc:443
  - source_labels: [__meta_kubernetes_node_name]
    regex: (.+)
    target_label: __metrics_path__
    replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
- job_name: 'kubernetes-apiserver'
  kubernetes_sd_configs:
  - role: endpoints
  scheme: https
  tls_config:
    ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
  relabel_configs:
  - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
    action: keep
    regex: default;kubernetes;https
- job_name: 'kubernetes-service-endpoints'
  kubernetes_sd_configs:
  - role: endpoints
  relabel_configs:
  - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
    action: keep
    regex: true
  - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
    action: replace
    target_label: __scheme__
    regex: (https?)
  - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
    action: replace
    target_label: __metrics_path__
    regex: (.+)
  - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
    action: replace
    target_label: __address__
    regex: ([^:]+)(?::\d+)?;(\d+)
    replacement: $1:$2
  - action: labelmap
    regex: __meta_kubernetes_service_label_(.+)
  - source_labels: [__meta_kubernetes_namespace]
    action: replace
    target_label: kubernetes_namespace
  - source_labels: [__meta_kubernetes_service_name]
    action: replace
    target_label: kubernetes_name

Events:  <none>

  可以看出:prometheus.yml 文件的基本配置包含了3个模块:global、rule_files 和 scrape_configs

其中 ,global 模块控制 Prometheus Server 的全局配置。

(1)scrape_interval:表示 prometheus 抓取指标数据的频率,默认是15s

(2)evaluation_interval:用来控制评估规则的频率,prometheus 使用规则产生新的时间序列数据或者产生警报

  rule_files 模块制定了规则所在的位置,prometheus 可以根据这个配置加载规则,用于生成新的时间序列数据或者报警信息,当前没有配置任何规则

  scrape_configs 用于控制 prometheus 监控哪些资源

4)通过deployment部署Prometheus Server

[root@k8s-master1 prometheus]# vim prometheus-deploy.yaml
[root@k8s-master1 prometheus]# cat prometheus-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus-server
  namespace: monitor-sa
  labels:
    app: prometheus
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prometheus
      component: server
    #matchExpressions:
    #- {key: app, operator: In, values: [prometheus]}
    #- {key: component, operator: In, values: [server]}
  template:
    metadata:
      labels:
        app: prometheus
        component: server
      annotations:
        prometheus.io/scrape: 'false' 
    spec:
      nodeName: k8s-node1  #prometheus调度到k8s-node1节点上
      serviceAccountName: monitor
      containers:
      - name: prometheus
        image: prom/prometheus:v2.2.1
        imagePullPolicy: IfNotPresent
        command:
          - prometheus
          - --config.file=/etc/prometheus/prometheus.yml
          - --storage.tsdb.path=/prometheus  #TSDB数据存储目录
          - --storage.tsdb.retention=720h   #何时删除旧数据,默认是15天
          - --web.enable-lifecycle   #开启热加热
        ports:
        - containerPort: 9090
          protocol: TCP
        volumeMounts:
        - mountPath: /etc/prometheus/prometheus.yml
          name: prometheus-config
          subPath: prometheus.yml
        - mountPath: /prometheus/
          name: prometheus-storage-volume
      volumes:
        - name: prometheus-config
          configMap:  #将 prometheus.yml 文件对应的 ConfigMap 对象通过 volume 的形式挂载进了 Pod,这样 ConfigMap 更新后,对应的 Pod 里面的文件也会热更新的,然后再执行上面的 reload 请求,Prometheus 配置就生效了
            name: prometheus-config
            items:
              - key: prometheus.yml
                path: prometheus.yml
                mode: 0644
        - name: prometheus-storage-volume
          hostPath:
           path: /prometheus/data
           type: Directory
[root@k8s-master1 prometheus]# kubectl apply -f prometheus-deploy.yaml
deployment.apps/prometheus-server created
[root@k8s-master1 prometheus]# kubectl get pods -n monitor-sa -o wide
NAME                                 READY   STATUS    RESTARTS   AGE   IP              NODE          NOMINATED NODE   READINESS GATES
node-exporter-k4wsq                  1/1     Running   1          11h   10.0.0.132      k8s-node1     <none>           <none>
node-exporter-x84r5                  1/1     Running   1          11h   10.0.0.133      k8s-node2     <none>           <none>
node-exporter-zrwvh                  1/1     Running   1          11h   10.0.0.131      k8s-master1   <none>           <none>
prometheus-server-787896b4d8-fbq8c   1/1     Running   0          48s   10.244.36.103   k8s-node1     <none>           <none>

  通过kubectl logs -f命令查看创建的Prometheus的pod对象是否正常:

[root@k8s-master1 prometheus]# kubectl logs -f prometheus-server-787896b4d8-fbq8c -n monitor-sa
level=info ts=2022-11-13T13:06:00.897055531Z caller=main.go:220 msg="Starting Prometheus" version="(version=2.2.1, branch=HEAD, revision=bc6058c81272a8d938c05e75607371284236aadc)"
level=info ts=2022-11-13T13:06:00.89716164Z caller=main.go:221 build_context="(go=go1.10, user=root@149e5b3f0829, date=20180314-14:15:45)"
level=info ts=2022-11-13T13:06:00.897180826Z caller=main.go:222 host_details="(Linux 3.10.0-693.el7.x86_64 #1 SMP Tue Aug 22 21:09:27 UTC 2017 x86_64 prometheus-server-787896b4d8-fbq8c (none))"
level=info ts=2022-11-13T13:06:00.897193657Z caller=main.go:223 fd_limits="(soft=65536, hard=65536)"
level=info ts=2022-11-13T13:06:00.899365267Z caller=main.go:504 msg="Starting TSDB ..."
level=info ts=2022-11-13T13:06:00.902628923Z caller=web.go:382 component=web msg="Start listening for connections" address=0.0.0.0:9090
level=info ts=2022-11-13T13:06:00.905415499Z caller=main.go:514 msg="TSDB started"
level=info ts=2022-11-13T13:06:00.905470066Z caller=main.go:588 msg="Loading configuration file" filename=/etc/prometheus/prometheus.yml
level=info ts=2022-11-13T13:06:00.910651585Z caller=kubernetes.go:191 component="discovery manager scrape" discovery=k8s msg="Using pod service account via in-cluster config"
level=info ts=2022-11-13T13:06:00.911971659Z caller=kubernetes.go:191 component="discovery manager scrape" discovery=k8s msg="Using pod service account via in-cluster config"
level=info ts=2022-11-13T13:06:00.912550402Z caller=kubernetes.go:191 component="discovery manager scrape" discovery=k8s msg="Using pod service account via in-cluster config"
level=info ts=2022-11-13T13:06:00.917409518Z caller=kubernetes.go:191 component="discovery manager scrape" discovery=k8s msg="Using pod service account via in-cluster config"
level=info ts=2022-11-13T13:06:00.939845366Z caller=main.go:491 msg="Server is ready to receive web requests."

  Pod 创建成功后,为了能够在外部访问到 prometheus 的 webui 服务,我们还需要创建一个 Service 对象:

[root@k8s-master1 prometheus]# vim prometheus-svc.yaml
[root@k8s-master1 prometheus]# cat prometheus-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: prometheus
  namespace: monitor-sa
  labels:
    app: prometheus
spec:
  type: NodePort
  ports:
    - port: 9090
      targetPort: 9090
      protocol: TCP
  selector:
    app: prometheus
    component: server
[root@k8s-master1 prometheus]# kubectl apply -f prometheus-svc.yaml
service/prometheus created
[root@k8s-master1 prometheus]# kubectl get svc -n  monitor-sa -o wide
NAME         TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE   SELECTOR
prometheus   NodePort   10.105.51.87   <none>        9090:32330/TCP   16s   app=prometheus,component=server

  通过上面可以看到service在宿主机上映射的端口是32330,这样访问k8s集群的master或者node节点的ip:32330,就可以访问到prometheus的web ui界面了。于Prometheus 的web ui的Targets状态页面中可以看到相关节点的发现结果,如下图所示:

  查看endpoint服务发现抓取到的数据,抓取到具有 "prometheus.io/scrape: true" 的annotation的端点

[root@k8s-master1 prometheus]# kubectl get svc -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   104d
[root@k8s-master1 prometheus]# kubectl describe svc kube-dns -n kube-system
Name:              kube-dns
Namespace:         kube-system
Labels:            k8s-app=kube-dns
                   kubernetes.io/cluster-service=true
                   kubernetes.io/name=KubeDNS
Annotations:       prometheus.io/port: 9153
                   prometheus.io/scrape: true
Selector:          k8s-app=kube-dns
Type:              ClusterIP
IP Families:       <none>
IP:                10.96.0.10
IPs:               10.96.0.10
Port:              dns  53/UDP
TargetPort:        53/UDP
Endpoints:         10.244.159.146:53,10.244.159.150:53
Port:              dns-tcp  53/TCP
TargetPort:        53/TCP
Endpoints:         10.244.159.146:53,10.244.159.150:53
Port:              metrics  9153/TCP
TargetPort:        9153/TCP
Endpoints:         10.244.159.146:9153,10.244.159.150:9153
Session Affinity:  None
Events:            <none>

  对应的 web UI界面上抓取到的数据显示:

5)Prometheus 热加载

  每次修改prometheus.yaml配置文件在不停止prometheus的情况下,就可以使配置生效,这就是Prometheus的热加载。

[root@k8s-master1 prometheus]# kubectl get pods -n monitor-sa -o wide -l app=prometheus
NAME                                 READY   STATUS    RESTARTS   AGE   IP              NODE        NOMINATED NODE   READINESS GATES
prometheus-server-787896b4d8-fbq8c   1/1     Running   0          42m   10.244.36.103   k8s-node1   <none>           <none>

  其中,10.244.36.103 是prometheus的pod的ip地址

   想要使配置生效可用如下命令进行热加载:

[root@k8s-master1 prometheus]# curl -X POST http://10.244.36.103:9090/-/reload

  注:热加载速度比较慢

3. 部署kube-state-metrics

  kube-state-metrics能够从kubernetes API Server 上收集各大多数资源对象的状态信息并将其转为指标数据,它工作于HTTP的/metrics接口,通过Prometheus的Go语言客户端暴露于外,Prometheus系统以及任何兼容相关客户端接口的数据抓取工具都可采集相关数据,并予以存储或展示。

1)kube-state-metrics介绍

  kube-state-metrics是kubernetes集群的一个附加组件,它通过kubernetes API Server 监听资源对象的状态并生成相关的指标,不过,它不在意单个kubernetes组件的运行状况,而是关注内部各种对象的整体运行状况,如Deployment对象或ReplicaSet对象等。换句话说,kube-state-metrics的重点是从kubernetes的对象状态生成全新的指标,kube-state-metrics主要关注的是业务相关的一些元数据,比如Deployment、Pod、副本状态等;调度了多少个replicas?现在可用的有几个?多少个Pod是running/stopped/terminated状态?Pod重启了多少次?有多少job在运行中等。目前kube-state-metrics主要负责为CronJob、Deployment、PersistentVolumeClaim等各类型的资源对象生成指标数据。

  需要注意的是kube-state-metrics只是简单的提供一个metrics数据,并不会存储这些指标数据,所以可以使用Prometheus来抓取这些数据然后存储。

2)安装kube-state-metrics组件

  创建sa,并对sa授权

[root@k8s-master1 prometheus]# vim kube-state-metrics-rbac.yaml
[root@k8s-master1 prometheus]# cat kube-state-metrics-rbac.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: kube-state-metrics
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kube-state-metrics
rules:
- apiGroups: [""]
  resources: ["nodes", "pods", "services", "resourcequotas", "replicationcontrollers", "limitranges", "persistentvolumeclaims", "persistentvolumes", "namespaces", "endpoints"]
  verbs: ["list", "watch"]
- apiGroups: ["extensions"]
  resources: ["daemonsets", "deployments", "replicasets"]
  verbs: ["list", "watch"]
- apiGroups: ["apps"]
  resources: ["statefulsets"]
  verbs: ["list", "watch"]
- apiGroups: ["batch"]
  resources: ["cronjobs", "jobs"]
  verbs: ["list", "watch"]
- apiGroups: ["autoscaling"]
  resources: ["horizontalpodautoscalers"]
  verbs: ["list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kube-state-metrics
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kube-state-metrics
subjects:
- kind: ServiceAccount
  name: kube-state-metrics
  namespace: kube-system
[root@k8s-master1 prometheus]# kubectl apply -f kube-state-metrics-rbac.yaml
serviceaccount/kube-state-metrics created
clusterrole.rbac.authorization.k8s.io/kube-state-metrics created
clusterrolebinding.rbac.authorization.k8s.io/kube-state-metrics created
[root@k8s-master1 prometheus]# kubectl get sa kube-state-metrics -n kube-system
NAME                 SECRETS   AGE
kube-state-metrics   1         85s
You have new mail in /var/spool/mail/root
[root@k8s-master1 prometheus]# kubectl get clusterrole kube-state-metrics -n kube-system
NAME                 CREATED AT
kube-state-metrics   2022-11-14T14:11:29Z
[root@k8s-master1 prometheus]# kubectl get clusterrolebinding kube-state-metrics -n kube-system
NAME                 ROLE                             AGE
kube-state-metrics   ClusterRole/kube-state-metrics   2m4s

  安装kube-state-metrics组件,在kube-system名称空间中创建一个名为kube-state-metrics的deployment控制器对象,其所生成的pod对象中的容器应用将监听8080端口以提供指标数据。

[root@k8s-master1 prometheus]# vim kube-state-metrics-deploy.yaml
You have new mail in /var/spool/mail/root
[root@k8s-master1 prometheus]# cat kube-state-metrics-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kube-state-metrics
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kube-state-metrics
  template:
    metadata:
      labels:
        app: kube-state-metrics
    spec:
      serviceAccountName: kube-state-metrics
      containers:
      - name: kube-state-metrics
        image: quay.io/coreos/kube-state-metrics:v1.9.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
[root@k8s-master1 prometheus]# kubectl apply -f kube-state-metrics-deploy.yaml
deployment.apps/kube-state-metrics created
[root@k8s-master1 prometheus]# kubectl get deployment kube-state-metrics -n kube-system  -o wide
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS           IMAGES                                     SELECTOR
kube-state-metrics   1/1     1            1           49s   kube-state-metrics   quay.io/coreos/kube-state-metrics:v1.9.0   app=kube-state-metrics
[root@k8s-master1 prometheus]# kubectl get pods -n kube-system -l app=kube-state-metrics
NAME                                  READY   STATUS    RESTARTS   AGE
kube-state-metrics-58d4957bc5-dhnvt   1/1     Running   0          2m24s

  创建service,在kube-system名称空间中创建一个名为kube-state-metrics的Service对象为客户端提供固定的访问入口

[root@k8s-master1 prometheus]# vim kube-state-metrics-svc.yaml
You have new mail in /var/spool/mail/root
[root@k8s-master1 prometheus]# cat kube-state-metrics-svc.yaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/scrape: 'true'
  name: kube-state-metrics
  namespace: kube-system
  labels:
    app: kube-state-metrics
spec:
  ports:
  - name: kube-state-metrics
    port: 8080
    protocol: TCP
  selector:
    app: kube-state-metrics
[root@k8s-master1 prometheus]# kubectl apply -f kube-state-metrics-svc.yaml
service/kube-state-metrics created
[root@k8s-master1 prometheus]# kubectl get svc -n kube-system -l app=kube-state-metrics -o wide
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE   SELECTOR
kube-state-metrics   ClusterIP   10.109.86.36   <none>        8080/TCP   42s   app=kube-state-metrics

  用户也可以通过任何类型的HTTP客户端程序测试访问其原始格式的指标及数据。例如,启动一个pod客户端进行测试访问,命令如下:

[root@k8s-master1 prometheus]# kubectl run client-test --image=cirros  -it --rm -- sh
If you don't see a command prompt, try pressing enter.
/ # curl -s kube-state-metrics.kube-system:8080/metrics | grep kube_endpoint_info
# HELP kube_endpoint_info Information about endpoint.
# TYPE kube_endpoint_info gauge
kube_endpoint_info{namespace="default",endpoint="ceph.com-rbd"} 1
kube_endpoint_info{namespace="kube-system",endpoint="kube-dns"} 1
kube_endpoint_info{namespace="kubernetes-dashboard",endpoint="dashboard-metrics-scraper"} 1
kube_endpoint_info{namespace="default",endpoint="ceph.com-cephfs"} 1
kube_endpoint_info{namespace="default",endpoint="kubernetes"} 1
kube_endpoint_info{namespace="kubernetes-dashboard",endpoint="kubernetes-dashboard"} 1
kube_endpoint_info{namespace="monitor-sa",endpoint="prometheus"} 1
kube_endpoint_info{namespace="kube-system",endpoint="kube-state-metrics"} 1
kube_endpoint_info{namespace="default",endpoint="example.com-nfs"} 1
/ #

  可以看到通过8080端口进行确认能得到一系列额指标数据,即表示kube-state-metrics准备就绪。

  在Prometheus Web UI界面上也可以看到集群资源状态指标数据:

4. 部署grafana

  在Prometheus 的Web UI界面中展示的数据,明显可以感觉到 Prometheus 的图表功能相对较弱,所以一般情况下会用第三方的工具来展示这些数据,常用的是grafana。

1)Grafana介绍

  Grafana是一个跨平台的开源的度量分析和可视化工具,可以将采集的数据可视化的展示,并及时通知给告警接收方。它主要有以下五大特点:

(1)展示方式:快速灵活的客户端图表,面板插件有许多不同方式的可视化指标和日志,官方库中具有丰富的仪表盘插件,比如热图、折线图、图表等多种展示方式;

(2)数据源:支持Graphite,InfluxDB,OpenTSDB,Prometheus,Elasticsearch,CloudWatch和KairosDB等;

(3)通知提醒:以可视方式定义最重要指标的警报规则,Grafana将不断计算并发送通知,在数据达到阈值时通过Slack、PagerDuty等获得通知;

(4)混合展示:在同一图表中混合使用不同的数据源,可以基于每个查询指定数据源,甚至自定义数据源;

(5)注释:使用来自不同数据源的丰富事件注释图表,将鼠标悬停在事件上会显示完整的事件元数据和标记。

2)安装Grafana

  将 grafana 安装到 Kubernetes 集群中,第一步同样是去查看 grafana 的 docker 镜像的介绍,可以在 dockerhub 上去搜索,也可以在官网去查看相关资料,镜像地址如下:https://hub.docker.com/r/grafana/grafana/。本次实验使用的是

  创建存储数据的目录:

[root@k8s-node1 ~]# mkdir /var/lib/grafana/ -p
[root@k8s-node1 ~]# chmod 777 /var/lib/grafana/
[root@k8s-node1 ~]# ll /var/lib/grafana/
total 0
[root@k8s-node1 ~]#

  安装grafana,使用Deployment控制器创建grafana应用,由于 grafana 将 dashboard、插件这些数据保存在/var/lib/grafana这个目录下面的,如果需要做数据持久化的话,就需要针对这个目录进行 volume 挂载声明,使用本地目录挂载。对外暴露 grafana 这个服务,还创建了对应的 Service 对象。

[root@k8s-master1 prometheus]# vim grafana.yaml
You have new mail in /var/spool/mail/root
[root@k8s-master1 prometheus]# cat grafana.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: monitoring-grafana
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      task: monitoring
      k8s-app: grafana
  template:
    metadata:
      labels:
        task: monitoring
        k8s-app: grafana
    spec:
      nodeName: k8s-node1
      containers:
      - name: grafana
        image: grafana/grafana:8.4.5
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 3000
          protocol: TCP
        volumeMounts:
        - mountPath: /etc/ssl/certs
          name: ca-certificates
          readOnly: true
        - mountPath: /var
          name: grafana-storage
        - mountPath: /var/lib/grafana/
          name: lib
        env:
        - name: INFLUXDB_HOST
          value: monitoring-influxdb
        - name: GF_SERVER_HTTP_PORT
          value: "3000"
          # The following env variables are required to make Grafana accessible via
          # the kubernetes api-server proxy. On production clusters, we recommend
          # removing these env variables, setup auth for grafana, and expose the grafana
          # service using a LoadBalancer or a public IP.
        - name: GF_AUTH_BASIC_ENABLED
          value: "false"
        - name: GF_AUTH_ANONYMOUS_ENABLED
          value: "true"
        - name: GF_AUTH_ANONYMOUS_ORG_ROLE
          value: Admin
        - name: GF_SERVER_ROOT_URL
          # If you're only using the API Server proxy, set this value instead:
          # value: /api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
          value: /
      volumes:
      - name: ca-certificates
        hostPath:
          path: /etc/ssl/certs
      - name: grafana-storage
        emptyDir: {}
      - name: lib
        hostPath:
         path: /var/lib/grafana/
         type: DirectoryOrCreate
---
apiVersion: v1
kind: Service
metadata:
  labels:
    # For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
    # If you are NOT using this as an addon, you should comment out this line.
    kubernetes.io/cluster-service: 'true'
    kubernetes.io/name: monitoring-grafana
  name: monitoring-grafana
  namespace: kube-system
spec:
  # In a production setup, we recommend accessing Grafana through an external Loadbalancer
  # or through a public IP.
  # type: LoadBalancer
  # You could also use NodePort to expose the service at a randomly-generated port
  # type: NodePort
  ports:
  - port: 80
    targetPort: 3000
  selector:
    k8s-app: grafana
  type: NodePort
[root@k8s-master1 prometheus]# kubectl apply -f grafana.yaml
deployment.apps/monitoring-grafana created
service/monitoring-grafana created

  创建完成后,可以查看 grafana 对应的 Pod 是否正常。通过下列命令可以看到grafana 的 Pod 已经正常启动起来了。这个时候可以查看 Service 对象

[root@k8s-master1 prometheus]# kubectl get pods -n kube-system -o wide| grep monitor
monitoring-grafana-664748d8cf-q46c8        1/1     Running   0          61s    10.244.36.106    k8s-node1     <none>           <none>
You have new mail in /var/spool/mail/root
[root@k8s-master1 prometheus]# kubectl get svc -n kube-system | grep grafana
monitoring-grafana   NodePort    10.107.29.10   <none>        80:30784/TCP             78s

  在浏览器中使用http://<任意节点IP:30784>来访问 grafana 这个服务

3)配置grafana的web界面

  选择Add your first data source

  出现如下界面:

选择Prometheus,出现如下:

  Name: Prometheus

  HTTP 处的URL如下http://prometheus.monitor-sa.svc:9090。配置好的整体页面如下:

  点击右下角Save & Test

  出现如下Data source is working,说明prometheus数据源成功的被grafana接入

4)导入监控模板

  可在https://grafana.com/dashboards?dataSource=prometheus&search=kubernetes链接搜索监控模板的json文件 ,链接打开如下界面:

(1)导入node-exporter.json监控模板,显示node节点指标

  点击左侧+号下面的Import

  显示如下界面:

  选择Upload json file,出现如下界面:

  选择一个本地的json文件,选择的是上面已下载的node_exporter.json这个文件,选择之后出现如下

  Name后面的名字是node_exporter.json定义的,Prometheus下选择Prometheus,然后再点击Import,就可以出现如下界面,显示的是节点k8s-master1的指标信息。

(2)导入docker的一个监控模板

  导入一个docker_rev1.json监控模板,步骤和上面导入node_exporter.json步骤一样:

5. 部署Alertmanager

  Prometheus的告警功能由两个步骤实现,首先是Prometheus服务器根据告警规则将告警信息发送给Alertmanager,而后由Alertmanager对收到的告警信息进行处理,包括去重、分组并路由到告警接收端,支持的形式包括slack、email、pagerduty、hitchat和WebHook等接口上的联系人信息。即:

  报警:指prometheus将监测到的异常事件发送给alertmanager

  通知:alertmanager将报警信息发送到邮件、微信、钉钉等。

1)告警处理流程

  Prometheus 一条告警的触发流程、等待时间,告警处理流程如下:

(1)Prometheus Server监控目标主机上暴露的http接口(这里假设接口A),通过Promethes配置的'scrape_interval'定义的时间间隔,定期采集目标主机上监控数据。

(2)当接口A不可用的时候,Server端会持续的尝试从接口中取数据,直到"scrape_timeout"时间后停止尝试。这时候把接口的状态变为“DOWN”。但是如果能从接口获取到数据时,接口的状态为“UP”,如下图:

(3)Prometheus同时根据配置的"evaluation_interval"的时间间隔,定期(默认1min)的对Alert Rule进行评估;当到达评估周期的时候,发现接口A为DOWN,即UP=0为真,激活Alert,进入“PENDING”状态,并记录当前active的时间;

(4)当下一个alert rule的评估周期到来的时候,发现UP=0继续为真,然后判断警报Active的时间是否已经超出rule里的‘for’ 持续时间,如果未超出,则进入下一个评估周期;如果时间超出,则alert的状态变为“FIRING”;同时调用Alertmanager接口,发送相关报警数据。

(5)AlertManager收到报警数据后,会将警报信息进行分组,然后根据alertmanager配置的“group_wait”时间先进行等待。等wait时间过后再发送报警信息。

(6)属于同一个Alert Group的警报,在等待的过程中可能进入新的alert,如果之前的报警已经成功发出,那么间隔“group_interval”的时间间隔后再重新发送报警信息。比如配置的是邮件报警,那么同属一个group的报警信息会汇总在一个邮件里进行发送。

(7)如果Alert Group里的警报一直没发生变化并且已经成功发送,等待‘repeat_interval’时间间隔之后再重复发送相同的报警邮件;如果之前的警报没有成功发送,则相当于触发第6条条件,则需要等待group_interval时间间隔后重复发送。

  同时最后至于警报信息具体发给谁,满足什么样的条件下指定警报接收人,设置不同报警发送频率,这里有alertmanager的route路由规则进行配置。

2)配置发送告警的邮箱

  使用的是126邮箱,邮箱设置POP3/SMTP/IMAP,开启SMTP服务器,获取授权码:

3)配置alertmanager-发送报警到qq邮箱

(1)创建alertmanager配置文件

  清单说明如下:

kind: ConfigMap
apiVersion: v1
metadata:
  name: alertmanager-config
  namespace: monitor-sa
data:
  alertmanager.yml: |-
    global:
      resolve_timeout: 1m
      smtp_smarthost: 'smtp.126.com:25'   #126邮箱的SMTP服务器地址+端口
      smtp_from: 'jiaweiyzj@126.com'      #指定从哪个邮箱发送报警
      smtp_auth_username: 'jiaweiyzj@126.com'   #发送邮箱的认证用户
      smtp_auth_password: '**********'  #发送邮箱的授权码而不是登录密码
      smtp_require_tls: false
    route:  #用于配置告警分发策略
      group_by: [alertname]   # 采用哪个标签来作为分组依据
      group_wait: 10s    # 组告警等待时间。也就是告警产生后等待10s,如果有同组告警一起发出
      group_interval: 10s  # 上下两组发送告警的间隔时间
      repeat_interval: 10m  # 重复发送告警的时间,减少相同邮件的发送频率,默认是1h
      receiver: email  #定义谁来收告警
    receivers:
    - name: 'email'
      email_configs:
      - to: '962532419@qq.com'  #指定发送到哪个邮箱,本实验使用的是发送到qq邮箱,不应该跟smtp服务器的邮箱的名称相同
        send_resolved: true

  创建该资源,查看相应的信息:

[root@k8s-master1 prometheus]# kubectl apply -f alertmanager-cm.yaml
configmap/alertmanager-config created
[root@k8s-master1 prometheus]# kubectl get cm alertmanager-config -n monitor-sa
NAME                  DATA   AGE
alertmanager-config   1      17s
[root@k8s-master1 prometheus]# kubectl describe cm alertmanager-config -n monitor-sa
Name:         alertmanager-config
Namespace:    monitor-sa
Labels:       <none>
Annotations:  <none>

Data
====
alertmanager.yml:
----
global:
  resolve_timeout: 1m
  smtp_smarthost: 'smtp.126.com:25'
  smtp_from: 'jiaweiyzj@126.com'
  smtp_auth_username: 'jiaweiyzj'
  smtp_auth_password: '***********'  #授权码
  smtp_require_tls: false
route:
  group_by: [alertname]
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 10m
  receiver: default-receiver
receivers:
- name: 'default-receiver'
  email_configs:
  - to: '962532419@qq.com'
    send_resolved: true
Events:  <none> 
(2)创建带有告警规则的prometheus配置文件

     在配置文件中添加告警规则:

[root@k8s-master1 prometheus]# vim prometheus-alertmanager-cfg.yaml
[root@k8s-master1 prometheus]# cat prometheus-alertmanager-cfg.yaml
kind: ConfigMap
apiVersion: v1
metadata:
  labels:
    app: prometheus
  name: prometheus-config
  namespace: monitor-sa
data:
  prometheus.yml: |
    rule_files:
    - /etc/prometheus/rules.yml
    alerting:
      alertmanagers:
      - static_configs:
        - targets: ["localhost:9093"]
    global:
      scrape_interval: 15s
      scrape_timeout: 10s
      evaluation_interval: 1m
    scrape_configs:
    - job_name: 'kubernetes-node'
      kubernetes_sd_configs:
      - role: node
      relabel_configs:
      - source_labels: [__address__]
        regex: '(.*):10250'
        replacement: '${1}:9100'
        target_label: __address__
        action: replace
      - action: labelmap
        regex: __meta_kubernetes_node_label_(.+)
    - job_name: 'kubernetes-node-cadvisor'
      kubernetes_sd_configs:
      - role:  node
      scheme: https
      tls_config:
        ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
      bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
      relabel_configs:
      - action: labelmap
        regex: __meta_kubernetes_node_label_(.+)
      - target_label: __address__
        replacement: kubernetes.default.svc:443
      - source_labels: [__meta_kubernetes_node_name]
        regex: (.+)
        target_label: __metrics_path__
        replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
    - job_name: 'kubernetes-apiserver'
      kubernetes_sd_configs:
      - role: endpoints
      scheme: https
      tls_config:
        ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
      bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
      relabel_configs:
      - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
        action: keep
        regex: default;kubernetes;https
    - job_name: 'kubernetes-service-endpoints'
      kubernetes_sd_configs:
      - role: endpoints
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
        action: replace
        target_label: __scheme__
        regex: (https?)
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
      - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
        action: replace
        target_label: __address__
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
      - action: labelmap
        regex: __meta_kubernetes_service_label_(.+)
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: kubernetes_namespace
      - source_labels: [__meta_kubernetes_service_name]
        action: replace
        target_label: kubernetes_name
    - job_name: 'kubernetes-pods'
      kubernetes_sd_configs:
      - role: pod
      relabel_configs:
      - action: keep
        regex: true
        source_labels:
        - __meta_kubernetes_pod_annotation_prometheus_io_scrape
      - action: replace
        regex: (.+)
        source_labels:
        - __meta_kubernetes_pod_annotation_prometheus_io_path
        target_label: __metrics_path__
      - action: replace
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
        source_labels:
        - __address__
        - __meta_kubernetes_pod_annotation_prometheus_io_port
        target_label: __address__
      - action: labelmap
        regex: __meta_kubernetes_pod_label_(.+)
      - action: replace
        source_labels:
        - __meta_kubernetes_namespace
        target_label: kubernetes_namespace
      - action: replace
        source_labels:
        - __meta_kubernetes_pod_name
        target_label: kubernetes_pod_name
    - job_name: 'kubernetes-schedule'
      scrape_interval: 5s
      static_configs:
      - targets: ['10.0.0.131:10251']
    - job_name: 'kubernetes-controller-manager'
      scrape_interval: 5s
      static_configs:
      - targets: ['10.0.0.131:10252']
    - job_name: 'kubernetes-kube-proxy'
      scrape_interval: 5s
      static_configs:
      - targets: ['10.0.0.131:10249','10.0.0.132:10249','10.0.0.133:10249']
    - job_name: 'kubernetes-etcd'
      scheme: https
      tls_config:
        ca_file: /var/run/secrets/kubernetes.io/k8s-certs/etcd/ca.crt
        cert_file: /var/run/secrets/kubernetes.io/k8s-certs/etcd/server.crt
        key_file: /var/run/secrets/kubernetes.io/k8s-certs/etcd/server.key
      scrape_interval: 5s
      static_configs:
      - targets: ['10.0.0.131:2379']
  rules.yml: |
    groups:
    - name: example
      rules:
      - alert: kube-proxy的cpu使用率大于80%
        expr: rate(process_cpu_seconds_total{job=~"kubernetes-kube-proxy"}[1m]) * 100 > 80
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过80%"
      - alert:  kube-proxy的cpu使用率大于90%
        expr: rate(process_cpu_seconds_total{job=~"kubernetes-kube-proxy"}[1m]) * 100 > 90
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过90%"
      - alert: scheduler的cpu使用率大于80%
        expr: rate(process_cpu_seconds_total{job=~"kubernetes-schedule"}[1m]) * 100 > 80
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过80%"
      - alert:  scheduler的cpu使用率大于90%
        expr: rate(process_cpu_seconds_total{job=~"kubernetes-schedule"}[1m]) * 100 > 90
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过90%"
      - alert: controller-manager的cpu使用率大于80%
        expr: rate(process_cpu_seconds_total{job=~"kubernetes-controller-manager"}[1m]) * 100 > 80
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过80%"
      - alert:  controller-manager的cpu使用率大于90%
        expr: rate(process_cpu_seconds_total{job=~"kubernetes-controller-manager"}[1m]) * 100 > 0
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过90%"
      - alert: apiserver的cpu使用率大于80%
        expr: rate(process_cpu_seconds_total{job=~"kubernetes-apiserver"}[1m]) * 100 > 80
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过80%"
      - alert:  apiserver的cpu使用率大于90%
        expr: rate(process_cpu_seconds_total{job=~"kubernetes-apiserver"}[1m]) * 100 > 90
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过90%"
      - alert: etcd的cpu使用率大于80%
        expr: rate(process_cpu_seconds_total{job=~"kubernetes-etcd"}[1m]) * 100 > 80
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过80%"
      - alert:  etcd的cpu使用率大于90%
        expr: rate(process_cpu_seconds_total{job=~"kubernetes-etcd"}[1m]) * 100 > 90
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过90%"
      - alert: kube-state-metrics的cpu使用率大于80%
        expr: rate(process_cpu_seconds_total{k8s_app=~"kube-state-metrics"}[1m]) * 100 > 80
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.k8s_app}}组件的cpu使用率超过80%"
          value: "{{ $value }}%"
          threshold: "80%"
      - alert: kube-state-metrics的cpu使用率大于90%
        expr: rate(process_cpu_seconds_total{k8s_app=~"kube-state-metrics"}[1m]) * 100 > 0
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.k8s_app}}组件的cpu使用率超过90%"
          value: "{{ $value }}%"
          threshold: "90%"
      - alert: coredns的cpu使用率大于80%
        expr: rate(process_cpu_seconds_total{k8s_app=~"kube-dns"}[1m]) * 100 > 80
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.k8s_app}}组件的cpu使用率超过80%"
          value: "{{ $value }}%"
          threshold: "80%"
      - alert: coredns的cpu使用率大于90%
        expr: rate(process_cpu_seconds_total{k8s_app=~"kube-dns"}[1m]) * 100 > 90
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.k8s_app}}组件的cpu使用率超过90%"
          value: "{{ $value }}%"
          threshold: "90%"
      - alert: kube-proxy打开句柄数>600
        expr: process_open_fds{job=~"kubernetes-kube-proxy"}  > 600
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>600"
          value: "{{ $value }}"
      - alert: kube-proxy打开句柄数>1000
        expr: process_open_fds{job=~"kubernetes-kube-proxy"}  > 1000
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>1000"
          value: "{{ $value }}"
      - alert: kubernetes-schedule打开句柄数>600
        expr: process_open_fds{job=~"kubernetes-schedule"}  > 600
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>600"
          value: "{{ $value }}"
      - alert: kubernetes-schedule打开句柄数>1000
        expr: process_open_fds{job=~"kubernetes-schedule"}  > 1000
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>1000"
          value: "{{ $value }}"
      - alert: kubernetes-controller-manager打开句柄数>600
        expr: process_open_fds{job=~"kubernetes-controller-manager"}  > 600
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>600"
          value: "{{ $value }}"
      - alert: kubernetes-controller-manager打开句柄数>1000
        expr: process_open_fds{job=~"kubernetes-controller-manager"}  > 1000
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>1000"
          value: "{{ $value }}"
      - alert: kubernetes-apiserver打开句柄数>600
        expr: process_open_fds{job=~"kubernetes-apiserver"}  > 600
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>600"
          value: "{{ $value }}"
      - alert: kubernetes-apiserver打开句柄数>1000
        expr: process_open_fds{job=~"kubernetes-apiserver"}  > 1000
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>1000"
          value: "{{ $value }}"
      - alert: kubernetes-etcd打开句柄数>600
        expr: process_open_fds{job=~"kubernetes-etcd"}  > 600
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>600"
          value: "{{ $value }}"
      - alert: kubernetes-etcd打开句柄数>1000
        expr: process_open_fds{job=~"kubernetes-etcd"}  > 1000
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>1000"
          value: "{{ $value }}"
      - alert: coredns
        expr: process_open_fds{k8s_app=~"kube-dns"}  > 600
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "插件{{$labels.k8s_app}}({{$labels.instance}}): 打开句柄数超过600"
          value: "{{ $value }}"
      - alert: coredns
        expr: process_open_fds{k8s_app=~"kube-dns"}  > 1000
        for: 2s
        labels:
          severity: critical
        annotations:
          description: "插件{{$labels.k8s_app}}({{$labels.instance}}): 打开句柄数超过1000"
          value: "{{ $value }}"
      - alert: kube-proxy
        expr: process_virtual_memory_bytes{job=~"kubernetes-kube-proxy"}  > 2000000000
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "组件{{$labels.job}}({{$labels.instance}}): 使用虚拟内存超过2G"
          value: "{{ $value }}"
      - alert: scheduler
        expr: process_virtual_memory_bytes{job=~"kubernetes-schedule"}  > 2000000000
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "组件{{$labels.job}}({{$labels.instance}}): 使用虚拟内存超过2G"
          value: "{{ $value }}"
      - alert: kubernetes-controller-manager
        expr: process_virtual_memory_bytes{job=~"kubernetes-controller-manager"}  > 2000000000
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "组件{{$labels.job}}({{$labels.instance}}): 使用虚拟内存超过2G"
          value: "{{ $value }}"
      - alert: kubernetes-apiserver
        expr: process_virtual_memory_bytes{job=~"kubernetes-apiserver"}  > 2000000000
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "组件{{$labels.job}}({{$labels.instance}}): 使用虚拟内存超过2G"
          value: "{{ $value }}"
      - alert: kubernetes-etcd
        expr: process_virtual_memory_bytes{job=~"kubernetes-etcd"}  > 2000000000
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "组件{{$labels.job}}({{$labels.instance}}): 使用虚拟内存超过2G"
          value: "{{ $value }}"
      - alert: kube-dns
        expr: process_virtual_memory_bytes{k8s_app=~"kube-dns"}  > 2000000000
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "插件{{$labels.k8s_app}}({{$labels.instance}}): 使用虚拟内存超过2G"
          value: "{{ $value }}"
      - alert: HttpRequestsAvg
        expr: sum(rate(rest_client_requests_total{job=~"kubernetes-kube-proxy|kubernetes-kubelet|kubernetes-schedule|kubernetes-control-manager|kubernetes-apiservers"}[1m]))  > 1000
        for: 2s
        labels:
          team: admin
        annotations:
          description: "组件{{$labels.job}}({{$labels.instance}}): TPS超过1000"
          value: "{{ $value }}"
          threshold: "1000"
      - alert: Pod_restarts
        expr: kube_pod_container_status_restarts_total{namespace=~"kube-system|default|monitor-sa"} > 0
        for: 2s
        labels:
          severity: warnning
        annotations:
          description: "在{{$labels.namespace}}名称空间下发现{{$labels.pod}}这个pod下的容器{{$labels.container}}被重启,这个监控指标是由{{$labels.instance}}采集的"
          value: "{{ $value }}"
          threshold: "0"
      - alert: Pod_waiting
        expr: kube_pod_container_status_waiting_reason{namespace=~"kube-system|default"} == 1
        for: 2s
        labels:
          team: admin
        annotations:
          description: "空间{{$labels.namespace}}({{$labels.instance}}): 发现{{$labels.pod}}下的{{$labels.container}}启动异常等待中"
          value: "{{ $value }}"
          threshold: "1"
      - alert: Pod_terminated
        expr: kube_pod_container_status_terminated_reason{namespace=~"kube-system|default|monitor-sa"} == 1
        for: 2s
        labels:
          team: admin
        annotations:
          description: "空间{{$labels.namespace}}({{$labels.instance}}): 发现{{$labels.pod}}下的{{$labels.container}}被删除"
          value: "{{ $value }}"
          threshold: "1"
      - alert: Etcd_leader
        expr: etcd_server_has_leader{job="kubernetes-etcd"} == 0
        for: 2s
        labels:
          team: admin
        annotations:
          description: "组件{{$labels.job}}({{$labels.instance}}): 当前没有leader"
          value: "{{ $value }}"
          threshold: "0"
      - alert: Etcd_leader_changes
        expr: rate(etcd_server_leader_changes_seen_total{job="kubernetes-etcd"}[1m]) > 0
        for: 2s
        labels:
          team: admin
        annotations:
          description: "组件{{$labels.job}}({{$labels.instance}}): 当前leader已发生改变"
          value: "{{ $value }}"
          threshold: "0"
      - alert: Etcd_failed
        expr: rate(etcd_server_proposals_failed_total{job="kubernetes-etcd"}[1m]) > 0
        for: 2s
        labels:
          team: admin
        annotations:
          description: "组件{{$labels.job}}({{$labels.instance}}): 服务失败"
          value: "{{ $value }}"
          threshold: "0"
      - alert: Etcd_db_total_size
        expr: etcd_debugging_mvcc_db_total_size_in_bytes{job="kubernetes-etcd"} > 10000000000
        for: 2s
        labels:
          team: admin
        annotations:
          description: "组件{{$labels.job}}({{$labels.instance}}):db空间超过10G"
          value: "{{ $value }}"
          threshold: "10G"
      - alert: Endpoint_ready
        expr: kube_endpoint_address_not_ready{namespace=~"kube-system|default"} == 1
        for: 2s
        labels:
          team: admin
        annotations:
          description: "空间{{$labels.namespace}}({{$labels.instance}}): 发现{{$labels.endpoint}}不可用"
          value: "{{ $value }}"
          threshold: "1"
    - name: 物理节点状态-监控告警
      rules:
      - alert: 物理节点cpu使用率
        expr: 100-avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by(instance)*100 > 90
        for: 2s
        labels:
          severity: ccritical
        annotations:
          summary: "{{ $labels.instance }}cpu使用率过高"
          description: "{{ $labels.instance }}的cpu使用率超过90%,当前使用率[{{ $value }}],需要排查处理"
      - alert: 物理节点内存使用率
        expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100 > 90
        for: 2s
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.instance }}内存使用率过高"
          description: "{{ $labels.instance }}的内存使用率超过90%,当前使用率[{{ $value }}],需要排查处理"
      - alert: InstanceDown
        expr: up == 0
        for: 2s
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.instance }}: 服务器宕机"
          description: "{{ $labels.instance }}: 服务器延时超过2分钟"
      - alert: 物理节点磁盘的IO性能
        expr: 100-(avg(irate(node_disk_io_time_seconds_total[1m])) by(instance)* 100) < 60
        for: 2s
        labels:
          severity: critical
        annotations:
          summary: "{{$labels.mountpoint}} 流入磁盘IO使用率过高!"
          description: "{{$labels.mountpoint }} 流入磁盘IO大于60%(目前使用:{{$value}})"
      - alert: 入网流量带宽
        expr: ((sum(rate (node_network_receive_bytes_total{device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}[5m])) by (instance)) / 100) > 102400
        for: 2s
        labels:
          severity: critical
        annotations:
          summary: "{{$labels.mountpoint}} 流入网络带宽过高!"
          description: "{{$labels.mountpoint }}流入网络带宽持续5分钟高于100M. RX带宽使用率{{$value}}"
      - alert: 出网流量带宽
        expr: ((sum(rate (node_network_transmit_bytes_total{device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}[5m])) by (instance)) / 100) > 102400
        for: 2s
        labels:
          severity: critical
        annotations:
          summary: "{{$labels.mountpoint}} 流出网络带宽过高!"
          description: "{{$labels.mountpoint }}流出网络带宽持续5分钟高于100M. RX带宽使用率{{$value}}"
      - alert: TCP会话
        expr: node_netstat_Tcp_CurrEstab > 1000
        for: 2s
        labels:
          severity: critical
        annotations:
          summary: "{{$labels.mountpoint}} TCP_ESTABLISHED过高!"
          description: "{{$labels.mountpoint }} TCP_ESTABLISHED大于1000%(目前使用:{{$value}}%)"
      - alert: 磁盘容量
        expr: 100-(node_filesystem_free_bytes{fstype=~"ext4|xfs"}/node_filesystem_size_bytes {fstype=~"ext4|xfs"}*100) > 80
        for: 2s
        labels:
          severity: critical
        annotations:
          summary: "{{$labels.mountpoint}} 磁盘分区使用率过高!"
          description: "{{$labels.mountpoint }} 磁盘分区使用大于80%(目前使用:{{$value}}%)"

  重新更新prometheus配置文件,删除之前的prometheus配置文件,使用上面创建的prometheus-alertmanager-cfg.yaml资源清单文件创建prometheus配置

[root@k8s-master1 prometheus]# kubectl delete -f prometheus-cfg.yaml
configmap "prometheus-config" deleted
[root@k8s-master1 prometheus]# kubectl apply -f prometheus-alertmanager-cfg.yaml
configmap/prometheus-config created
[root@k8s-master1 prometheus]# kubectl get cm prometheus-config -n monitor-sa
NAME                DATA   AGE
prometheus-config   2      16s
(3)安装Prometheus和Alertmanager

  因为要将prometheus数据重新挂载到其他目录下,重新安装prometheus。本次实验是将prometheus和alermanager安装在同一个pod中两个不同的容器,资源清单配置文件如下:

[root@k8s-master1 prometheus]# vim prometheus-alertmanager-deploy.yaml
You have new mail in /var/spool/mail/root
[root@k8s-master1 prometheus]# cat prometheus-alertmanager-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus-server
  namespace: monitor-sa
  labels:
    app: prometheus
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prometheus
      component: server
    #matchExpressions:
    #- {key: app, operator: In, values: [prometheus]}
    #- {key: component, operator: In, values: [server]}
  template:
    metadata:
      labels:
        app: prometheus
        component: server
      annotations:
        prometheus.io/scrape: 'false'
    spec:
      nodeName: k8s-node2
      serviceAccountName: monitor
      containers:
      - name: prometheus
        image: prom/prometheus:v2.2.1
        imagePullPolicy: IfNotPresent
        command:
        - "/bin/prometheus"
        args:
        - "--config.file=/etc/prometheus/prometheus.yml"
        - "--storage.tsdb.path=/prometheus"
        - "--storage.tsdb.retention=24h"
        - "--web.enable-lifecycle"
        ports:
        - containerPort: 9090
          protocol: TCP
        volumeMounts:
        - mountPath: /etc/prometheus
          name: prometheus-config
        - mountPath: /prometheus/
          name: prometheus-storage-volume
        - name: k8s-certs
          mountPath: /var/run/secrets/kubernetes.io/k8s-certs/etcd/
        - name: localtime
          mountPath: /etc/localtime
      - name: alertmanager
        image: prom/alertmanager:v0.14.0
        imagePullPolicy: IfNotPresent
        args:
        - "--config.file=/etc/alertmanager/alertmanager.yml"
        - "--log.level=debug"
        ports:
        - containerPort: 9093
          protocol: TCP
          name: alertmanager
        volumeMounts:
        - name: alertmanager-config
          mountPath: /etc/alertmanager
        - name: alertmanager-storage
          mountPath: /alertmanager
        - name: localtime
          mountPath: /etc/localtime
      volumes:
        - name: prometheus-config
          configMap:
            name: prometheus-config
        - name: prometheus-storage-volume
          hostPath:
           path: /prometheus/data
           type: Directory
        - name: k8s-certs
          secret:
           secretName: etcd-certs
        - name: alertmanager-config
          configMap:
            name: alertmanager-config
        - name: alertmanager-storage
          hostPath:
           path: /prometheus/data
           type: DirectoryOrCreate
        - name: localtime
          hostPath:
           path: /usr/share/zoneinfo/Asia/Shanghai 

  从上述配置清单中可以看到部署Prometheus时需要一个etcd-certs,下面命令可以生成etcd-certs

[root@k8s-master1 prometheus]# kubectl -n monitor-sa create secret generic etcd-certs --from-file=/etc/kubernetes/pki/etcd/server.key  --from-file=/etc/kubernetes/pki/etcd/server.crt --from-file=/etc/kubernetes/pki/etcd/ca.crt
secret/etcd-certs created
[root@k8s-master1 prometheus]# kubectl get secrets etcd-certs -n monitor-sa
NAME         TYPE     DATA   AGE
etcd-certs   Opaque   3      20s

  使用kubectl apply命令创建Prometheus和alertmanager

[root@k8s-master1 prometheus]# kubectl delete -f prometheus-deploy.yaml
deployment.apps "prometheus-server" deleted
[root@k8s-master1 prometheus]# kubectl apply -f prometheus-alertmanager-deploy.yaml
deployment.apps/prometheus-server created
[root@k8s-master1 prometheus]# kubectl get pods -n monitor-sa -o wide| grep prometheus
prometheus-server-646bf944c6-rf5kq   2/2     Running   0          39s    10.244.169.161   k8s-node2     <none>           <none>
(4)部署Alertmanager的service服务
[root@k8s-master1 prometheus]# vim alertmanager-svc.yaml
[root@k8s-master1 prometheus]# cat alertmanager-svc.yaml
---
apiVersion: v1
kind: Service
metadata:
  labels:
    name: prometheus
    kubernetes.io/cluster-service: 'true'
  name: alertmanager
  namespace: monitor-sa
spec:
  ports:
  - name: alertmanager
    nodePort: 30066
    port: 9093
    protocol: TCP
    targetPort: 9093
  selector:
    app: prometheus
  sessionAffinity: None
  type: NodePort
[root@k8s-master1 prometheus]# kubectl apply -f alertmanager-svc.yaml
service/alertmanager created
[root@k8s-master1 prometheus]# kubectl get svc -n monitor-sa
NAME           TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
alertmanager   NodePort   10.96.171.82   <none>        9093:30066/TCP   13s
prometheus     NodePort   10.105.51.87   <none>        9090:32330/TCP   5d21h

  从上面可以看到prometheus的service在物理机映射的端口是32330,alertmanager的service在物理机映射的端口是30066

  在浏览器上输入:http://10.0.0.131:30066/#/alerts 访问Alertmanager的web UI界面,如下:

  访问prometheus的web界面,点击status->targets,可看到如下:

[root@k8s-master1 prometheus]# ss -lntup |grep 10251
[root@k8s-master1 prometheus]# ss -lntup |grep 10252
[root@k8s-master1 prometheus]#

  从上面可以发现kubernetes-controller-manager和kubernetes-schedule显示连接的端口为10251和10252,但是在10.0.0.131节点上这两个端口没有被监听,因此这两个组件端口连接不上,其次,配置文件默认监听在127.0.0.1上的,需要改成监听到物理节点上。因此,状态显示DOWN。

  解决办法:分别修改/etc/kubernetes/manifests/kube-scheduler.yaml和/etc/kubernetes/manifests/kube-controller-manager.yaml配置文件: 

  将--bind-address=127.0.0.1变成--bind-address=10.0.0.131(这里的ip地址是组件所在节点的ip地址)

  将httpGet:字段下的hosts由127.0.0.1变成10.0.0.131(这里的ip地址是组件所在节点的ip地址)

  将--port=0删除

[root@k8s-master1 prometheus]# kubectl get pods -n kube-system -o wide |grep scheduler
kube-scheduler-k8s-master1                 1/1     Running   0          8m54s   10.0.0.131       k8s-master1   <none>           <none>
[root@k8s-master1 prometheus]# kubectl get pods -n kube-system -o wide |grep kube-controller-manager
kube-controller-manager-k8s-master1        1/1     Running   1          9m51s   10.0.0.131       k8s-master1   <none>           <none>

  修改完成后,重启各个节点上的kubelet服务:

[root@k8s-master1 prometheus]# vim /etc/kubernetes/manifests/kube-scheduler.yaml
[root@k8s-master1 prometheus]# vim /etc/kubernetes/manifests/kube-controller-manager.yaml
[root@k8s-master1 prometheus]# systemctl restart kubelet
[root@k8s-master1 prometheus]# systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/kubelet.service.d
           └─10-kubeadm.conf
   Active: active (running) since Sat 2022-11-19 19:28:17 CST; 11min ago
     Docs: https://kubernetes.io/docs/
 Main PID: 115259 (kubelet)
   Memory: 47.0M
   CGroup: /system.slice/kubelet.service
           └─115259 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=...

Nov 19 19:28:24 k8s-master1 kubelet[115259]: I1119 19:28:24.548706  115259 reconciler.go:224] operationExecutor.VerifyControllerAttachedVolume...621314")
Nov 19 19:28:24 k8s-master1 kubelet[115259]: I1119 19:28:24.548739  115259 reconciler.go:224] operationExecutor.VerifyControllerAttachedVolume...621314")
Nov 19 19:28:24 k8s-master1 kubelet[115259]: I1119 19:28:24.548846  115259 reconciler.go:224] operationExecutor.VerifyControllerAttachedVolume...55dd65")
Nov 19 19:28:24 k8s-master1 kubelet[115259]: I1119 19:28:24.549464  115259 reconciler.go:224] operationExecutor.VerifyControllerAttachedVolume...55dd65")
Nov 19 19:28:24 k8s-master1 kubelet[115259]: I1119 19:28:24.549581  115259 reconciler.go:224] operationExecutor.VerifyControllerAttachedVolume...621314")
Nov 19 19:28:24 k8s-master1 kubelet[115259]: I1119 19:28:24.549630  115259 reconciler.go:224] operationExecutor.VerifyControllerAttachedVolume...621314")
Nov 19 19:28:24 k8s-master1 kubelet[115259]: I1119 19:28:24.549668  115259 reconciler.go:224] operationExecutor.VerifyControllerAttachedVolume...621314")
Nov 19 19:28:24 k8s-master1 kubelet[115259]: I1119 19:28:24.549712  115259 reconciler.go:157] Reconciler: start to sync state
Nov 19 19:34:55 k8s-master1 kubelet[115259]: E1119 19:34:55.712150  115259 remote_runtime.go:392] ExecSync d381f1b22ada140b52eb69a63885551d2f4...exceeded
Nov 19 19:37:36 k8s-master1 kubelet[115259]: I1119 19:37:36.927852  115259 scope.go:111] [topologymanager] RemoveContainer - Container ID: cd7...c44d39ef
Hint: Some lines were ellipsized, use -l to show in full.

  检查组件的健康状况:

[root@k8s-master1 prometheus]# kubectl get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-0               Healthy   {"health":"true"}

  组件相应的端口也被物理机监听

[root@k8s-master1 prometheus]# ss -antulp | grep :10251
tcp    LISTEN     0      128      :::10251                :::*                   users:(("kube-scheduler",pid=114828,fd=7))
[root@k8s-master1 prometheus]# ss -antulp | grep :10252
tcp    LISTEN     0      128      :::10252                :::*                   users:(("kube-controller",pid=124198,fd=7))

  刷新Prometheus的web ui界面,可以看到kubernetes-controller-manager和kubernetes-schedule组件状态变为UP:

  可以看到kubernetes-kube-proxy组件状态依然为DOWN,是因为kube-proxy的配置文件中的metricsBindAddress: ""为空导致的,需要将metricsBindAddress这段修改成metricsBindAddress: 0.0.0.0:10249

[root@k8s-master1 prometheus]# kubectl edit configmap kube-proxy -n kube-system
configmap/kube-proxy edited
#重新启动kube-proxy这个pod
[root@k8s-master1 prometheus]# kubectl get pods -n kube-system | grep kube-proxy |awk '{print $1}' | xargs kubectl delete pods -n kube-system
pod "kube-proxy-4tnzv" deleted
pod "kube-proxy-mnnsg" deleted
pod "kube-proxy-mxnhb" deleted
#查看监听端口
[root@k8s-master1 prometheus]# ss  -antulp |grep :10249
tcp    LISTEN     0      128      :::10249                :::*                   users:(("kube-proxy",pid=6079,fd=13))

  再次刷新Prometheus的Web UI界面,可以看到已经显示正常:

(5)查看告警信息

  在页面上点击Alerts,可看到告警信息如下:

  把controller-manager的cpu使用率大于90%展开,可看到如下

  其中,FIRING表示prometheus已经将告警发给alertmanager,在Alertmanager 中可以看到有一个 alert

  登录到alertmanager web界面,浏览器输入10.0.0.131:30066,搜索alertname="controller-manager的cpu使用率大于90%"的告警信息,显示如下:

   登录qq邮箱查看是否收到告警邮件,显示如下:

  查看发送告警邮件内容如下:

4)配置alertmanager-发送报警到钉钉

(1)创建机器人

  在电脑版钉钉创建机器人,参考文档:https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq

  打开钉钉客户端:群设置-->智能群助手-->添加机器人-->自定义-->添加-->自定义(Webhook接入)

  复制Webhook地址,并将其保存,然后点击完成

  创建完成后,钉钉页面会显示出test-alert机器人:

  创建机器人成功之后,万一没有复制Webhook地址,怎么查看webhook,方法如下:

  点击智能群助手,可以看到刚才创建的test-alert这个机器人,点击test-alert,就会进入到test-alert机器人的设置界面

(2)安装钉钉webhook插件

  安装钉钉的webhook插件(网上下载安装包prometheus-webhook-dingtalk-0.3.0.linux-amd64.tar.gz),在k8s的控制节点k8s-master1操作

[root@k8s-master1 prometheus]# tar -zxvf /data/software/prometheus-webhook-dingtalk-0.3.0.linux-amd64.tar.gz
prometheus-webhook-dingtalk-0.3.0.linux-amd64/
prometheus-webhook-dingtalk-0.3.0.linux-amd64/default.tmpl
prometheus-webhook-dingtalk-0.3.0.linux-amd64/prometheus-webhook-dingtalk
[root@k8s-master1 prometheus]# cd prometheus-webhook-dingtalk-0.3.0.linux-amd64/
#启动钉钉报警插件
[root@k8s-master1 prometheus-webhook-dingtalk-0.3.0.linux-amd64]# nohup ./prometheus-webhook-dingtalk --web.listen-address="0.0.0.0:8060" --ding.profile="cluster=https://oapi.dingtalk.com/robot/send?access_token=e16601dd8010d298361b927bd0f310bc3b4246317cfadfa2e19fd3437bdb334d" &
[1] 36805
[root@k8s-master1 prometheus-webhook-dingtalk-0.3.0.linux-amd64]# nohup: ignoring input and appending output to ‘nohup.out’

[root@k8s-master1 prometheus-webhook-dingtalk-0.3.0.linux-amd64]# ss -lntup |grep 8060
tcp    LISTEN     0      128      :::8060                 :::*                   users:(("prometheus-webh",pid=36805,fd=3))
(3)修改alertmanager-cm.yaml文件
[root@k8s-master1 prometheus]# cp alertmanager-cm.yaml alertmanager-cm.yaml.bak
[root@k8s-master1 prometheus]# vim alertmanager-cm.yaml
[root@k8s-master1 prometheus]# cat alertmanager-cm.yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: alertmanager-config
  namespace: monitor-sa
data:
  alertmanager.yml: |-
    global:
      resolve_timeout: 1m
      smtp_smarthost: 'smtp.126.com:25'
      smtp_from: 'jiaweiyzj@126.com'
      smtp_auth_username: 'jiaweiyzj@126.com'
      smtp_auth_password: '*******'  #授权码
      smtp_require_tls: false
    route:
      group_by: [alertname]
      group_wait: 10s
      group_interval: 10s
      repeat_interval: 10m
      receiver: cluster
    receivers:
    - name: 'cluster'
      webhook_configs:
      - url: 'http://10.0.0.131:8060/dingtalk/cluster/send'
        send_resolved: true
[root@k8s-master1 prometheus]# kubectl delete -f alertmanager-cm.yaml
configmap "alertmanager-config" deleted
[root@k8s-master1 prometheus]# kubectl apply -f alertmanager-cm.yaml
configmap/alertmanager-config created
[root@k8s-master1 prometheus]# kubectl get cm alertmanager-config -n monitor-sa
NAME                  DATA   AGE
alertmanager-config   1      23s
(4)更新prometheus和alertmanager

  重新更新prometheus-alertmanager-cfg.yaml和prometheus-alertmanager-deploy.yaml

[root@k8s-master1 prometheus]# kubectl delete -f prometheus-alertmanager-cfg.yaml
configmap "prometheus-config" deleted
[root@k8s-master1 prometheus]# kubectl apply -f prometheus-alertmanager-cfg.yaml
configmap/prometheus-config created
[root@k8s-master1 prometheus]# kubectl delete -f prometheus-alertmanager-deploy.yaml
deployment.apps "prometheus-server" deleted
[root@k8s-master1 prometheus]# kubectl apply -f prometheus-alertmanager-deploy.yaml
deployment.apps/prometheus-server created
[root@k8s-master1 prometheus]# kubectl get pods -n monitor-sa
NAME                                 READY   STATUS    RESTARTS   AGE
node-exporter-k4wsq                  1/1     Running   4          6d13h
node-exporter-x84r5                  1/1     Running   4          6d13h
node-exporter-zrwvh                  1/1     Running   4          6d13h
prometheus-server-646bf944c6-lvv9h   2/2     Running   0          11s
(5)查看告警信息

  在钉钉客户端查看机器人是否发出告警信息:

 5)配置alertmanager-发送报警到微信

(1)注册企业微信

  登陆网址:https://work.weixin.qq.com/,点击立即注册,注册成功后,进入后台管理,找到应用管理,创建应用,输入应用名称wechat,创建成功后获取AgentId和Secret

 

  查看后保存好Secret。可以看到AgentID:1000002

  注册完成之后,通过企业微信官网登录后台管理,在【我的企业】的企业信息里面,获取到Alertmanager服务配置需用到的第一个配置:企业ID

(2)修改alertmanager-cm.yaml
[root@k8s-master1 prometheus]# vim alertmanager-cm.yaml
[root@k8s-master1 prometheus]# cat alertmanager-cm.yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: alertmanager-config
  namespace: monitor-sa
data:
  alertmanager.yml: |-
    global:
      resolve_timeout: 1m
      smtp_smarthost: 'smtp.126.com:25'
      smtp_from: 'jiaweiyzj@126.com'
      smtp_auth_username: 'jiaweiyzj@126.com'
      smtp_auth_password: '*************' #授权码
      smtp_require_tls: false
    route:
      group_by: [alertname]
      group_wait: 10s
      group_interval: 10s
      repeat_interval: 10m
      receiver: wechat
    receivers:
    - name: 'wechat'
      wechat_configs:
      - corp_id: 'wwae4b9a0bd90a5852'  #企业ID
        to_party: '1'  #发送给整个公司
        agent_id: '1000002'   #应用agent_id
        api_secret: '***********************'  #应用的secret
        send_resolved: true
[root@k8s-master1 prometheus]# kubectl delete -f alertmanager-cm.yaml
configmap "alertmanager-config" deleted
[root@k8s-master1 prometheus]# kubectl apply -f alertmanager-cm.yaml
configmap/alertmanager-config created
[root@k8s-master1 prometheus]# kubectl describe cm alertmanager-config -n monitor-sa
Name:         alertmanager-config
Namespace:    monitor-sa
Labels:       <none>
Annotations:  <none>

Data
====
alertmanager.yml:
----
global:
  resolve_timeout: 1m
  smtp_smarthost: 'smtp.126.com:25'
  smtp_from: 'jiaweiyzj@126.com'
  smtp_auth_username: 'jiaweiyzj@126.com'
  smtp_auth_password: '************' #授权码
  smtp_require_tls: false
route:
  group_by: [alertname]
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 10m
  receiver: wechat
receivers:
- name: 'wechat'
  wechat_configs:
  - corp_id: 'wwae4b9a0bd90a5852'
    to_party: '1'
    agent_id: '1000002'
    api_secret: '****************'
    send_resolved: true
Events:  <none>

  重新更新prometheus-alertmanager-cfg.yaml和prometheus-alertmanager-deploy.yaml文件

[root@k8s-master1 prometheus]# kubectl delete -f prometheus-alertmanager-cfg.yaml
configmap "prometheus-config" deleted
[root@k8s-master1 prometheus]# kubectl apply -f prometheus-alertmanager-cfg.yaml
configmap/prometheus-config created
[root@k8s-master1 prometheus]# kubectl delete -f prometheus-alertmanager-deploy.yaml
deployment.apps "prometheus-server" deleted
[root@k8s-master1 prometheus]# kubectl apply -f prometheus-alertmanager-deploy.yaml
deployment.apps/prometheus-server created
(3)查看告警信息

  登录Prometheus和Alertmanager 的Web UI界面,查看监控变化

  再次查看Alertmanager的Web UI界面显示结果如下:

  在企业微信客户端无法接收到告警信息,查看日志报错信息如下:

[root@k8s-master1 prometheus]# kubectl logs -f prometheus-server-646bf944c6-b6r8d -c alertmanager -n monitor-sa

  显示报错信息如下,没有配置可信的ip地址

  解决办法:应用管理>自建>要发送的应用wechat>最下面拉>开发者接口>企业可信IP 配置提示IP(待测试验证)!!!

posted @ 2022-11-20 14:25  出水芙蓉·薇薇  阅读(929)  评论(0编辑  收藏  举报