prometheus监控(二)

为什么prometheus天然适合监控kubernetes
指标分析

容器基础资源指标 | kubelet 内置cadvisor metrics接口 | 查看容器cpu、mem利用率等 |k8s_sd node级别直接访问node_ip| 
k8s资源指标 通过coredns访问
k8s服务组件指标| 服务组件 metrics接口 | 查看apiserver 、scheduler、etc、coredns请求延迟等 | k8s_sd endpoint级别
部署在pod中业务埋点指标| pod 的metrics接口 |  依据业务指标场景 | k8s_sd pod级别,访问pod ip的metricspath |

适配分析

k8s服务发现  | 通过watch即时发现资源变化
| 各个组件metrics自暴露  | 所有组件将自身指标暴露在各自的服务端口上,prometheus通过pull过来拉取指标 
| 鉴权  | k8s的组件接口都是要鉴权的,所以k8s的采集器要支持配置鉴权 | 支持配置token和tls证书 |
| 标签relabel能力  | 过滤服务发现标的  | `labelmap`去掉服务发现标签的长前缀 |

奥妙

  • prometheus是pull模型采集的,各个被监控的源只需要将自身指标暴露在本地http端口中,prometheus就可以访问接口来采集指标
  • prometheus在k8s中也是这样的,组件需要暴露自身指标,如我们在容器基础资源指标中提到的kubelet 内置cadvisor指标就是暴露在10250端口下的/metrics/cadvisor下。
  • prometheus通过 k8s服务发现这些指标源完成采集

举例

  • 一、endpoint级别的服务发现 :举例 在采集apiserver、kube-controller-manager等
kubernetes_sd_configs:
   role: endpoints
  • 二、node级别的服务发现 :举例 在采集cadvisor和kubelet自身指标时
  kubernetes_sd_configs:
  - role: node

解读:watch即时更新

  • 通过watch即时发现资源变化,就满足了我们一开始提出的云原生情况下监控的挑战之一,要及时感知到采集源的变化。
  • 同时在k8s大二层环境中,prometheus可以访问到发现出来的的 endpoint 、node、pod

适配3. 采集鉴权:token & 证书

k8s中很多接口都要鉴权,甚至还需要tls双向认证

  • 同时我们知道在k8s中很多接口都是带有访问鉴权的,比如我们直接访问k8s node上的kubelet的/metrics/cadvisor接口会返回未授权。如下面所示
[root@Kubernetes-node01 logs]# curl -k https://localhost:10250/metrics/cadvisor
Unauthorized
  • prometheus在采集cadvisor指标时同样面临鉴权问题

解决方法

  • 聪明的prometheus开发人员通过在采集中支持配置中相关token和证书来解决这个问题,如下面的配置代表有一个token文件,同时还有一个证书文件。
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true

适配4. 强大的relabel能力 做标签截取、变换、静态分片

prometheus relabel说明

  • 文档地址 https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config

应用1: labelmap 在采集cadvisor指标时 对服务发现标签key名字截取

  • 在采集cadvisor时可以看到服务发现源给添加了很多__meta_kubernetes_node_label_开头的标签

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MTaCNEEW-1687484851787)(/img/bVcSC9K)]

  • 但是这些标签名字太长了,需要精简。我们使用如下的relabel配置
relabel_configs:
- separator: ;
regex: __meta_kubernetes_node_label_(.+)
replacement: $1
action: labelmap
  • 以这个标签为例,__meta_kubernetes_node_label_kubernetes_io_os="linux",上面的配置代表匹配_meta_kubernetes_node_label_开头的key,只保留后面的部分,所以在最终的标签看到的就是beta_kubernetes_io_os="linux"
  • labelmap代表匹配到的标签赋值给目标标签

