prometheus 监控系统

监控系统

为什么监控,监控什么内容?

  • 当集群规模很大时,分别去看各个服务是否正常已经不可能了,所以需要一套监控系统来检测服务的健康情况。
  • 对自己系统的运行状态了如指掌,有问题及时发现,而不是用户先发现我们系统不能使用。
  • 我们也需要知道我们的服务运行情况。例如,slowsql 处于什么水平,平均响应时间超过 200ms 的占比有百分之多少?

我们为什么需要监控我们的服务?

  • 需要监控工具来提醒我服务出现了故障,比如通过监控服务的负载来决定扩容或缩容。如果机器普遍负载不高,则可以考虑是否缩减一下机器规模,如果数据库连接经常维持在一个高位水平,则可以考虑一下是否可以进行拆库处理,优化一下架构。
  • 监控还可以帮助进行内部统制,尤其是对安全比较敏感的行业,比如证券银行等。比如服务器受到攻击时,我们需要分析事件,找到根本原因,识别类似攻击,发现没有发现的被攻击的系统,甚至完成取证等工作。

监控的目的

  • 减少宕机时间
  • 扩展和性能管理
  • 资源规划
  • 识别异常事件
  • 故障排除、分析

在k8s集群中的监控系统

这张图其实就是prometheus的架构。

  • prometheus server:包括Retrieval,Storage,PromQL。

    • Retrieval:prometheus采用pull模式来扫描集群中哪些服务暴露了prometheus的指标,然后会按照既定的频率去拉取这些指标。Retrieval会轮询所有目标节点,把节点上的日志取出来。
      为什么用的pull而不是push?

      • 因为这样pod只需要收集指标就可以了,提供一个URL来访问这些指标。这样pod是不需要知道监控系统的存在,系统耦合度降低。
    • storage:prometheus有自己的tsdb,存储监控数据到硬盘。
    • PromQL:prometheus的查询语言。下文会介绍。
  • WebUI:prometheus的前端页面,可以通过PQL来查询。

  • Grafana:以图表的形式展示监控数据。

  • Alertmanager:定义一些规则来说明应用是否正常,不正常发送告警,可以和多个平台去集成。需要防止过度设计,比如可以自动恢复的服务就不需要告警。

  • pushgateway:有些pod不是一直运行,比如job,就可以推送这些指标到pushgateway。这样prometheus拉取指标到时候就直接去push gateway拉取。

file

节点上的监控指标

容器化的一个好处是监控变得标准化了。容器技术底层是cgroup和namespace。cgroup是做资源管控的,namespace是做隔离的。这样只需要扫描节点上cgroup文件夹的文件即可知道资源的使用情况。

每个节点的 kubelet(集成了 cAdvisor)会收集当前节点 host 上所有信息,包括 cpu、内存、磁盘等。Prometheus 会 pull 这些信息,给每个节点打上标签来区分不同的节点。

file

可以通过http://nodeip:9100/metrics来查看

file

如何汇报监控指标

pod中需要指定

annotations:prometheus.io/scrape: "true"

还需要声明上报指标的端口和地址即pod的ip。
上篇文章中部署的loki,就自带了prometheus的指标。

k get po loki-0 -n loki -o yaml
metadata:
    annotations:
        prometheus.io/port: http-metrics
        prometheus.io/scrape: "true"
spec:
    ports:
    - containerPort: 3100
      name: http-metrics
      protocol: TCP

在target中查看loki endpoint。
file

k8s的control panel,包括各种controller都原生的暴露 prometheus格式的metrics。这也是为什么prometheus是集群监控的标准。
file

prometheu指标类型

  • Counter(计数器器)

    • Counter 类型代表一种样本数据单调递增的指标,即只增不减,除非监控系统发生了重置。
    • 比如统计error出现的次数。
  • Gauge(仪表盘)

    • Guage 类型代表一种样本数据可以任意变化的指标,即可增可减。
    • 监控各种数值比如CPU,内存,硬盘等。
  • Histogram(直方图)

    • Histogram 在一段时间范围内对数据进行采样(通常是请求持续时间或响应大小等),并将其计入可配置的存储桶(bucket)中,后续可通过指定区间筛选样本,也可以统计样本总数,最后一般将数据展示为直方图。
    • 样本的值分布在 bucket 中的数量,命名为 _bucketlle= "<上边界>"。
    • 所有样本值的大小总和,命名为 _sum样本总数,命名为 _count。值和cbasename>_bucketlle="+Inf"}相同。
    • 一般用在统计相关和性能分析,比如统计访问http服务的耗时情况。
  • Summary(摘要)

    • 与 Histogram 类型类似,用于表示一段时间内的数据采样结果(通常是请求持续时间或响应大小等),但它直接存储了分位数(通过客户端计算,然后展示出来),而不是通过区间来计算。
    • 它们都包含了 cbasename>_sum 和 _count指标。
    • Histogram 需要通过 bucket 来计算分位数,而 Summary 则直接存储了分位数的值。
    • 用户统计区间,比如百分之90的访问耗时是0.1秒。

