mac 上学习k8s系列(30)prometheus part II

2022-08-02 19:36:14 浏览数 (5)

在上一讲mac 上学习k8s系列(29)prometheus part I搭建完k8s prometheus的基础环境后,我们开始完成一段完整的应用监控。使用

https://github.com/prometheus/client_golang 通过matrix协议,提供http服务,prometheus采用轮询的方式获取指标数据,即拉的方式。当然为了满足推这种方式的需求,prometheus提供了Pushgateway组件进行接收并临时存储数据,作为一个中间层,实现完美适配。对于mysql和redis等服务,由于出现比prometheus早,没有暴露matrix协议接口,因此需要相关的exporter,提取相关数据,暴露给prometheus。

matrix协议提供了四种指标:

1,Counter(计数器):是一个累计的指标,代表一个单调递增的计数器,它的值只会增加或在重启时重置为零。

代码语言:javascript复制
package main

import "github.com/prometheus/client_golang/prometheus"

var (
  hdFailures = prometheus.NewCounterVec(
    prometheus.CounterOpts{
      Name: "hd_errors_total",
      Help: "Number of hard-disk errors.",
    },
    []string{"device"},
  )
)

func init() {
  prometheus.MustRegister(hdFailures)
}

2,Gauge(计量器):是代表一个数值类型的指标,它的值可以增或减。用于一些度量的值例如温度或是当前内存使用。

代码语言:javascript复制
package main

import "github.com/prometheus/client_golang/prometheus"

var (
  cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{
    Name: "cpu_temperature_celsius",
    Help: "Current temperature of the CPU.",
  })
)

func init() {
  // Metrics have to be registered to be exposed:
  prometheus.MustRegister(cpuTemp)
}

3,Histogram(分布图):对观测值进行采样,并用一些可配置的桶来计数。给出一个所有观测值的总和。

代码语言:javascript复制
package main

import (
  "fmt"

  "github.com/prometheus/client_golang/prometheus"
)

var (
  TemperatureHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
    Name:    "beijing_temperature",
    Help:    "The temperature of the beijing",
    Buckets: prometheus.LinearBuckets(0, 10, 3),
  })
)

func InsertTemperature() {
  var temperature = [10]float64{1, 4, 5, 10, 14, 15, 20, 25, 11, 30}
  for i := 0; i < len(temperature); i   {
    TemperatureHistogram.Observe(temperature[i])
    fmt.Printf("insert number: %f n", temperature[i])
  }
}

func init() {
  prometheus.MustRegister(TemperatureHistogram)
}

4,Summary(摘要):也对观测值采样。给出一个总数以及所有观测值的总和,它在一个滑动的时间窗口上计算可配置的分位数。

代码语言:javascript复制
package main

import (
  "fmt"

  "github.com/prometheus/client_golang/prometheus"
)

var (
  SalarySummary = prometheus.NewSummary(prometheus.SummaryOpts{
    Name:       "beijing_salary",
    Help:       "the relationship between salary and population of beijing city",
    Objectives: map[float64]float64{0.5: 0.05, 0.8: 0.001, 0.9: 0.01, 0.95: 0.01},
  })
)

func InsertSummary() {
  var salary = [10]float64{8000, 7000, 8900, 10000, 9800, 17000, 15000, 14000, 11000, 12000}
  for i := 0; i < len(salary); i   {
    SalarySummary.Observe(salary[i])
    fmt.Printf("Insert number: %f n", salary[i])
  }
}

func init() {
  prometheus.MustRegister(SalarySummary)
}

我们在启动一个服务实现上述四种指标

代码语言:javascript复制
package main

import (
  "fmt"
  "log"
  "math"
  "math/rand"
  "net/http"

  "github.com/gogo/protobuf/proto"
  "github.com/prometheus/client_golang/prometheus"
  "github.com/prometheus/client_golang/prometheus/promhttp"
  dto "github.com/prometheus/client_model/go"
)
func main() {
    http.HandleFunc("/httpInc", func(w http.ResponseWriter, r *http.Request) {
    for i := 0; i < rand.Int()0; i   {
      hdFailures.With(prometheus.Labels{"device": "/dev/sda"}).Inc()
    }
    hdFailures.With(prometheus.Labels{"device": "/dev/sda"}).Inc()

    cpuTemp.Set(65.3   rand.Float64()*100)

    InsertTemperature()
    InsertSummary()

    fmt.Fprintf(w, "httpInc")
    return
  })
  // The Handler function provides a default handler to expose metrics
  // via an HTTP server. "/metrics" is the usual endpoint for that.
  http.Handle("/metrics", promhttp.Handler())
  log.Fatal(http.ListenAndServe(":12345", nil))
}