应用2: replace 在采集pod自定义指标 对标签进行赋值

  • 我们在使用pod自定义指标时在pod yaml 的spec.template.metadata.annotations中需要定义三个以prometheus.io开头的配置,分布代表是否需要prometheus采集、metrics暴露的端口、metrics的http path信息,详细配置如下:
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
      annotations:
        prometheus.io/scrape: 'true'
        prometheus.io/port: '9102'
        prometheus.io/path: 'metrics'
  • 在采集pod自定义指标时采用如下
  relabel_configs:
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
    separator: ;
    regex: "true"
    replacement: $1
    action: keep
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
    separator: ;
    regex: (.+)
    target_label: __metrics_path__
    replacement: $1
    action: replace
  - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
    separator: ;
    regex: ([^:]+)(?::\d+)?;(\d+)
    target_label: __address__
    replacement: $1:$2
    action: replace
  • 意思是将__meta_kubernetes_pod_annotation_prometheus_io_path赋值给 __metrics_path__
  • 意思是将相关 __meta_kubernetes_pod_annotation_prometheus_io_port赋值给 __address__后面的端口

应用2: keep 做过滤,在采集服务组件endpoint时

  • endpoint资源是暴露一个服务的ip地址和port的列表
  • 代表采用k8s服务发现 endpoint,endpoint会非常多,所以需要过滤apiserver的
kubernetes_sd_configs:
- role: endpoints
  • 过滤手段为 标签 __meta_kubernetes_namespace匹配default并且 __meta_kubernetes_service_name 匹配kubernetes 并且 __meta_kubernetes_endpoint_port_name 匹配https,咋样呢 : keep
relabel_configs:
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
separator: ;
regex: default;kubernetes;https
replacement: $1
action: keep

  • k8s 会在default namespace中创建apiserver的 service
$ kubectl get svc -A |grep  443
default         kubernetes                                     ClusterIP   10.96.0.1       <none>        443/TCP                  9d
  • 最后获取到的endpoint转换为采集路径为: https://masterip:6443/metrics

一个完整的配置如下