Prometheu Query Language

以这个为例

histogram_quantile(0.95, sum(rate(httpserver_execution_latency_seconds_ bucket(5ml)) by (le))
  1. Histogram 是直方图
  2. httpserver_execution_latency_seconds_bucke 是直方图指标,是将httpserver 处理请求的时间放入不同的bucket内,意思是落在不同时长区间的响应次数。
  3. by (le),是将采集的数据按桶的上边界分组。
    1.rate(httpserver_execution_latency_seconds_bucketl5m]),计算的是五分钟内的变化率。
  4. Sum(),是将所有指标的变化率总计。
  5. 0.95,是取 95 分位。

综上:上述表达式计算的是 httpserver 处理请求时,95% 的请求在五分钟内,落在不同响应时间区间的变化情况。

grafana dashboard

官网地址:https://grafana.com/grafana/dashboards/

复制id或者下载对应的json文件

这里列举几个id:7429,315,6417,9729
file

grafana中导入

file

file

查看图表

file

开启告警

  1. 查看prometheus的配置文件
    k exec -it loki-prometheus-server-757c887449-jqpt4 -n loki -- sh
    cat /etc/config/prometheus.yml

    file
    rule配置:
    file

  2. 查看目录文件
    file
  3. 配置文件的目录也是通过configmap挂载的,所以需要修改configmap。创建rules目录,修改prometheus配置文件。
    rule_files:
    - /tmp/rules/*.rules
  4. 进入容器创建目录,拷贝rule规则。参考链接:https://awesome-prometheus-alerts.grep.to/rules
    k exec -it loki-prometheus-server-5f48565875-66st8 -n loki -- sh
    mkdir /tmp/rules
    exit
    vim k8s.rules
    k cp k8s.rules loki-prometheus-server-5f48565875-66st8:/tmp/rules/k8s.rules -n loki

    file

  5. prometheus是有prometheus-alertmanager-configmap-reload pod的,这个容器的作用就是读取configmap,热加载prometheus的配置文件。大约1分钟后,进入容器查看prometheus的配置文件已经更新。
    file
    查看prometheus-alertmanager-configmap-reload pod日志
    file
  6. 修改alertmanager配置,alertmanager配置文件的目录也是通过configmap挂载的,所以也需要修改configmap。
    # pod的yaml
    volumeMounts:
    - mountPath: /etc/config
      name: config-volume
    volumes:
    - configMap:
      defaultMode: 420
      name: loki-prometheus-alertmanager
    name: config-volume

    不能进容器修改文件,也不能kubectl cp到目录,即使修改了目录权限,以root用户登录容器也不行。

    #修改cm中global字段的配置
    global:
      resolve_timeout: 5m #处理超时时间,默认为5min
      smtp_smarthost: 'smtp.qq.com:465' # 邮箱smtp服务器代理
      smtp_from: 'aaa@qq.com' # 发送邮箱名称
      smtp_auth_username: 'xxx@qq.com' # 邮箱名称
      smtp_auth_password: 'qwertyuio' # 邮箱密码或授权码
      smtp_require_tls: false
    # 定义模板信息
    templates:
      - '/alertmanager/template/alertmanager-email.tmpl'
    route:  # 路由组
      group_by: ['alertname', 'app']
      group_wait: 30s
      group_interval: 40s
      repeat_interval: 1m
      receiver: email-receiver          # 发送给那个组
    receivers:                                # 收件人组
    - name: 'email-receiver'
      email_configs:
      - to: 'xxx@qq.com'     # 收件人地址
        html: '{{ template "email.to.html" . }}' # HTML模板文件正文
        send_resolved: true
    inhibit_rules:
      - source_match:
          severity: 'critical'
        target_match_re:
          severity: '.*'
        equal: ['instance']
  7. alertmanager是有prometheus-alertmanager-configmap-reload容器的,作用和prometheus的pod一样,等待更新完成。
    file
  8. 拷贝模版文件到容器

    crictl ps | grep alert
    nerdctl -n k8s.io exec -it -u root 1f46997746b96 sh
    mkdir /alertmanager/template
    k cp alertmanager-email.tmpl loki/loki-prometheus-alertmanager-86c8f5c8d6-mdjgq:/alertmanager/template/

    模版文件如下:

    cat alertmanager-email.tmpl
    {{ define "email.to.html" }}
    {{- if gt (len .Alerts.Firing) 0 -}}
    {{- range $index, $alert := .Alerts -}}
    
    ========= <span style=color:red;font-size:36px;font-weight:bold;> 监控告警 </span>=========<br>
    
    <span style=font-size:20px;font-weight:bold;> 告警程序:</span>     Alertmanage <br>
    <span style=font-size:20px;font-weight:bold;> 告警类型:</span>    {{ $alert.Labels.alertname }} <br>
    <span style=font-size:20px;font-weight:bold;> 告警级别:</span>    {{ $alert.Labels.severity }} 级 <br>
    <span style=font-size:20px;font-weight:bold;> 告警状态:</span>    {{ .Status }} <br>
    <span style=font-size:20px;font-weight:bold;> 故障主机:</span>    {{ $alert.Labels.instance }} {{ $alert.Labels.device }} <br>
    <span style=font-size:20px;font-weight:bold;> 告警主题:</span>    {{ .Annotations.summary }} <br>
    <span style=font-size:20px;font-weight:bold;> 告警详情:</span>    {{ $alert.Annotations.message }}{{ $alert.Annotations.description}} <br>
    <span style=font-size:20px;font-weight:bold;> 主机标签:</span>    {{ range .Labels.SortedPairs  }} <br> [{{ .Name }}: {{ .Value  | html }} ]{{ end }}<br>
    <span style=font-size:20px;font-weight:bold;> 故障时间:</span>    {{ ($alert.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}<br>
    
    ========= = end =  =========<br>
    <br>
    <br>
    <br>
    <br>
    
    <div>
        <div style=margin:40px>
            <p style=font-size:20px>wghdr</p>
            <p style=color:red;font-size:14px>
            (这是一封自动发送的邮件,请勿回复。)
            </p>
        </div>
    
        <div align=right style="margin:40px;border-top:solid 1px gray" id=bottomTime>
            <p style=margin-right:20px>
                WghDr Ops
            </p>
            <label style=margin-right:20px>
                {{ ($alert.StartsAt.Add 28800e9).Format "2006-01-02 " }}<br>
            </label>
        </div>
    </div>
    
    {{- end }}
    {{- end }}
    {{- if gt (len .Alerts.Resolved) 0 -}}
    {{- range $index, $alert := .Alerts -}}
    ========= <span style=color:#00FF00;font-size:24px;font-weight:bold;> 告警恢复 </span>=========<br>
    <span style=font-size:20px;font-weight:bold;> 告警程序:</span>    Alertmanage <br>
    <span style=font-size:20px;font-weight:bold;> 告警主题:</span>    {{ $alert.Annotations.summary }}<br>
    <span style=font-size:20px;font-weight:bold;> 告警主机:</span>    {{ .Labels.instance }} <br>
    <span style=font-size:20px;font-weight:bold;> 告警类型:</span>    {{ .Labels.alertname }}<br>
    <span style=font-size:20px;font-weight:bold;> 告警级别:</span>    {{ $alert.Labels.severity }} 级 <br>
    <span style=font-size:20px;font-weight:bold;> 告警状态:</span>    {{   .Status }}<br>
    <span style=font-size:20px;font-weight:bold;> 告警详情:</span>    {{ $alert.Annotations.message }}{{ $alert.Annotations.description}}<br>
    <span style=font-size:20px;font-weight:bold;> 故障时间:</span>    {{ ($alert.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}<br>
    <span style=font-size:20px;font-weight:bold;> 恢复时间:</span>    {{ ($alert.EndsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}<br>
    {{- end }}
    ========= = end =  =========
    <br>
    <br>
    <br>
    <br>
    
    <div>
        <div style=margin:40px>
            <p style=font-size:20px>wghdr</p>
            <p style=color:red;font-size:14px>
            (这是一封自动发送的邮件,请勿回复。)
            </p>
        </div>
    
        <div align=right style="margin:40px;border-top:solid 1px gray" id=bottomTime>
            <p style=margin-right:20px>
                WghDr Ops
            </p>
            <label style=margin-right:20px>
                {{ ($alert.StartsAt.Add 28800e9).Format "2006-01-02 " }}<br>
            </label>
        </div>
    </div>
    {{- end }}
    {{- end }}
    {{- end }}

构建生产系统的监控

  • Metrics
    收集数据。
  • Alert
    创建告警规则,如果告警规则被触发,按照不同级别来告警。
  • Assertion
    比如有些组件不是那么活跃,平时没什么人用,如果有问题的话不会充分暴露出来。
    那么创建Assertion的pod,类似Cluster Autoscaler中的Simulator以一定时间间隔,模拟客户行为,操作k8s对象,并断言成功,如果不成功则按照不同级别告警。

注意

  1. Prometheus 需要大内存和存储

    • Prometheus自身有tsdb需要存储数据,如果给定的硬盘很小,Prometheus会经常发生 O0M kill。
    • 在提高硬盘以后,发现内存占用也很高,kubelet就把它踢走了,踢到了别的node上,如果存储用的hostpath,那么新节点上的硬盘不久也会被撑爆了。
    • 如果发生 crash 或者重启,Prometheus 需要 30 分钟以上的时间来读取数据进行初始化。
  2. Prometheus 是运营生产系统过程中最重要的模块

    • 如果 Prometheus down 机,则没有任何数据和告警,管理员两眼一黑,什么都不知道了。
    • 解决方法:用其他集群中的prometheus互相监控。
0 0 投票数
文章评分
订阅评论
提醒
guest

0 评论
最旧
最新 最多投票
内联反馈
查看所有评论

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部
0
希望看到您的想法,请您发表评论x