当我们请求http接口的路径httpInc时,更新上述4个指标,各个指标通过metrics路径暴露给prometheus。制作镜像

代码语言:javascript复制
FROM scratch
WORKDIR /
COPY matrix matrix
CMD ["./matrix"]

部署服务

代码语言:javascript复制
apiVersion: apps/v1  # 指定api版本,此值必须在kubectl api-versions中  
kind: Deployment  # 指定创建资源的角色/类型   
metadata:  # 资源的元数据/属性 
  name: matrix-deployment  # 资源的名字,在同一个namespace中必须唯一
  namespace: default # 部署在哪个namespace中
  labels:  # 设定资源的标签
    app: matrix-deployment
    version: stable
spec: # 资源规范字段
  replicas: 1 # 声明副本数目
  revisionHistoryLimit: 3 # 保留历史版本
  selector: # 选择器
    matchLabels: # 匹配标签
      app: matrix-deployment
      version: stable
  strategy: # 策略
    rollingUpdate: # 滚动更新
      maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数
      maxUnavailable: 30% # 示在更新过程中能够进入不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
    type: RollingUpdate # 滚动更新策略
  template: # 模版
    metadata: # 资源的元数据/属性 
      annotations: # 自定义注解列表
        sidecar.istio.io/inject: "false" # 自定义注解名字
      labels: # 设定资源的标签
        app: matrix-deployment
        version: stable
    spec: # 资源规范字段
      containers:
      - name: matrix-deployment # 容器的名字   
        image: matrix:0.0.3 # 容器使用的镜像地址   
        imagePullPolicy: IfNotPresent # 每次Pod启动拉取镜像策略,三个选择 Always、Never、IfNotPresent
                                      # Always,每次都检查;Never,每次都不检查(不管本地是否有);IfNotPresent,如果本地有就不检查,如果没有就拉取 
        resources: # 资源管理
          limits: # 最大使用
            cpu: 300m # CPU,1核心 = 1000m
            memory: 500Mi # 内存,1G = 1024Mi
          requests:  # 容器运行时,最低资源需求,也就是说最少需要多少资源容器才能正常运行
            cpu: 100m
            memory: 100Mi
       # livenessProbe: # pod 内部健康检查的设置
       #   httpGet: # 通过httpget检查健康,返回200-399之间,则认为容器正常
       #     path: /healthCheck # URI地址
       #     port: 8080 # 端口
       #     scheme: HTTP # 协议
            # host: 127.0.0.1 # 主机地址
       #   initialDelaySeconds: 30 # 表明第一次检测在容器启动后多长时间后开始
       #   timeoutSeconds: 5 # 检测的超时时间
       #   periodSeconds: 30 # 检查间隔时间
       #   successThreshold: 1 # 成功门槛
       #   failureThreshold: 5 # 失败门槛,连接失败5次,pod杀掉,重启一个新的pod
       # readinessProbe: # Pod 准备服务健康检查设置
       #   httpGet:
       #     path: /healthCheck
       #     port: 8080
       #     scheme: HTTP
       #   initialDelaySeconds: 30
       #   timeoutSeconds: 5
       #   periodSeconds: 10
       #   successThreshold: 1
       #   failureThreshold: 5
        #也可以用这种方法   
        #exec: 执行命令的方法进行监测,如果其退出码不为0,则认为容器正常   
        #  command:   
        #    - cat   
        #    - /tmp/health   
        #也可以用这种方法   
        #tcpSocket: # 通过tcpSocket检查健康  
        #  port: number 
        ports:
          - name: http # 名称
            containerPort: 12345 # 容器开发对外的端口 
            protocol: TCP # 协议
    #  imagePullSecrets: # 镜像仓库拉取密钥
    #    - name: harbor-certification
    #  affinity: # 亲和性调试
    #    nodeAffinity: # 节点亲和力
    #      requiredDuringSchedulingIgnoredDuringExecution: # pod 必须部署到满足条件的节点上
    #        nodeSelectorTerms: # 节点满足任何一个条件就可以
    #        - matchExpressions: # 有多个选项,则只有同时满足这些逻辑选项的节点才能运行 pod
    #          - key: beta.kubernetes.io/arch
    #            operator: In
    #            values:
    #            - amd64

---
apiVersion: v1 # 指定api版本,此值必须在kubectl api-versions中 
kind: Service # 指定创建资源的角色/类型 
metadata: # 资源的元数据/属性
  name: matrix-deployment # 资源的名字,在同一个namespace中必须唯一
  namespace: default # 部署在哪个namespace中
  labels: # 设定资源的标签
    app: matrix-deployment