- 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: ['192.168.40.180:10251']
    - job_name: 'kubernetes-controller-manager'
      scrape_interval: 5s
      static_configs:
      - targets: ['192.168.40.180:10252']
    - job_name: 'kubernetes-kube-proxy'
      scrape_interval: 5s
      static_configs:
      - targets: ['192.168.40.180:10249','192.168.40.181: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: ['192.168.40.180:2379']

指标分析

容器基础资源指标

cpu

  • cpu 在Kubernetes中CPU属于可压缩资源,意思是pod中服务使用CPU超过设置的limits,pod不会被kill掉但会被限制。所以我们应该通过观察容器CPU被限制的情况来考虑是否将CPU的limit调大。
  • 有这样的两个CPU指标,container_cpu_cfs_periods_total代表 container生命周期中度过的CPU周期总数,container_cpu_cfs_throttled_periods_total代表container生命周期中度过的受限的CPU周期总数。
  • 所以我们可以使用下面的表达式来查出最近5分钟,超过25%的CPU执行周期受到限制的container有哪些。
 100 * sum by(container_name, pod_name, namespace) (increase(container_CPU_cfs_throttled_periods_total{container_name!=""}[5m]))
/ sum by(container_name, pod_name, namespace) (increase(container_CPU_cfs_periods_total[5m])) > 25
  • 我们可以用下面的计算方式表示容器CPU使用率,其中container_cpu_usage_seconds_total代表CPU的计数器,container_spec_cpu_quota是容器的CPU配额,它的值是容器指定的CPU个数*100000。
sum(rate(container_CPU_usage_seconds_total{image!=""}[1m])) by (container, pod) / (sum(container_spec_CPU_quota{image!=""}/100000) by (container, pod) )* 100

mem

  • 在Kubernetes中mem属于不可压缩资源,pod之间是无法共享的,完全独占的。所以一旦容器内存使用超过limits,会导致oom,然后重新调度。
  • container_memory_working_set_bytes是容器真实使用的内存量, kubelet通过比较container_memory_working_set_bytescontainer_spec_memory_limit_bytes来决定oom container。
  • 同时还有container_memory_usage_bytes用来表示容器使用内存,其中包含了很久没用的缓存,该值比container_memory_working_set_bytes要大
  • 所以内存使用率可以使用下面的公式计算
(container_memory_working_set_bytes/container_spec_memory_limit_bytes )*100

其他

容器网卡流量的计算和机器上类似,通过对相关counter做rate即可,比如

  • 容器网卡入流量 rate(container_network_receive_bytes_total[1m])
  • 网卡出流量 rate(container_network_transmit_bytes_total[1m])
  • 文件系统使用率,可以用下面计算
    • container_fs_usage_bytes代表使用
    • container_fs_limit_bytes代表限制
    • (container_fs_usage_bytes/container_fs_limit_bytes) *100

k8s对象资源指标

pod状态

下面的表格中一系列指标描述容器的运行状态。

指标名含义标签举例
kube_pod_status_phase容器当前的运行状态,非Running的都是异常的。Pending
Succeeded
Failed
Running
Unknown
kube_pod_container_status_waiting容器处于waiting状态值为1代表waiting
kube_pod_container_status_waiting_reasonpod处于waiting状态原因ContainerCreating
CrashLoopBackOff pod启动崩溃,再次启动然后再次崩溃
CreateContainerConfigError
ErrImagePull
ImagePullBackOff
CreateContainerError
InvalidImageName
kube_pod_container_status_terminatedpod处于terminated状态值为1代表terminated
kube_pod_container_status_terminated_reasonpod处于terminated状态原因OOMKilled
Completed
Error
ContainerCannotRun
DeadlineExceeded
Evicted
kube_pod_container_status_restarts_totalpod中的容器重启次数-
  • 下面给出一些经常使用的告警。

  • 比如查看下因为拉取镜像失败导致waiting的容器kube_pod_container_status_waiting_reason{reason="ErrImagePull"}==1

  • 查看下发生oom的容器kube_pod_container_status_last_terminated_reason{reason="OOMKilled"}==1

  • 最近十分钟内有重启(kube_pod_container_status_restarts_total - kube_pod_container_status_restarts_total offset 10m >= 1)

node状态

  • 那么站在Kubernetes集群管理员的角度,也需要关心下node整体的资源情况。
指标名含义标签举例
kube_node_status_conditionnode节点的运行状态,非Ready都是异常
同时可以看到因为哪种资源压力导致的
condition:
NetworkUnavailable
MemoryPressure
DiskPressure
PIDPressure
Ready
kube_node_status_allocatable_CPU_cores节点可以分配CPU核数
kube_node_status_allocatable_memory_bytes节点可以分配内存总量(单位:字节)
kube_node_spec_taint节点污点情况
  • 下面给出一些经常使用的告警。

  • 比如查看节点因为内存有压力不可用kube_node_status_condition{condition="MemoryPressure",status="true"}==1

其他资源的常见指标

  • dep 副本数不正常 kube_deployment_spec_replicas!= kube_deployment_status_replicas_available
  • daemonset中不可用的数量 :kube_daemonset_status_number_unavailable > 0来表示
  • 可以使用kube_job_failed > 0来表示失败的job
  • 在kube-state-metrics中还有很多其他对象的指标,你可以自行查阅使用。

k8s服务组件指标

四大黄金指标

延迟、请求qps、错误数、饱和度

  • 站在Kubernetes集群管理员的角度,服务组件的健康状况需要额外的关注。
apiserver
  • 在监控apiserver时,我们可以重点关注四大黄金指标:延迟、请求qps、错误数、饱和度。

  • apiserver_request_total代表apiserver的请求计数器,所以我们可以使用sum(rate(apiserver_request_total{job=“kubernetes-apiservers”,code=~“2…”}[5m]))来计算apiserver请求成功的qps。

  • 所以响应=2xx的qps除以总的qps就是apiserver的请求成功率,表达式如下。可以设置成功率低于95%的告警。

  • 100 * sum(rate(apiserver_request_total{job=“kubernetes-apiservers”,code=~“2…”}[5m])) /sum(rate(apiserver_request_total{job=“kubernetes-apiservers”}[5m]))

    • 同理也可以关注4xx和5xx的错误qps,表达式如下
    • sum(rate(apiserver_request_total{job=“kubernetes-apiservers”,code=~"[45] [5m]))
    • 错误的qps过高,可能是服务组件有问题,需要尽快排查。
  • 对于延迟,可以使用下面的表达式计算。

  • histogram_quantile(0.99, sum(rate(apiserver_request_duration_seconds_bucket{job=“kubernetes-apiservers”}[5m])) by (verb, le))

  • 可以得到各个http的请求方法的99分位延迟值。

{verb="WATCH"} 60
{verb="DELETE"} NaN
{verb="PATCH"} 0.0495
{verb="PUT"} 0.08797499999999975
{verb="GET"} 0.06524999999999985
{verb="LIST"} 0.09421428571428572
{verb="POST"} 0.0495

  • 如果99分位延迟值很高,可能是apiserver处理能力达到上限,可以考虑扩容一下。

  • 对于饱和度可以查看apiserver请求队列的情况,如apiserver_current_inqueue_requests很大的话,说明排队严重。

etcd作为Kubernetes中元信息存储的数据库也需要额外关注下。
  • etcd存储文件大小相关指标,比如etcd_db_total_size_in_bytes表征db物理文件大小。

    • 使用下面表达式可以得到etcd存储空间使用率: 当前使用量/配额。如果使用率大于80%需要扩容
    • (etcd_mvcc_db_total_size_in_bytes / etcd_server_quota_backend_bytes)*100
  • 关于etcd的网络流量可以使用下面两个指标表示。

    • etcd_network_client_grpc_received_bytes_total代表client调etcd的流量。
    • etcd_network_client_grpc_sent_bytes_total代表etcd发送的流量。
  • etcd中存储key和相关key操作的qps指标,如etcd_debugging_mvcc_keys_total代表etcd中存储的key总数,数量太多也会影响性能。

    • 同时关于etcd key的操作的qps,rate(etcd_debugging_mvcc_put_total[1m])代表put的qps,同理 rate(etcd_debugging_mvcc_delete_total[1m])代表删除的qps。
  • 存储的fsync刷盘99分位延迟可以使用下面的分位值计算得到

  • histogram_quantile(0.99, sum(rate(etcd_disk_backend_commit_duration_seconds_bucket[5m])) by (instance, le))

kube-scheduler是调度器,所以有关调度成功统计的指标都应被关注。
  • 如scheduler_pod_scheduling_attempts_sum/scheduler_pod_scheduling_attempts_count代表成功调度一个pod 的平均尝试次数。如果尝试次数过高,可能当前node剩余量不多,或者集群出错,建议排查下。

  • 如histogram_quantile(0.99, sum(rate(scheduler_pod_scheduling_duration_seconds_bucket[5m])) by ( le)) 代码pod调度的99分位延迟,如果过高,考虑schduler压力大或者其他原因。

  • 在kube-controller-manager负责集群内的 Node、Pod 等所有资源的管理。

  • 如rate(workqueue_adds_total[2m])表征工作队列新增的qps,其实就是请求的qps,太高考虑压力大。

  • 如histogram_quantile(0.99, sum(rate(rest_client_request_duration_seconds_bucket{job=“kube-controller-manager”}[5m])) by (verb, url, le)),可以查看和apiserver通信的延迟99分位值,太高考虑扩容下apiserver。

pod业务埋点指标

各个业务自行决定即可。

prometheus与consul结合

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: consul
spec:
  serviceName: consul
  replicas: 3
  selector:
    matchLabels:
      app: consul
  template:
    metadata:
      labels:
        app: consul
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: consul
        image: consul:latest
        imagePullPolicy: IfNotPresent
        args:
             - "agent"
             - "-server"
             - "-bootstrap-expect=3"
             - "-ui"
             - "-data-dir=/consul/data"
             - "-bind=0.0.0.0"
             - "-client=0.0.0.0"
             - "-advertise=$(PODIP)"
             - "-retry-join=consul-0.consul.$(NAMESPACE).svc.cluster.local"
             - "-retry-join=consul-1.consul.$(NAMESPACE).svc.cluster.local"
             - "-retry-join=consul-2.consul.$(NAMESPACE).svc.cluster.local"
             - "-domain=cluster.local"
             - "-disable-host-node-id"
        env:
            - name: PODIP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
        ports:
            - containerPort: 8500
              name: ui-port
            - containerPort: 8443
              name: https-port
---
apiVersion: v1
kind: Service
metadata:
  name: consul
  labels:
    name: consul
spec:
  type: NodePort
  ports:
    - name: http
      port: 8500
      nodePort: 30007
      targetPort: 8500
    - name: https
      port: 8443
      nodePort: 30008
      targetPort: 8443
  selector:
    app: consul

使用python向consul发送请求

import consul
import asyncio
import logging

logging.basicConfig(
    # TODO console 日志,上线时删掉
    # filename=LOG_PATH,
    format='%(asctime)s %(levelname)s %(filename)s %(funcName)s [line:%(lineno)d]:%(message)s',
    datefmt="%Y-%m-%d %H:%M:%S",
    level="INFO"
)


class ConsulWork(object):
    def __init__(self, host, port):
        self.consul = consul.Consul(host, port)

    def set_key_v(self, key, value):
        res = self.consul.kv.put(key, value)
        msg = "[set_key_v_res][key:{}][value:{}][res:{}]".format(

            key,
            value,
            res,

        )
        logging.info(msg)

    def get_key_v(self, key):
        index, data = self.consul.kv.get(key)
        msg = "[get_key_v_res][key:{}][index:{}][data:{}]".format(
            key,
            index,
            data,
        )
        logging.info(msg)
        return index, data

    def block_get_key_v(self, key, index):
        index, data = self.consul.kv.get(key, index=index)
        msg = "[block_get_key_res][key:{}][index:{}][data:{}]".format(
            key,
            index,
            data,
        )
        logging.info(msg)
        return index, data


if __name__ == '__main__':
    c_host = '192.168.150.24'
    c_port = 30007
    c = ConsulWork(c_host, c_port)
    # 简单put get
    c.set_key_v("key_a", "value_a")
    index,data = c.get_key_v("key_a")
    # 使用index 作为参数会阻塞住请求
    # 直到有数据更新或者超时
    c.block_get_key_v("key_a", index)

接受请求

import consul
import asyncio
import logging

logging.basicConfig(
    # TODO console 日志,上线时删掉
    # filename=LOG_PATH,
    format='%(asctime)s %(levelname)s %(filename)s %(funcName)s [line:%(lineno)d]:%(message)s',
    datefmt="%Y-%m-%d %H:%M:%S",
    level="INFO"
)


class ConsulWork(object):
    def __init__(self, host, port):
        self.consul = consul.Consul(host, port)

    def set_key_v(self, key, value):
        res = self.consul.kv.put(key, value)
        msg = "[set_key_v_res][key:{}][value:{}][res:{}]".format(

            key,
            value,
            res,

        )
        logging.info(msg)

    def get_key_v(self, key):
        index, data = self.consul.kv.get(key)
        msg = "[get_key_v_res][key:{}][index:{}][data:{}]".format(
            key,
            index,
            data,
        )
        logging.info(msg)
        return index, data

    def block_get_key_v(self, key, index):
        index, data = self.consul.kv.get(key, index=index)
        msg = "[block_get_key_res][key:{}][index:{}][data:{}]".format(
            key,
            index,
            data,
        )
        logging.info(msg)
        return index, data


if __name__ == '__main__':
    c_host = '192.168.3.50'
    c_port = 8500
    c = ConsulWork(c_host, c_port)
    # 简单put get
    index = None
    key = 'key_a'
    while True:
        index, data = c.block_get_key_v(key, index)
        msg = "[key:{}][value:{}][index:{}]".format(
            key,
            data['Value'],
            index
        )
        # chan <- data

        logging.info(msg)
    # 此时再开一个窗口执行更新 key_a操作,可以看到key_a的变化

向consul注册一个服务

import consul
import asyncio
import logging

logging.basicConfig(
    # TODO console 日志,上线时删掉
    # filename=LOG_PATH,
    format='%(asctime)s %(levelname)s %(filename)s %(funcName)s [line:%(lineno)d]:%(message)s',
    datefmt="%Y-%m-%d %H:%M:%S",
    level="INFO"
)


class ConsulWork(object):
    def __init__(self, host, port):
        self.consul = consul.Consul(host, port)

    def set_key_v(self, key, value):
        res = self.consul.kv.put(key, value)
        msg = "[set_key_v_res][key:{}][value:{}][res:{}]".format(

            key,
            value,
            res,

        )
        logging.info(msg)

    def get_key_v(self, key):
        index, data = self.consul.kv.get(key)
        msg = "[get_key_v_res][key:{}][index:{}][data:{}]".format(
            key,
            index,
            data,
        )
        logging.info(msg)
        return index, data

    def block_get_key_v(self, key, index):
        index, data = self.consul.kv.get(key, index=index)
        msg = "[block_get_key_res][key:{}][index:{}][data:{}]".format(
            key,
            index,
            data,
        )
        logging.info(msg)
        return index, data

    def register_service(self, name, host, port, tags=None):
        tags = tags or []
        # 注册服务
        id = "{}_{}_{}".format(name, host, port)
        return self.consul.agent.service.register(
            name,
            id,
            host,
            port,
            tags,
            # 健康检查ip端口,检查时间:5,超时时间:30,注销时间:30s
            # check=consul.Check().tcp(host, port, "5s", "5s", "60s"))
            check=consul.Check().tcp(host, port, "5s", "5s"))


    def register_service_with_deregister(self, name, host, port, tags=None):
        tags = tags or []
        # 注册服务
        id = "{}_{}_{}".format(name, host, port)
        return self.consul.agent.service.register(
            name,
            id,
            host,
            port,
            tags,
            # 健康检查ip端口,检查时间:5,超时时间:30,注销时间:15s
            # 意思是15秒后 还是检测失败就自动注销掉服务
            check=consul.Check().tcp(host, port, "5s", "5s", "15s"))
            # check=consul.Check().tcp(host, port, "5s", "5s"))



if __name__ == '__main__':
    c_host = '192.168.150.24'
    c_port = 30007
    c = ConsulWork(c_host, c_port)
    res1 = c.register_service("pgw", '192.168.3.50', 9091)
    # res2 = c.register_service_with_deregister("pushgateway", '172.20.70.205', 9991)
    res2 = c.register_service("pgw", '192.168.3.51', 9091)
    print(res1,res2)

watch服务

"""
curl -vvv --request PUT 'http://172.20.70.205:8500/v1/agent/service/deregister/pushgateway_172.20.70.205_9991'
curl -vvv --request PUT 'http://172.20.70.205:8500/v1/agent/service/deregister/pushgateway_172.20.70.205_9091'

"""You have new mail in /var/spool/mail/root
[root@k8s-master1 consel]# cat 005watch服务.py 
import time

import consul
import asyncio
import logging

logging.basicConfig(
    # TODO console 日志,上线时删掉
    # filename=LOG_PATH,
    format='%(asctime)s %(levelname)s %(filename)s %(funcName)s [line:%(lineno)d]:%(message)s',
    datefmt="%Y-%m-%d %H:%M:%S",
    level="INFO"
)


class ConsulWork(object):
    def __init__(self, host, port):
        self.consul = consul.Consul(host, port)

    def set_key_v(self, key, value):
        res = self.consul.kv.put(key, value)
        msg = "[set_key_v_res][key:{}][value:{}][res:{}]".format(

            key,
            value,
            res,

        )
        logging.info(msg)

    def get_key_v(self, key):
        index, data = self.consul.kv.get(key)
        msg = "[get_key_v_res][key:{}][index:{}][data:{}]".format(
            key,
            index,
            data,
        )
        logging.info(msg)
        return index, data

    def block_get_key_v(self, key, index):
        index, data = self.consul.kv.get(key, index=index)
        msg = "[block_get_key_res][key:{}][index:{}][data:{}]".format(
            key,
            index,
            data,
        )
        logging.info(msg)
        return index, data

    def register_service(self, name, host, port, tags=None):
        tags = tags or []
        # 注册服务
        id = "{}_{}_{}".format(name, host, port)
        return self.consul.agent.service.register(
            name,
            id,
            host,
            port,
            tags,
            # 健康检查ip端口,检查时间:5,超时时间:30,注销时间:30s
            # check=consul.Check().tcp(host, port, "5s", "5s", "60s"))
            check=consul.Check().tcp(host, port, "5s", "5s"))

    def register_service_with_deregister(self, name, host, port, tags=None):
        tags = tags or []
        # 注册服务
        id = "{}_{}_{}".format(name, host, port)
        return self.consul.agent.service.register(
            name,
            id,
            host,
            port,
            tags,
            # 健康检查ip端口,检查时间:5,超时时间:30,注销时间:15s
            # 意思是15秒后 还是检测失败就自动注销掉服务
            check=consul.Check().tcp(host, port, "5s", "5s", "15s"))
        # check=consul.Check().tcp(host, port, "5s", "5s"))

    def watch_service(self, service_name):
        index = None
        while True:
            try:
                last_index = index

                index, d = self.consul.health.service(service_name, passing=True, index=index, wait='10s')

                if last_index == index or last_index == None:
                    print('没变')
                    continue
                print("变了")
                data = d
                new_nodes = []
                num = len(data)
                print(data)
                for num_index, x in enumerate(data):
                    Service = x.get("Service")
                    address = Service.get("Address")
                    id = Service.get("ID")
                    msg = "[alive_node_detail][num:{}/{}][addr:{}][id:{}]".format(
                        num_index + 1,
                        num,
                        address,
                        id
                    )
                    print(msg)
                    logging.info(msg)
                    if address:
                        new_nodes.append(address)


            except Exception as e:
                logging.error("[watch_error,service:{},error:{}]".format(service_name, e))
                time.sleep(5)
                continue


if __name__ == '__main__':
    c_host = '192.168.3.50'
    c_port = 8500
    c = ConsulWork(c_host, c_port)
    # c.watch_service('pushgateway')
    c.watch_service('pgw')
    # step 1
    """
    首先能看到正常的两个节点信息
    2021-04-02 19:20:05 INFO 005watch服务.py watch_service [line:99]:[alive_node_detail][num:1/2][addr:172.20.70.205][id:pushgateway_172.20.70.205_9091]
    2021-04-02 19:20:05 INFO 005watch服务.py watch_service [line:99]:[alive_node_detail][num:2/2][addr:172.20.70.215][id:pushgateway_172.20.70.215_9091]
    """
    # step 2
    # 此时到机器上停止一台pushgateway服务
    # systemctl stop  pushgateway
    """
    可以看到 只有一个存活节点
    2021-04-02 19:21:08 INFO 005watch服务.py watch_service [line:99]:[alive_node_detail][num:1/1][addr:172.20.70.215][id:pushgateway_172.20.70.215_9091]
    """
    # step 3
    # 此时到机器上启动pushgateway服务
    # systemctl start  pushgateway
    """
    可以看到 又打印两个节点信息了
    2021-04-02 19:22:08 INFO 005watch服务.py watch_service [line:99]:[alive_node_detail][num:1/2][addr:172.20.70.205][id:pushgateway_172.20.70.205_9091]
    2021-04-02 19:22:08 INFO 005watch服务.py watch_service [line:99]:[alive_node_detail][num:2/2][addr:172.20.70.215][id:pushgateway_172.20.70.215_9091]
    """

    # step 4
    # 过了5分钟发现又打印了一遍两个节点
    # 这是因为consul.health.service函数有个 wait参数代表本地请求的最长时间等待时间,默认为5分钟
    # 所以我们需要根据index 是否变换判断节点到底变没变
    #             *wait* the maximum duration to wait (e.g. '10s') to retrieve
    #             a given index. this parameter is only applied if *index* is also
    #             specified. the wait time by default is 5 minutes.
    """
    2021-04-02 19:48:16 INFO 005watch服务.py watch_service [line:99]:[alive_node_detail][num:1/2][addr:172.20.70.205][id:pushgateway_172.20.70.205_9091]
    2021-04-02 19:48:16 INFO 005watch服务.py watch_service [line:99]:[alive_node_detail][num:2/2][addr:172.20.70.215][id:pushgateway_172.20.70.215_9091]
    2021-04-02 19:53:17 INFO 005watch服务.py watch_service [line:99]:[alive_node_detail][num:1/2][addr:172.20.70.205][id:pushgateway_172.20.70.205_9091]
    2021-04-02 19:53:17 INFO 005watch服务.py watch_service [line:99]:[alive_node_detail][num:2/2][addr:172.20.70.215][id:pushgateway_172.20.70.215_9091]
    """

    """
    没变
    2021-04-02 20:01:25 INFO 005watch服务.py watch_service [line:105]:[alive_node_detail][num:1/2][addr:172.20.70.205][id:pushgateway_172.20.70.205_9091]
    2021-04-02 20:01:25 INFO 005watch服务.py watch_service [line:105]:[alive_node_detail][num:2/2][addr:172.20.70.215][id:pushgateway_172.20.70.215_9091]
    没变
    2021-04-02 20:01:35 INFO 005watch服务.py watch_service [line:105]:[alive_node_detail][num:1/2][addr:172.20.70.205][id:pushgateway_172.20.70.205_9091]
    2021-04-02 20:01:35 INFO 005watch服务.py watch_service [line:105]:[alive_node_detail][num:2/2][addr:172.20.70.215][id:pushgateway_172.20.70.215_9091]
    """

将pushgateway 服务改造成consul 服务发现模式

  - job_name: 'pushgateway'
    honor_timestamps: true
    scrape_interval: 15s
    scrape_timeout: 10s
    metrics_path: /metrics
    scheme: http
    consul_sd_configs:
      - server: 172.20.70.205:8500
        services:
          - pushgateway
    relabel_configs:
      - source_labels:  ["__meta_consul_dc"]
        target_label: "dc"

基于文件的服务发现配置

之前配置了很多个traget

  • redis
  • mysql
  • grafana
  • pushgateway

文档地址

  • https://prometheus.io/docs/prometheus/latest/configuration/configuration/#file_sd_config

特点

  • 基于文件的服务发现提供了一种配置静态目标的更通用的方法
  • 并充当了插入自定义服务发现机制的接口。
  • 摆脱对特定服务发现源的依赖
  • 只要能正确给出 json/yaml文件即可
  • 和服务树的最好匹配方案

文件类型

  • yaml
YAML yaml - targets: [ - '<host>' ] labels: [ <labelname>: <labelvalue> ... ]

  • json
json [ { "targets": [ "<host>", ... ], "labels": { "<labelname>": "<labelvalue>", ... } }, ... ]

文件路径 支持通配符
文件刷新间隔 refresh_interval
prometheus配置样例

  - job_name: 'ECS'
    scrape_interval: 30s
    scrape_timeout: 10s
    metrics_path: /metrics
    scheme: http
    honor_timestamps: false
    file_sd_configs:
    - files:
      - /App/prometheus/sd/file_sd_by_prome_shared.json
      refresh_interval: 5m

json文件样例

[
  {
    "targets": [
      "172.20.70.205:9100"
    ],
    "labels": {
      "name": "prometheus-storage-01",
      "account": "aliyun-01",
      "region": "ap-south-1",
      "env": "prod",
      "group": "inf",
      "project": "monitor",
      "stree_gpa": "inf.monitor.prometheus"
    }
  },
  {
    "targets": [
      "172.20.70.205:9091",
      "172.20.70.215:9091"
    ],
    "labels": {
      "account": "aliyun-01",
      "region": "ap-south-2",
      "env": "prod",
      "group": "inf",
      "project": "middleware",
      "stree_gpa": "inf.middleware.kafka"
    }
  }
]

下面来解读一下

  • targets 是一组实例地址的列表
  • labels 是这组实例的标签,应用到列表中所有实例
  • 如果想每个实例不同的标签,可以将targets列表保留一个实例即可
  • 标签可以自定义,下面举几个例子
    • account 代表公有云账户,多账户情况
    • region 代表区域
    • env 代表所属环境 prod代表生产,pre代表预发,test代表测试
    • group代表业务大组
    • project 代表项目
    • stree_gpa 代表服务树三级标签
  • 那么prometheus在采集对应target时就会将对应标签打入其metrics中
  • 为后续我们按照标签过滤提供方便

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值