spec: # 资源规范字段
  type: ClusterIP # ClusterIP 类型
  ports:
    - port: 12345 # service 端口
      targetPort: 12345 # 容器暴露的端口
      protocol: TCP # 协议
      name: http # 端口名称
  selector: # 选择器
    app: matrix-deployment

部署完服务后,我们就可以配置prometheus,来对服务进行监控,但是比较坑爹的是,prometheus的配置文件必须写在一起,我们只能在上一讲的Configmap里进行修改。prometheus的配置文件中包含了3个模块:global、rule_files 和 scrape_configs。

A,global

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

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

B,rule_files:指定了报警规则所在的位置。

C,scrape_configs 用于控制 prometheus 监控哪些资源。

我们修改scrape_configs就可以了

代码语言:javascript复制
 % kubectl get configMap prometheus-config -n prom -o yaml > prometheus-config.yaml

增加我们的服务项

代码语言:javascript复制
 - job_name: 'kubernetes-matrix-xzm'
  static_configs:
    - targets: ['matrix-deployment.default.svc:12345']
代码语言:javascript复制
% kubectl  apply -f prometheus-config.yaml
configmap/prometheus-config configured

在prometheus的端口可以看到我们暴露的服务:

代码语言:javascript复制
http://127.0.0.1:30090/targets
kubernetes-matrix-xzm (1/1 up) 

在grafana的界面可以搜到我们的指标了。但是数据没有变,我们可以请求http接口,来更新我们的指标数据。但是我们的服务没有暴露端口,需要配置一个ingress

代码语言:javascript复制
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-matrix
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
  labels:
    matrix-ingress: matrix-deployment
spec:
  rules:
  #The Ingress "ingress-with-auth" is invalid: spec.rules[0].host: Invalid value: "127.0.0.1": must be a DNS name, not an IP address
  #- host: apple-app
    #www.xzm-text789.com或者service的名字都不行,所以不指定域名
  #error: error validating "noAuth/ingress.yaml": error validating data: ValidationError(Ingress.spec.rules): invalid type for io.k8s.api.networking.v1.IngressSpec.rules: got "map", expected "array"; if you choose to ignore these errors, turn validation off with --validate=false
   - host: localhost
     http:
      paths:
      #The Ingress "ingress-with-auth" is invalid: spec.rules[0].http.paths[0].pathType: Required value: pathType must be specified
      - pathType: Prefix
        path: /
        backend:
          #unknown field "serviceName" in io.k8s.api.networking.v1.IngressBackend, ValidationError(Ingress.spec.rules[0].http.paths[0].backend): unknown field "servicePort" in io.k8s.api.networking.v1.IngressBackend]; if you choose to ignore these errors, turn validation off with --validate=false
          #serviceName: http-svc
          #servicePort: 80
          service:
            name: matrix-deployment
            port:
              number: 12345

测试下

代码语言:javascript复制
 % curl http://localhost:80/httpInc
httpInc

压测来造数据

代码语言:javascript复制
% ab -n 200 http://localhost:80/httpInc 

然后,我们可以看到grafana数据的变化。我们可以通过promql来丰富我们的监控查询。通过请求matrix接口可以得到样本数据

代码语言:javascript复制
# 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"} 6.62885731e 06

# 开头表示注释,node_cpu_seconds_total是指标名称,{}里面的是标签,我们可以通过标签来筛选,浮点数则是该监控样本的具体值。Prometheus 会将所有采集到的样本数据以时间序列的方式保存在内存数据库中,并且定时保存到硬盘上。时间序列是按照时间戳和值的序列顺序存放的,我们称之为向量(vector),每条时间序列通过指标名称(metrics name)和一组标签集(labelset)命名。在时间序列中的每一个点称为一个样本(sample),样本由以下三部分组成:

A,指标(metric):metric name 和描述当前样本特征的 labelsets

B,时间戳(timestamp):一个精确到毫秒的时间戳

C,样本值(value): 一个 float64 的浮点型数据表示当前样本的值

代码语言:javascript复制
<--------------- metric ---------------------><-timestamp -><-value->
http_request_total{status="200", method="GET"}@1434417560938 => 94355

promql的格式如下

代码语言:javascript复制
delta(cpu_temp_celsius{host="zeus"}[2h])
predict_linear(node_filesystem_free_bytes[1h], 4 * 3600)

包括一系列函数,可以通过{}来进行标签筛选,通过[]来做事件聚合,最终生成我们需要的监控图表。

0 人点赞