Kubernetes平台监控方案之:Exporters+Prometheus+Grafana

1. 概述

1.1 总体目标

从监控平台本身的业务需求来看,至少应该通过平台获取到以下的监控数据:

  • 性能指标(如:CPU、Memory、Load、磁盘、网络等)

    • 容器、Pod相关的性能指标数据
    • 主机节点相关的性能指标数据
    • 容器内进程自己主动暴露的指标数据
    • k8s上应用的网络性能,如http、tcp等数据
  • 状态指标

    • k8s资源对象(Deployment、Daemonset、Pod等)的运行状态指标
    • k8s平台组件(如kube-apiserver、kube-scheduler等)的运行状态指标

获取监控数据之后,还需要对监控进行可视化展示,以及对监控中出现的异常情况进行告警。

本文旨在实践一整套监控方案,篇幅限制,不会对Prometheus等组件的基础原理做具体介绍。Prometheus的基本原理可以参考此书:prometheus-book

1.2 主流监控方案

目前对于kubernetes的主流监控方案主要有以下几种:

  • Heapster+InfluxDB+Grafana
    每个K8S节点的Kubelet内含cAdvisor,暴露出API,Heapster通过访问这些端点得到容器监控数据。它支持多种储存方式,常用的是InfluxDB。这套方案的缺点是数据来源单一、缺乏报警功能以及InfluxDB的单点问题,而且Heapster也已经在新版本中被deprecated(被metrics server取代)了。这种实现方案的详细介绍请见这篇文章。
  • Metrics-Server+InfluxDB+Grafana
    k8s从1.8版本开始,CPU、内存等资源的metrics信息可以通过 Metrics API来获取,用户还可以通过kubectl top直接获取这些metrics信息。Metrics API需要部署Metrics-Server。
  • 各种Exporter+Prometheus+Grafana
    通过各种export采集不同维度的监控指标,并通过Prometheus支持的数据格式暴露出来,Prometheus定期pull数据并用Grafana展示,异常情况使用AlertManager告警。本方案下文详细叙述。

2. 架构

2.1 实现思路

总体实现思路如下:

  • 采集
    • 通过cadvisor采集容器、Pod相关的性能指标数据,并通过暴露的/metrics接口用prometheus抓取
    • 通过prometheus-node-exporter采集主机的性能指标数据,并通过暴露的/metrics接口用prometheus抓取
    • 应用侧自己采集容器中进程主动暴露的指标数据(暴露指标的功能由应用自己实现,并添加平台侧约定的annotation,平台侧负责根据annotation实现通过Prometheus的抓取)
    • 通过blackbox-exporter采集应用的网络性能(http、tcp、icmp等)数据,并通过暴露的/metrics接口用prometheus抓取
    • 通过kube-state-metrics采集k8s资源对象的状态指标数据,并通过暴露的/metrics接口用prometheus抓取
    • 通过etcd、kubelet、kube-apiserver、kube-controller-manager、kube-scheduler自身暴露的/metrics获取节点上与k8s集群相关的一些特征指标数据。
  • 存储(汇聚),通过prometheus pull并汇聚各种exporter的监控数据
  • 展示,通过grafana展示监控信息
  • 告警,通过alertmanager进行告警

2.2 总体架构图

总体架构如下图:
Kubernetes平台监控方案之:Exporters+Prometheus+Grafana_第1张图片

3.监控指标采集实现

3.1 容器、Pod相关的性能指标数据—cAdvisor

cAdvisor是谷歌开源的一个容器监控工具,cadvisor采集了主机上容器相关的性能指标数据,通过容器的指标还可进一步计算出pod的指标。

cadvisor提供的一些主要指标有:

container_cpu_*	
container_fs_*	
container_memory_*	
container_network_*	
container_spec_*(cpu/memory)		
container_start_time_*	
container_tasks_state_*

可以看到基本都是容器相关的一些资源使用情况。

3.1.1 cadvisor接口

目前cAdvisor集成到了kubelet组件内,可以在kubernetes集群中每个启动了kubelet的节点使用cAdvisor提供的metrics接口获取该节点所有容器相关的性能指标数据。1.7.3版本以前,cadvisor的metrics数据集成在kubelet的metrics中,在1.7.3以后版本中cadvisor的metrics被从kubelet的metrics独立出来了,在prometheus采集的时候变成两个scrape的job。

cAdvisor对外提供服务的默认端口为***4194***,主要提供两种接口:

  • Prometheus格式指标接口:nodeIP:4194/metrics(或者通过kubelet暴露的cadvisor接口nodeIP:10255/metrics/cadvisor);
  • WebUI界面接口:nodeIP:4194/containers/

Prometheus作为一个时间序列数据收集,处理,存储的服务,能够监控的对象必须通过http api暴露出基于Prometheus认可的数据模型的监控数据,cAdvisor接口(nodeIP:4194/metrics)暴露的监控指标数据如下所示:

# HELP cadvisor_version_info A metric with a constant '1' value labeled by kernel version, OS version, docker version, cadvisor version & cadvisor revision.
# TYPE cadvisor_version_info gauge
cadvisor_version_info{cadvisorRevision="",cadvisorVersion="",dockerVersion="1.12.6",kernelVersion="4.9.0-1.2.el7.bclinux.x86_64",osVersion="CentOS Linux 7 (Core)"} 1
# HELP container_cpu_cfs_periods_total Number of elapsed enforcement period intervals.
# TYPE container_cpu_cfs_periods_total counter
container_cpu_cfs_periods_total{container_name="",id="/kubepods/burstable/pod1b0c1f83322defae700f33b1b8b7f572",image="",name="",namespace="",pod_name=""} 7.062239e+06
container_cpu_cfs_periods_total{container_name="",id="/kubepods/burstable/pod7f86ba308f28df9915b802bc48cfee3a",image="",name="",namespace="",pod_name=""} 1.574206e+06
container_cpu_cfs_periods_total{container_name="",id="/kubepods/burstable/podb0c8f695146fe62856bc23709a3e056b",image="",name="",namespace="",pod_name=""} 7.107043e+06
container_cpu_cfs_periods_total{container_name="",id="/kubepods/burstable/podc8cf73836b3caba7bf952ce1ac5a5934",image="",name="",namespace="",pod_name=""} 5.932159e+06
container_cpu_cfs_periods_total{container_name="",id="/kubepods/burstable/podfaa9db59-64b7-11e8-8792-00505694eb6a",image="",name="",namespace="",pod_name=""} 6.979547e+06
container_cpu_cfs_periods_total{container_name="calico-node",id="/kubepods/burstable/podfaa9db59-64b7-11e8-8792-
...

可以看到以上接口的数据是按prometheus的格式输出的。

3.1.2 Prometheus配置

如下配置Prometheus来定期拉取cAdvisor的metrics:

      - job_name: 'cadvisor'
        # 通过https访问apiserver,通过apiserver的api获取数据
        scheme: https
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
              
        #以k8s的角色(role)来定义收集,比如node,service,pod,endpoints,ingress等等 
        kubernetes_sd_configs:
        # 从k8s的node对象获取数据
        - role: node
      
        relabel_configs:
        # 用新的前缀代替原label name前缀,没有replacement的话功能就是去掉label name前缀
        # 例如:以下两句的功能就是将__meta_kubernetes_node_label_kubernetes_io_hostname
        # 变为kubernetes_io_hostname
        - action: labelmap
          regex: __meta_kubernetes_node_label_(.+)
          
        # replacement中的值将会覆盖target_label中指定的label name的值,
        # 即__address__的值会被替换为kubernetes.default.svc:443
        - target_label: __address__
          replacement: kubernetes.default.svc:443
          
        # 获取__meta_kubernetes_node_name的值
        - source_labels: [__meta_kubernetes_node_name]
          #匹配一个或多个任意字符,将上述source_labels的值生成变量
          regex: (.+)
          # replacement中的值将会覆盖target_label中指定的label name的值,
          # 即__metrics_path__的值会被替换为/api/v1/nodes/${1}/proxy/metrics,
          # 其中${1}的值会被替换为__meta_kubernetes_node_name的值
          target_label: __metrics_path__
          replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
          
        metric_relabel_configs:
          - action: replace
            source_labels: [id]
            regex: '^/machine\.slice/machine-rkt\\x2d([^\\]+)\\.+/([^/]+)\.service$'
            target_label: rkt_container_name
            replacement: '${2}-${1}'
          - action: replace
            source_labels: [id]
            regex: '^/system\.slice/(.+)\.service$'
            target_label: systemd_service_name
            replacement: '${1}'

之后,在prometheus的target(IP:Port/targets)中可以看到cadvisor相应的target:

备注

关于配置文件的一些说明:

  1. 以上配置遵照官方example配置,通过apiserver提供的api做代理获取cAdvisor( https://kubernetes.default.svc:443/api/v1/nodes/k8smaster01/proxy/metrics/cadvisor )的监控指标(和从nodeIP:4194/metrics获取到的内容是一样的),而不是直接从node上获取。为什么这样做,官方这样解释的:This means it will work if Prometheus is running out of cluster, or can’t connect to nodes for some other reason (e.g. because of firewalling)。
  2. Promethues在K8S集群内通过DNS地址 https://kubernetes.default.svc访问apiserver来scrape数据
  3. Prometheus配置文件的语法规则较复杂,为便于理解,我加了一些注释;更多语法规则请见Prometheus官方文档。

关于target中label的一些说明,target中必有的几个source label有:

  1. __address__(当static_configs时通过targets手工配置,当kubernetes_sd_configs时,值从apiserver中获取)、
  2. __metrics_path__(默认值是/metrics)、
  3. __scheme__(默认值http)
  4. job

其他source label则是根据kubernetes_sd_configs时设置的- role(如endpoints、nodes、service、pod等)从k8s资源对象的label、annotation及其他一些信息中提取的。

3.2 主机节点性能指标数据—node-exporter

Prometheus社区提供的NodeExporter项目可以对主机的关键度量指标进行监控,通过Kubernetes的DeamonSet可以在各个主机节点上部署有且仅有一个NodeExporter实例,实现对主机性能指标数据的监控。node-exporter所采集的指标主要有:

 node_cpu_*		
 node_disk_*	
 node_entropy_*		
 node_filefd_*
 node_filesystem_*	
 node_forks_*	
 node_intr_total_*
 node_ipvs_*	
 node_load_*	
 node_memory_*		
 node_netstat_*		
 node_network_*		
 node_nf_conntrack_*		
 node_scrape_*		
 node_sockstat_*		
 node_time_seconds_*
 node_timex	_*
 node_xfs_*

可以看到全是节点相关的一些资源使用情况。

3.2.1 prometheus-node-exporter部署

我的NodeExporter部署文件node-exporter-daemonset.yaml如下,可从github下载。

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: prometheus-node-exporter
  namespace: kube-system
  labels:
    app: prometheus-node-exporter
spec:
  template:
    metadata:
      name: prometheus-node-exporter
      labels:
        app: prometheus-node-exporter
    spec:
      containers:
      - image: prom/node-exporter:v0.16.0
        imagePullPolicy: IfNotPresent
        name: prometheus-node-exporter
        ports:
        - name: prom-node-exp
          #^ must be an IANA_SVC_NAME (at most 15 characters, ..)
          containerPort: 9100
          hostPort: 9100
      tolerations:
      - key: "node-role.kubernetes.io/master"
        effect: "NoSchedule"
      hostNetwork: true
      hostPID: true
      hostIPC: true
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/scrape: 'true'
    prometheus.io/app-metrics: 'true'
    prometheus.io/app-metrics-path: '/metrics'
  name: prometheus-node-exporter
  namespace: kube-system
  labels:
    app: prometheus-node-exporter
spec:
  clusterIP: None
  ports:
    - name: prometheus-node-exporter
      port: 9100
      protocol: TCP
  selector:
    app: prometheus-node-exporter
  type: ClusterIP

备注:

1.为了让容器里的node-exporter获取到主机上的网络、PID、IPC指标,这里设置了hostNetwork: true、hostPID: true、hostIPC: true,来与主机共用网络、PID、IPC这三个namespace。
2.为了
2.此处在Service的annotations中定义标注prometheus.io/scrape: 'true',表明该Service需要被Promethues发现并采集数据。

通过NodeExporter暴露的metrics接口(nodeIP:9100/metrics)查看采集到的数据,可以看到是按Prometheus的格式输出的数据:

# HELP node_arp_entries ARP entries by device
# TYPE node_arp_entries gauge
node_arp_entries{device="calid63983a5754"} 1
node_arp_entries{device="calid67ce395c9e"} 1
node_arp_entries{device="calid857f2bf9d5"} 1
node_arp_entries{device="calief3a4b64165"} 1
node_arp_entries{device="eno16777984"} 9
# HELP node_boot_time Node boot time, in unixtime.
# TYPE node_boot_time gauge
node_boot_time 1.527752719e+09
# HELP node_context_switches Total number of context switches.
# TYPE node_context_switches counter
node_context_switches 3.1425612674e+10
# HELP node_cpu Seconds the cpus spent in each mode.
# TYPE node_cpu counter
node_cpu{cpu="cpu0",mode="guest"} 0
node_cpu{cpu="cpu0",mode="guest_nice"} 0
node_cpu{cpu="cpu0",mode="idle"} 2.38051096e+06
node_cpu{cpu="cpu0",mode="iowait"} 11904.19
node_cpu{cpu="cpu0",mode="irq"} 0
node_cpu{cpu="cpu0",mode="nice"} 2990.94
node_cpu{cpu="cpu0",mode="softirq"} 8038.3
...

3.2.2 Prometheus配置

配置Prometheus来scrape node-exporter的metrics:

      - job_name: 'prometheus-node-exporter'
        
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        kubernetes_sd_configs:
        #The endpoints role discovers targets from listed endpoints of a service. For each
        #endpoint address one target is discovered per port. If the endpoint is backed by
        #a pod, all additional container ports of the pod, not bound to an endpoint port,
        #are discovered as targets as well
        - role: endpoints
        relabel_configs:
        # 只保留endpoints的annotations中含有prometheus.io/scrape: 'true'和port的name为prometheus-node-exporter的endpoint
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape, __meta_kubernetes_endpoint_port_name]
          regex: true;prometheus-node-exporter
          action: keep
        # Match regex against the concatenated source_labels. Then, set target_label to replacement, 
        # with match group references (${1}, ${2}, ...) in replacement substituted by their value. 
        # If regex does not match, no replacement takes place.
        - 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
        # 去掉label name中的前缀__meta_kubernetes_service_label_
        - action: labelmap
          regex: __meta_kubernetes_service_label_(.+)
        # 将__meta_kubernetes_namespace重命名为kubernetes_namespace
        - source_labels: [__meta_kubernetes_namespace]
          action: replace
          target_label: kubernetes_namespace
        # 将__meta_kubernetes_service_name重命名为kubernetes_name
        - source_labels: [__meta_kubernetes_service_name]
          action: replace
          target_label: kubernetes_name

在Prometheus中可以看到相应的target:

3.3 采集应用实例中某个进程自己暴露的指标数据

有的应用具有暴露容器内具体进程性能指标的需求,这些指标由应用侧实现采集并暴露,平台侧做汇聚。

3.3.1 如何标识哪些是主动暴露监控指标的应用并获取指标

平台侧可以约定好带哪些annotation前缀的服务是自主暴露监控指标的服务。应用添加平台侧约定的这些annotations,平台侧可以根据这些annotations实现Prometheus的scrape。

例如,应用侧为自己的服务添加如下平台侧约定约定的annotation:

prometheus.io/scrape: 'true'
prometheus.io/app-metrics: 'true'
prometheus.io/app-metrics-port: '8080'
prometheus.io/app-metrics-path: '/metrics'

Prometheus可以:

  • 根据prometheus.io/scrape: 'true'获知对应的endpoint是需要被scrape的
  • 根据prometheus.io/app-metrics: 'true'获知对应的endpoint中有应用进程暴露的metrics
  • 根据prometheus.io/app-metrics-port: '8080'获知进程暴露的metrics的端口号
  • 根据prometheus.io/app-metrics-path: '/metrics'获知进程暴露的metrics的具体路径

3.3.2 如何给应用加一些标志信息,并带到Prometheus侧

可能还需要根据平台和业务的需求添加其他一些以prometheus.io/app-info-为前缀的annotation,Prometheus截取下前缀,保留后半部分做key,连同value保留下来。这样满足在平台对应用做其他一些标识的需求。比如加入如下annotation来标识应用所属的的环境、租户以及应用名称:

prometheus.io/app-info-env: 'test'
prometheus.io/app-info-tenant: 'test-tenant'
prometheus.io/app-info-name: 'test-app'

Prometheus的config配置如下:

      - job_name: 'kubernetes-app-metrics'
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        kubernetes_sd_configs:
        #The endpoints role discovers targets from listed endpoints of a service. For each
        #endpoint address one target is discovered per port. If the endpoint is backed by
        #a pod, all additional container ports of the pod, not bound to an endpoint port,
        #are discovered as targets as well
        - role: endpoints
        relabel_configs:
        # 只保留endpoint中含有prometheus.io/scrape: 'true'的annotation的endpoint
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape, __meta_kubernetes_service_annotation_prometheus_io_app_metrics]
          regex: true;true
          action: keep
        # 将用户指定的进程的metrics_path替换默认的metrics_path
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_app_metrics_path]
          action: replace
          target_label: __metrics_path__
          regex: (.+)
        # 用pod_ip和用户指定的进程的metrics端口组合成真正的可以拿到数据的地址来替换原始__address__
        - source_labels: [__meta_kubernetes_pod_ip, __meta_kubernetes_service_annotation_prometheus_io_app_metrics_port]
          action: replace
          target_label: __address__
          regex: (.+);(.+)
          replacement: $1:$2
        # 去掉label name中的前缀__meta_kubernetes_service_annotation_prometheus_io_app_info_
        - action: labelmap
          regex: __meta_kubernetes_service_annotation_prometheus_io_app_info_(.+)

备注:

最后两行的作用是将例如prometheus.io/app-info-tenant的annotation名切割成名为tenant的label。

在Prometheus中可以获取到对应的应用进程targets:

3.4 通过blackbox-exporter采集应用的网络性能数据

blackbox-exporter是一个黑盒探测工具,可以对服务的http、tcp、icmp等进行网络探测。

3.4.1 blackbox-exporter部署

我的blackbox-exporter部署文件如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: prometheus-blackbox-exporter
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: prometheus-blackbox-exporter
  replicas: 1
  template:
    metadata:
      labels:
        app: prometheus-blackbox-exporter
    spec:
      restartPolicy: Always
      containers:
      - name: prometheus-blackbox-exporter
        image: prom/blackbox-exporter:v0.12.0
        imagePullPolicy: IfNotPresent
        ports:
        - name: blackbox-port
          containerPort: 9115
        readinessProbe:
          tcpSocket:
            port: 9115
          initialDelaySeconds: 5
          timeoutSeconds: 5
        resources:
          requests:
            memory: 50Mi
            cpu: 100m
          limits:
            memory: 60Mi
            cpu: 200m
        volumeMounts:
        - name: config
          mountPath: /etc/blackbox_exporter
        args:
        - --config.file=/etc/blackbox_exporter/blackbox.yml
        - --log.level=debug
        - --web.listen-address=:9115
      volumes:
      - name: config
        configMap:
          name: prometheus-blackbox-exporter
      nodeSelector:
        node-role.kubernetes.io/master: "true"
      tolerations:
      - key: "node-role.kubernetes.io/master"
        effect: "NoSchedule"
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: prometheus-blackbox-exporter
  name: prometheus-blackbox-exporter
  namespace: kube-system
  annotations:
    prometheus.io/scrape: 'true'
spec:
  type: NodePort
  selector:
    app: prometheus-blackbox-exporter
  ports:
  - name: blackbox
    port: 9115
    targetPort: 9115
    nodePort: 30009
    protocol: TCP

对应的configmap如下:

apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    app: prometheus-blackbox-exporter
  name: prometheus-blackbox-exporter
  namespace: kube-system
data:
  blackbox.yml: |-
    modules:
      http_2xx:
        prober: http
        timeout: 10s
        http:
          valid_http_versions: ["HTTP/1.1", "HTTP/2"]
          valid_status_codes: []
          method: GET
          preferred_ip_protocol: "ip4"
      http_post_2xx: # http post 监测模块
        prober: http
        timeout: 10s
        http:
          valid_http_versions: ["HTTP/1.1", "HTTP/2"]
          method: POST
          preferred_ip_protocol: "ip4"
      tcp_connect:
        prober: tcp
        timeout: 10s
      icmp:
        prober: icmp
        timeout: 10s
        icmp:
          preferred_ip_protocol: "ip4"

备注:

blackbox-exporter的配置文件为/etc/blackbox_exporter/blackbox.yml,可以运行时动态的重新加载配置文件,当重新加载配置文件失败时,不影响在运行的配置。重载方式:curl -XPOST http://IP:9115/-/reload

3.4.2 Prometheus配置

在Prometheus的config文件中分别配置对http和tcp的探测:

      - job_name: 'kubernetes-service-http-probe'
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        kubernetes_sd_configs:
        - role: service
        # 将metrics_path由默认的/metrics改为/probe
        metrics_path: /probe
        # Optional HTTP URL parameters.
        # 生成__param_module="http_2xx"的label
        params:
          module: [http_2xx]
        relabel_configs:
        # 只保留含有label为prometheus/io=scrape的service
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape, __meta_kubernetes_service_annotation_prometheus_io_http_probe]
          regex: true;true
          action: keep
        - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_namespace, __meta_kubernetes_service_annotation_prometheus_io_http_probe_port, __meta_kubernetes_service_annotation_prometheus_io_http_probe_path]
          action: replace
          target_label: __param_target
          regex: (.+);(.+);(.+);(.+)
          replacement: $1.$2:$3$4
        # 用__address__这个label的值创建一个名为__param_target的label为blackbox-exporter,值为内部service的访问地址,作为blackbox-exporter采集用
        #- source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_http_probe_path]
        #  action: replace
        #  target_label: __param_target
        #  regex: (.+);(.+)
        #  replacement: $1$2
        # 用blackbox-exporter的service地址值”prometheus-blackbox-exporter:9115"替换原__address__的值
        - target_label: __address__
          replacement: prometheus-blackbox-exporter:9115
        - source_labels: [__param_target]
          target_label: instance
        # 去掉label name中的前缀__meta_kubernetes_service_annotation_prometheus_io_app_info_
        - action: labelmap
          regex: __meta_kubernetes_service_annotation_prometheus_io_app_info_(.+)
        #- source_labels: [__meta_kubernetes_namespace]
        #  target_label: kubernetes_namespace
        #- source_labels: [__meta_kubernetes_service_name]
        #  target_label: kubernetes_name
      ## kubernetes-services and kubernetes-ingresses are blackbox_exporter related
      
      # Example scrape config for probing services via the Blackbox Exporter.
      # 
      # The relabeling allows the actual service scrape endpoint to be configured
      # for all or only some services.
      - job_name: 'kubernetes-service-tcp-probe'
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        kubernetes_sd_configs:
        - role: service
        # 将metrics_path由默认的/metrics改为/probe
        metrics_path: /probe
        # Optional HTTP URL parameters.
        # 生成__param_module="tcp_connect"的label
        params:
          module: [tcp_connect]
        relabel_configs:
        # 只保留含有label为prometheus/io=scrape的service
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape, __meta_kubernetes_service_annotation_prometheus_io_tcp_probe]
          regex: true;true
          action: keep
        - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_namespace, __meta_kubernetes_service_annotation_prometheus_io_tcp_probe_port]
          action: replace
          target_label: __param_target
          regex: (.+);(.+);(.+)
          replacement: $1.$2:$3
        # 用__address__这个label的值创建一个名为__param_target的label为blackbox-exporter,值为内部service的访问地址,作为blackbox-exporter采集用
        #- source_labels: [__address__]
        #  target_label: __param_target
        # 用blackbox-exporter的service地址值”prometheus-blackbox-exporter:9115"替换原__address__的值
        - target_label: __address__
          replacement: prometheus-blackbox-exporter:9115
        - source_labels: [__param_target]
          target_label: instance
        # 去掉label name中的前缀__meta_kubernetes_service_annotation_prometheus_io_app_info_
        - action: labelmap
          regex: __meta_kubernetes_service_annotation_prometheus_io_app_info_(.+)

3.4.3 应用侧配置

应用可以在service中指定平台侧约定的annotation,实现监控平台对该应用的网络服务进行探测:

  • http探测
   prometheus.io/scrape: 'true'
   prometheus.io/http-probe: 'true'
   prometheus.io/http-probe-port: '8080'
   prometheus.io/http-probe-path: '/healthz'
  • tcp探测
   prometheus.io/scrape: 'true'
   prometheus.io/tcp-probe: 'true'
   prometheus.io/tcp-probe-port: '80'

Prometheus根据这些annotation可以获知相应service是需要被探测的,探测的具体网络协议是http还是tcp或其他,以及具体的探测端口。http探测的话还要知道探测的具体url。

在Prometheus中可以获取到对应的targets:

3.5 资源对象(Deployment、Pod等)的状态—kube-state-metrics

kube-state-metrics采集了k8s中各种资源对象的状态信息:

kube_daemonset_*(创建时间、所处的阶段、期望跑在几台节点上、应当正在运行的节点数量、不应该跑却跑了daemon pod的节点数、跑好了pod(ready)的节点数)	
kube_deployment_*(创建时间、是否将k8s的label转化为prometheus的label、所处的阶段、是不是以处于paused状态并不再被dp controller处理、期望副本数、rolling update时最多不可用副本数、dp controller观察到的阶段、实际副本数、availabel副本数、unavailabel副本数、updated副本数)
kube_job_*(是否执行完成、创建时间戳...)
kube_namespace_*
kube_node_*
kube_persistentvolumeclaim_*
kube_pod_container_*
kube_pod_*
kube_replicaset_*
kube_service_*
kube_statefulset_*

查看数据(IP:Port/metrics),可以看到是以prometheus的格式输出:

3.5.1 kube-state-metrics部署

我的kube-state-metrics部署文件kube-state-metrics-deployment.yaml如下,可以直接从github下载。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: kube-state-metrics
  namespace: kube-system
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: kube-state-metrics
  namespace: kube-system
  labels:
    app: kube-state-metrics
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: kube-state-metrics
    spec:
      serviceAccountName: kube-state-metrics
      containers:
      - name: kube-state-metrics
        image: daocloud.io/liukuan73/kube-state-metrics:v1.1.0
        ports:
        - containerPort: 8080
      restartPolicy: Always
      nodeSelector:
        node-role.kubernetes.io/master: "true"
      tolerations:
      - key: "node-role.kubernetes.io/master"
        effect: "NoSchedule"
---
apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/scrape: 'true'
    prometheus.io/http-probe: 'true'
    prometheus.io/http-probe-path: '/healthz'
    prometheus.io/http-probe-port: '8080'
  name: kube-state-metrics
  namespace: kube-system
  labels:
    app: kube-state-metrics
spec:
  type: NodePort
  ports:
  - name: kube-state-metrics
    port: 8080
    targetPort: 8080
    nodePort: 30005
  selector:
    app: kube-state-metrics

3.5.2 Prometheus配置

配置Prometheus来scrape来自kube-state-metrics的metrics:

      - job_name: 'kube-state-metrics'
        
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        kubernetes_sd_configs:

        #The endpoints role discovers targets from listed endpoints of a service. For each
        #endpoint address one target is discovered per port. If the endpoint is backed by
        #a pod, all additional container ports of the pod, not bound to an endpoint port,
        #are discovered as targets as well
        - role: endpoints
        relabel_configs:
        # 只保留endpoint中的annotations含有prometheus.io/scrape: 'true'和port的name为prometheus-node-exporter的endpoint
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape,__meta_kubernetes_endpoint_port_name]
          regex: true;kube-state-metrics
          action: keep
        - 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
        # 去掉label name中的前缀__meta_kubernetes_service_label_
        - action: labelmap
          regex: __meta_kubernetes_service_label_(.+)
        # 将__meta_kubernetes_namespace重命名为kubernetes_namespace
        - source_labels: [__meta_kubernetes_namespace]
          action: replace
          target_label: kubernetes_namespace
        # 将__meta_kubernetes_service_name重命名为kubernetes_name
        - source_labels: [__meta_kubernetes_service_name]
          action: replace
          target_label: kubernetes_name

在prometheus中可以看到相应的target:

3.6 k8s集群组件的状态指标采集

etcd、kube-controller-manager、kube-scheduler、kube-proxy、kube-apiserver、kubelet这几个k8d平台组件分别向外暴露了prometheus标准的指标接口/metrics。可通过配置prometheus来进行读取。

3.6.1 etcd指标获取

以kubeadm启动的k8s集群中,etcd是以static pod的形式启动的,默认没有service及对应的endpoint可供集群内的prometheus访问。所以首先创建一个用来为prometheus提供接口的service(endpoint),etcd-svc.yaml文件如下:

apiVersion: v1
kind: Service
metadata:
  namespace: kube-system
  name: etcd-prometheus-discovery
  labels:
    component: etcd
  annotations:
    prometheus.io/scrape: 'true'
spec:
  selector:
    component: etcd
  type: ClusterIP
  clusterIP: None
  ports:
  - name: http-metrics
    port: 2379
    targetPort: 2379
    protocol: TCP

prometheus配置抓取的文件加入如下配置:

      - job_name: 'etcd'

        # 通过https访问apiserver
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

        #以k8s的角色(role)来定义收集,比如node,service,pod,endpoints,ingress等等
        kubernetes_sd_configs:
        # 从endpoints获取apiserver数据
        - role: endpoints

        #relabel_configs允许在抓取之前对任何目标及其标签进行修改。
        relabel_configs:
        # 选择哪些label
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape, __meta_kubernetes_namespace, __meta_kubernetes_service_name]
          # 上述选择的label的值需要与下述对应
          regex: true;kube-system;etcd-prometheus-discovery
          # 含有符合regex的source_label的endpoints进行保留
          action: keep

3.6.2 kube-proxy指标获取

kube-proxy通过10249端口暴露/metrics指标。与3.6.1同理,kube-proxy-svc.yaml如下:

apiVersion: v1
kind: Service
metadata:
  namespace: kube-system
  name: kube-proxy-prometheus-discovery
  labels:
    k8s-app: kube-proxy
  annotations:
    prometheus.io/scrape: 'true'
spec:
  selector:
    k8s-app: kube-proxy
  type: ClusterIP
  clusterIP: None
  ports:
  - name: http-metrics
    port: 10249
    targetPort: 10249
    protocol: TCP

prometheus配置抓取的文件加入如下配置:

        - job_name: 'kube-proxy'

        # 通过https访问apiserver
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

        #以k8s的角色(role)来定义收集,比如node,service,pod,endpoints,ingress等等
        kubernetes_sd_configs:
        # 从endpoints获取apiserver数据
        - role: endpoints

        #relabel_configs允许在抓取之前对任何目标及其标签进行修改。
        relabel_configs:
        # 选择哪些label
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape, __meta_kubernetes_namespace, __meta_kubernetes_service_name]
          # 上述选择的label的值需要与下述对应
          regex: true;kube-system;kube-proxy-prometheus-discovery
          # 含有符合regex的source_label的endpoints进行保留
          action: keep

3.6.3 kube-scheduler指标获取

kube-scheduler通过10251端口暴露/metrics指标。与3.6.1同理,kube-scheduler-svc.yaml如下:

apiVersion: v1
kind: Service
metadata:
  namespace: kube-system
  name: kube-scheduler-prometheus-discovery
  labels:
    k8s-app: kube-scheduler
  annotations:
    prometheus.io/scrape: 'true'
spec:
  selector:
    component: kube-scheduler
  type: ClusterIP
  clusterIP: None
  ports:
  - name: http-metrics
    port: 10251
    targetPort: 10251
    protocol: TCP

prometheus对应的配置如下:

      - job_name: 'kube-scheduler'

        # 通过https访问apiserver
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

        #以k8s的角色(role)来定义收集,比如node,service,pod,endpoints,ingress等等
        kubernetes_sd_configs:
        # 从endpoints获取apiserver数据
        - role: endpoints

        #relabel_configs允许在抓取之前对任何目标及其标签进行修改。
        relabel_configs:
        # 选择哪些label
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape, __meta_kubernetes_namespace, __meta_kubernetes_service_name]
          # 上述选择的label的值需要与下述对应
          regex: true;kube-system;kube-scheduler-prometheus-discovery
          # 含有符合regex的source_label的endpoints进行保留
          action: keep

3.6.4 kube-controller-manager指标获取

kube-controller-manager通过10252端口暴露/metrics指标。与3.6.1同理,kube-controller-manager-svc.yaml如下:

apiVersion: v1
kind: Service
metadata:
  namespace: kube-system
  name: kube-controller-manager-prometheus-discovery
  labels:
    k8s-app: kube-controller-manager
  annotations:
    prometheus.io/scrape: 'true'
spec:
  selector:
    component: kube-controller-manager
  type: ClusterIP
  clusterIP: None
  ports:
  - name: http-metrics
    port: 10252
    targetPort: 10252
    protocol: TCP

prometheus配置抓取的文件加入如下配置:

      - job_name: 'kube-controller-manager'

        # 通过https访问apiserver
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

        #以k8s的角色(role)来定义收集,比如node,service,pod,endpoints,ingress等等
        kubernetes_sd_configs:
        # 从endpoints获取apiserver数据
        - role: endpoints

        #relabel_configs允许在抓取之前对任何目标及其标签进行修改。
        relabel_configs:
        # 选择哪些label
        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape, __meta_kubernetes_namespace, __meta_kubernetes_service_name]
          # 上述选择的label的值需要与下述对应
          regex: true;kube-system;kube-controller-manager-prometheus-discovery
          # 含有符合regex的source_label的endpoints进行保留
          action: keep

以上四步配置好后可以看到分别增加了etcd-prometheus-discovery、kube-controller-manager-prometheus-discovery、kube-scheduler-prometheus-discovery和kube-proxy四个endpints:

prometheus中也可以看到抓取到了相应target:

3.6.5 kube-apiserver数据获取

kube-apiserver与上面四个组件不同的是,部署好后集群中默认会有一个名为kubernetes的service和对应的名为kubernetes的endpoint,这个endpoint就是集群内的kube-apiserver的访问入口。可以如下配置prometheus抓取数据:

      - job_name: 'kube-apiservers'

        # 通过https访问apiserver
        scheme: https
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

        #以k8s的角色(role)来定义收集,比如node,service,pod,endpoints,ingress等等
        kubernetes_sd_configs:
        # 从endpoints获取apiserver数据
        - role: endpoints

        #relabel_configs允许在抓取之前对任何目标及其标签进行修改。
        relabel_configs:
        # 选择哪些label
        - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
          # 上述选择的label的值需要与下述对应
          regex: default;kubernetes;https
          # 含有符合regex的source_label的endpoints进行保留
          action: keep

之后就可以看到prometheus多了一个kube-apiserver的target:

3.6.6 kubelet数据获取

kubelet暴露的metrics端口默认为 10255

  • 提供的prometheus格式指标接口:nodeIP:10255/metrics,使用Prometheus从这里取数据
  • kubelet提供的stats/summary接口:nodeIP:10255/stats/summary,heapster和最新的metrics-server从这里获取数据

kubelet采集的指标主要有:

apiserver_client_certificate_expiration_seconds_bucket
apiserver_client_certificate_expiration_seconds_sum
apiserver_client_certificate_expiration_seconds_count
etcd_helper_cache_entry_count
etcd_helper_cache_hit_count
etcd_helper_cache_miss_count
etcd_request_cache_add_latencies_summary
etcd_request_cache_add_latencies_summary_sum
etcd_request_cache_add_latencies_summary_count
etcd_request_cache_get_latencies_summary
etcd_request_cache_get_latencies_summary_sum
etcd_request_cache_get_latencies_summary_count
kubelet_cgroup_manager_latency_microseconds
kubelet_containers_per_pod_count
kubelet_docker_operations
kubelet_network_plugin_operations_latency_microseconds
kubelet_pleg_relist_interval_microseconds
kubelet_pleg_relist_latency_microseconds
kubelet_pod_start_latency_microseconds
kubelet_pod_worker_latency_microseconds
kubelet_running_container_count
kubelet_running_pod_count
kubelet_runtime_operations*
kubernetes_build_info
process_cpu_seconds_total
reflector*
rest_client_request_*
storage_operation_duration_seconds_*

查看kubelet监控指标数据(nodeIP:10255/metrics):

# HELP apiserver_audit_event_total Counter of audit events generated and sent to the audit backend.
# TYPE apiserver_audit_event_total counter
apiserver_audit_event_total 0
# HELP apiserver_client_certificate_expiration_seconds Distribution of the remaining lifetime on the certificate used to authenticate a request.
# TYPE apiserver_client_certificate_expiration_seconds histogram
apiserver_client_certificate_expiration_seconds_bucket{le="0"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="21600"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="43200"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="86400"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="172800"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="345600"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="604800"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="2.592e+06"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="7.776e+06"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="1.5552e+07"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="3.1104e+07"} 0
apiserver_client_certificate_expiration_seconds_bucket{le="+Inf"} 4161
apiserver_client_certificate_expiration_seconds_sum 1.3091542942737878e+12
apiserver_client_certificate_expiration_seconds_count 4161
...

kubelet由于在每个节点上都有且仅有一个,所以可以通过k8s的node对象找到kubelet的指标,prometheus配置如下:

      - job_name: 'kubelet'
        # 通过https访问apiserver,通过apiserver的api获取数据
        scheme: https
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
                
        #以k8s的角色(role)来定义收集,比如node,service,pod,endpoints,ingress等等 
        kubernetes_sd_configs:
        # 从k8s的node对象获取数据
        - role: node
        relabel_configs:
        # 用新的前缀代替原label name前缀,没有replacement的话功能就是去掉label_name前缀
        # 例如:以下两句的功能就是将__meta_kubernetes_node_label_kubernetes_io_hostname
        # 变为kubernetes_io_hostname
        - action: labelmap
          regex: __meta_kubernetes_node_label_(.+)
        # replacement中的值将会覆盖target_label中指定的label name的值,
        # 即__address__的值会被替换为kubernetes.default.svc:443
        - target_label: __address__
          replacement: kubernetes.default.svc:443
          #replacement: 10.142.21.21:6443
        # 获取__meta_kubernetes_node_name的值
        - source_labels: [__meta_kubernetes_node_name]
          #匹配一个或多个任意字符,将上述source_labels的值生成变量
          regex: (.+)
          # 将# replacement中的值将会覆盖target_label中指定的label name的值,
          # 即__metrics_path__的值会被替换为/api/v1/nodes/${1}/proxy/metrics,
          # 其中${1}的值会被替换为__meta_kubernetes_node_name的值
          target_label: __metrics_path__
          replacement: /api/v1/nodes/${1}/proxy/metrics
        #or:
        #- source_labels: [__address__]
        #  regex: '(.*):10250'
        #  replacement: '${1}:4194'
        #  target_label: __address__
        #- source_labels: [__meta_kubernetes_node_label_role]
        #  action: replace
        #  target_label: role

4. 数据汇聚层—Prometheus部署和配置

前面介绍了K8S平台的监控数据怎么采集出来,接下来介绍怎么用prometheus收集和处理。

4.1 prometheus全局配置文件

configmap-prom-config.yaml,这个是我改写的prometheus全局配置的configmap文件,我参考的Github官方示例,然后做适当改动并增加了一些注释。prometheus的config文件的更详细语法请见官网

4.2 prometheus告警规则配置文件

Prometheus中的告警规则允许用户基于PromQL表达式定义告警触发条件,Prometheus后端对这些触发规则进行周期性的计算,当满足触发条件后会触发告警通知。用户可以通过Prometheus的Web界面查看这些告警规则(/rules)以及告警的触发状态(/alerts)。当Promthues与Alertmanager关联之后,可以将告警发送到外部服务如Alertmanager中并通过Alertmanager可以对这些告警进行进一步的处理。

在Prometheus全局配置文件中通过rule_files指定一组告警规则文件的路径。Prometheus启动后会自动扫描这些路径下规则文件中定义的内容,并且根据这些规则计算是否向外部发送通知。计算告警触发条件的周期可以通过global下的evaluation_interval进行配置:

global:
  [ evaluation_interval:  | default = 1m ]
rule_files:
  [ -  ... ]

4.2.1 定义告警规则

告警规则文件使用yaml格式进行定义,一个典型的告警配置如下:

groups:
- name: example
  rules:
  - alert: HighErrorRate
    expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
    for: 10m
    labels:
      severity: page
    annotations:
      summary: High request latency
      description: description info

在告警规则文件中,我们可以将一组相关的规则设置定义在一个group下。在每一个group中我们可以定义多个告警规则(rule)。一条告警规则主要由以下几部分组成:

  • alert:告警规则的名称。
  • expr:基于PromQL表达式告警触发条件,用于计算是否有时间序列满足该条件。
  • for:评估等待时间,可选参数。用于表示只有当触发条件持续一段时间后才发送告警。在等待期间新产生告警的状态为pending。
  • labels:自定义标签,允许用户指定要附加到告警上的一组附加标签。
  • annotations:用于指定一组附加信息,比如用于描述告警详细信息的文字等。

Prometheus根据global.evaluation_interval定义的周期计算PromQL表达式。如果PromQL表达式能够找到匹配的时间序列则会为每一条时间序列产生一个告警实例。

rules的config文件的详细语法请见官网

4.2.2 模板化

一般来说,在告警规则文件的annotations中使用summary描述告警的概要信息,description用于描述告警的详细信息。同时Alertmanager的UI也会根据这两个标签值,显示告警信息。为了让告警信息具有更好的可读性,Prometheus支持模板化label和annotations的中标签的值。

通过$labels.变量可以访问当前告警实例中指定标签的值。$value则可以获取当前PromQL表达式计算的样本值。

# To insert a firing element's label values:
{{ $labels. }}
# To insert the numeric expression value of the firing element:
{{ $value }}

例如,可以通过模板化优化summary以及description的内容的可读性:

groups:
- name: example
  rules:
​
  # Alert for any instance that is unreachable for >5 minutes.
  - alert: InstanceDown
    expr: up == 0
    for: 5m
    labels:
      severity: page
    annotations:
      summary: "Instance {{ $labels.instance }} down"
      description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."
​
  # Alert for any instance that has a median request latency >1s.
  - alert: APIHighRequestLatency
    expr: api_http_request_latencies_second{quantile="0.5"} > 1
    for: 10m
    annotations:
      summary: "High request latency on {{ $labels.instance }}"
      description: "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)"

4.2.3 告警规则创建

configmap-prom-rule.yaml,这是我的prometheus配置告警规则的configmap文件。参考的这里
告警规则允许你定义基于Prometheus语言表达的报警条件,并发送报警通知到外部服务,如AlertManager。rules的config文件的详细语法请见官网

可以在Prometheus的alert界面看到告警规则:

可以在Prometheus的alert界面看到告警信息:

4.3 prometheus启动文件

prometheus.yaml是prometheus以deployment起在kubernetes上的部署文件,给出了两种部署方式,分别是集群开启和未开启rbac时的部署文件,以开启rbac时的为例,内容如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: prometheus
  namespace: kube-system
  labels:
    app: prometheus
spec:
  replicas: 1
  template:
    metadata:
      name: prometheus
      labels:
        app: prometheus
    spec:
      serviceAccountName: prometheus
#      hostNetwork: true
      containers:
      - name: prometheus
        image: prom/prometheus:v2.3.2
        imagePullPolicy: IfNotPresent
        args:
          - '--storage.tsdb.path=/prometheus/data/'
          - '--storage.tsdb.retention=1d'
          - '--config.file=/etc/prometheus/prometheus.yaml'
          - '--web.enable-lifecycle'
        ports:
        - name: webui
          containerPort: 9090
        resources:
          requests:
            cpu: 500m
            memory: 500M
        #  limits:
        #    cpu: 500m
        #    memory: 500M
        volumeMounts:
        - name: config-volume
          mountPath: /etc/prometheus
        - name: rules-volume
          mountPath: /etc/prometheus-rules
      volumes:
      - name: config-volume
        configMap:
          name: prometheus
      - name: rules-volume
        configMap:
          name: prometheus-rules
      nodeSelector:
        node-role.kubernetes.io/master: "true"
      tolerations:
      - key: "node-role.kubernetes.io/master"
        effect: "NoSchedule"
---
apiVersion: v1
kind: Service
metadata:
  name: prometheus
  namespace: kube-system
  labels:
    app: prometheus
  annotations:
    prometheus.io/scrape: 'true'
spec:
  type: NodePort
#  type: ClusterIP
  ports:
    - name: webui
      port: 9090
      protocol: TCP
      nodePort: 30006
  selector:
    app: prometheus

对args参数做几点说明:

  • --storage.tsdb.path:tsdb数据库存储路径
  • --storage.tsdb.retention:数据保留多久,可以看官方文档存储部分
  • --config.file:指定prometheus的config文件的路径
  • --web.enable-lifecycle:加上这个参数后可以向/-/reload(curl -XPOST 10.142.232.150:30006/-/reload)发送HTTP POST请求实现prometheus在config文件修改后的动态reload,更多信息请查看官方文档
  • --web.enable-admin-api:加上这个参数可以为一些高级用户暴露操作数据库功能的API,比如快照备份(curl -XPOST http://>/api/v2/admin/tsdb/snapshot),更多信息请查看官方文档 TSDB Admin APIs部分

启动后可以(IP:Port/targets)查看所有targets:

更多告警规则用法请参考:https://yunlzheng.gitbook.io/prometheus-book/parti-prometheus-ji-chu/alert/prometheus-alert-rule

5.可视化展示

5.1安装grafana

rpm部署方式:

wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.4-1.x86_64.rpm
yum -y localinstall grafana-5.0.4-1.x86_64.rpm 
systemctl enable grafana-server
systemctl start grafana-server

docker部署方式:

docker run -d -p 3000:3000 --name=grafana -e "GF_SERVER_HTTP_PORT=3000" -e "GF_AUTH_BASIC_ENABLED=false" -e "GF_AUTH_ANONYMOUS_ENABLED=true" -e "GF_AUTH_ANONYMOUS_ORG_ROLE=Admin"  -e "GF_SERVER_ROOT_URL=/" daocloud.io/liukuan73/grafana:5.0.0
		

kubernetes部署方式(推荐):
安装grafana,grafana.yaml如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: monitoring-grafana
  namespace: kube-system
spec:
  replicas: 1
  template:
    metadata:
      labels:
        task: monitoring
        k8s-app: grafana
    spec:
      containers:
      - name: grafana
        image: daocloud.io/liukuan73/grafana:5.0.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 3000
          protocol: TCP
        volumeMounts:
        - mountPath: /var
          name: grafana-storage
        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: grafana-storage
        emptyDir: {}
      nodeSelector:
        node-role.kubernetes.io/master: "true"
      tolerations:
      - key: "node-role.kubernetes.io/master"
        effect: "NoSchedule"
---
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
  annotations:
    prometheus.io/scrape: 'true'
    prometheus.io/tcp-probe: 'true'
    prometheus.io/tcp-probe-port: '80'
  name: monitoring-grafana
  namespace: kube-system
spec:
  type: NodePort
  # 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
    nodePort: 30007
  selector:
    k8s-app: grafana

5.2 grafana配置

配置prometheus数据源:
点击“Add data source”配置数据源:

配置dashboard:
可以手工配置dashboard,也可以如下图直接使用官网上别人分享的已配好的模板,直接输入需要import的模板号即可:
效果:

一些比较实用的模板:

  • 315这个模板是cadvisor采集的各种指标的图表
  • 1860这个模板是node-exporter采集的各种主机相关的指标的图表
  • 6417这个模板是kube-state-metrics采集的各种k8s资源对象的状态的图表
  • 4859和4865这两个模板是blackbox-exporter采集的服务的http状态指标的图表(两个效果基本一样,选择其一即可)
  • 5345这个模板是blackbox-exporter采集的服务的网络状态指标的图表

6. 告警

设置警报和通知的主要步骤:

  • 安装配置Alertmanager
  • 配置Prometheus与Alertmanager通信
  • 在Prometheus中创建告警规则

6.1 告警组件AlertManager部署

部署文件alertmanager.yaml如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: alertmanager
  namespace: kube-system
spec:
  replicas: 1
  template:
    metadata:
      name: alertmanager
      labels:
        app: alertmanager
    spec:
      containers:
      - name: alertmanager
        image: prom/alertmanager:v0.11.0
        args
          - '-config.file=/etc/alertmanager/config.yml'
          - '-storage.path=/alertmanager'
          - '-web.external-url=http://alertmanager:9093'
        ports:
        - name: alertmanager
          containerPort: 9093
#        env:
#        - name: EXTERNAL_URL
#          valueFrom:
#            configMapKeyRef:
#              name: external-url
#              key: url
        volumeMounts:
        - name: config-volume
          mountPath: /etc/alertmanager
#        - name: templates-volume
#          mountPath: /etc/alertmanager-templates
        - name: alertmanager
          mountPath: /alertmanager
      volumes:
      - name: config-volume
        configMap:
          name: alertmanager
#      - name: templates-volume
#        configMap:
#          name: alertmanager-templates
      - name: alertmanager
        emptyDir: {}
      nodeSelector:
        node-role.kubernetes.io/master: "true"
      tolerations:
      - key: "node-role.kubernetes.io/master"
        effect: "NoSchedule"
---
apiVersion: v1
kind: Service
metadata:
  name: alertmanager
  namespace: kube-system
  annotations:
    prometheus.io/scrape: 'true'
    prometheus.io/path: '/alertmanager/metrics'
  labels:
    name: alertmanager
spec:
  selector:
    app: alertmanager
  type: NodePort
  ports:
  - name: alertmanager
    protocol: TCP
    port: 9093
    targetPort: 9093
    nodePort: 30008

alertmanager的配置文件configmap-alertmanager.yaml如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: alertmanager
  namespace: kube-system
data:
  config.yml: |-
    global:
      resolve_timeout: 5m
      smtp_smarthost: 'smtp.exmail.qq.com:25'
      smtp_from: '[email protected]'
      smtp_auth_username: '[email protected]'
      smtp_auth_password: 'yourpassword'
      slack_api_url: ''
    route:
      receiver: slack-notifications
      group_by: ['alertname', 'cluster', 'service']
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 15m
      routes:
      - match:
          severity: email
        receiver: email_alert
    receivers:
    - name: 'email_alert'
      email_configs:
      - to: '[email protected]'
        from: 'test.com'
        smarthost: "smtp.163.com:25"
        auth_username: "liukuan73"
        auth_password: "123456"
        require_tls: true
    - name: 'slack-notifications'
      slack_configs:
      - channel: '#alerts'
    - name: 'test-webhook'
      webhook_configs:
      - url: 'alertmanager:9093/api/v1/monitor'
        send_resolved: true

可以在运行时动态的重新加载配置文件,当重新加载配置文件失败时,不影响在运行的配置。重载方式:curl -XPOST http://IP:9093/-/reload。告警部分的配置语法请见官网文档

6.2 关联Prometheus与Alertmanager

在Prometheus的架构中被划分成两个独立的部分。Prometheus负责产生告警,而Alertmanager负责告警产生后的后续处理。因此Alertmanager部署完成后,需要在Prometheus中设置Alertmanager相关的信息。

编辑Prometheus配置文件prometheus.yml,并添加以下内容

    alerting:
      alertmanagers:
        - static_configs:
          - targets: ['alertmanager:9093']

reload Prometheus后可以在AlertManager的webui上查看告警信息:http://IP:Port

6.3 Alertmanager配置

Alertmanager的配置主要包含两个部分:路由(route)以及接收器(receivers)。所有的告警信息都会从配置中的顶级路由(route)进入路由树,根据路由规则将告警信息发送给相应的接收器。

在Alertmanager中可以定义一组接收器,比如可以按照角色(比如系统运维,数据库管理员)来划分多个接收器。接收器可以关联邮件,Slack以及其它方式接收告警信息。

route:
  receiver: 'default-receiver'
receivers:
  - name: default-receiver

以上配置文件中定义了一个默认的接收者default-receiver,由于这里没有设置接收方式,目前只相当于一个占位符。

在配置文件中使用route定义了顶级的路由,路由是一个基于标签匹配规则的树状结构。所有的告警信息从顶级路由开始,根据标签匹配规则进入到不同的子路由,并且根据子路由设置的接收器发送告警。目前配置文件中只设置了一个顶级路由route并且定义的接收器为default-receiver。因此,所有的告警都会发送给default-receiver。

6.3.1 路由(route)配置基础介绍

路由的配置格式如下:

[ receiver:  ]
[ group_by: '[' , ... ']' ]
[ continue:  | default = false ]
​
match:
  [ : , ... ]
​
match_re:
  [ : , ... ]
​
[ group_wait:  | default = 30s ]
​
[ group_interval:  | default = 5m ]
​
[ repeat_interval:  | default = 4h ]
​
routes:
  [ -  ... ]

首先每一个告警都会从配置文件中顶级的route进入路由树,需要注意的是顶级的route必须匹配所有告警,即不能有任何的匹配设置(match/match_re),每一个路由都可以定义自己的接收器。告警进入到顶级路由后会遍历所有的子节点。如果设置了continue的值为false,则告警在匹配到第一个子节点之后就直接停止。如果continue为true,报警则会继续进行后续子节点的匹配。如果当前告警匹配不到任何的子节点,那么该告警将会基于当前路由节点的接收器配置方式进行处理。

其中告警的匹配有两种方式可以选择。一种方式基于字符串验证,通过设置match规则判断当前告警中是否存在标签labelname并且其值等于labelvalue。第二种方式则基于正则表达式,通过设置match_re验证当前告警标签的值是否满足正则表达式的内容。

在之前的部分有讲过,Alertmanager可以对告警通知进行分组,将多条告警合合并为一个通知。这里我们可以使用group_by来定义分组规则。基于告警中包含的标签,如果满足group_by中定义标签名称,那么这些告警将会合并为一个通知发送给接收器。

有的时候为了能够一次性收集和发送更多的相关信息,可以通过group_wait参数设置等待时间,如果在等待时间内当前group接收到了新的告警,这些告警将会合并为一个通知向receiver发送。

group_interval配置,则用于定义相同的Gourp之间发送告警通知的时间间隔。

如果警报已经成功发送通知, 如果想设置发送告警通知之前要等待时间,则可以通过repeat_interval参数进行设置。

案例

例如,当使用Prometheus监控多个集群以及部署在集群中的应用和数据库服务,并且定义以下的告警处理路由规则来对集群中的异常进行通知。

route:
  receiver: 'default-receiver'
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  group_by: [cluster, alertname]
  routes:
  - receiver: 'database-pager'
    group_wait: 10s
    match_re:
      service: mysql|cassandra
  - receiver: 'frontend-pager'
    group_by: [product, environment]
    match:
      team: frontend

默认情况下所有的告警都会发送给集群管理员default-receiver,因此在Alertmanager的配置文件的根路由中,对告警信息按照集群以及告警的名称对告警进行分组。

如果告警时来源于数据库服务如MySQL或者Cassandra,此时则需要将告警发送给相应的数据库管理员(database-pager)。这里定义了一个单独子路由,如果告警中包含service标签,并且service为MySQL或者Cassandra,则向database-pager发送告警通知,由于这里没有定义group_by等属性,这些属性的配置信息将从上级路由继承,database-pager将会接收到按cluser和alertname进行分组的告警通知。

而某些告警规则来源可能来源于开发团队的定义,这些告警中通过添加标签team来标示这些告警的创建者。在Alertmanager配置文件的告警路由下,定义单独子路由用于处理这一类的告警通知,如果匹配到告警中包含标签team,并且team的值为frontend,Alertmanager将会按照标签product和environment对告警进行分组。此时如果应用出现异常,开发团队就能清楚的知道哪一个环境(environment)中的哪一个应用程序出现了问题,可以快速对应用进行问题定位。

更多路由配置实例请参考:https://yunlzheng.gitbook.io/prometheus-book/parti-prometheus-ji-chu/alert/alert-manager-routes

6.3.2 接收器(receiver)配置基础介绍

告警接收器可以通过以下形式进行配置:

receivers:
  -  ...

每一个receiver具有一个全局唯一的名称,并且对应一个或者多个通知方式:

name: 
email_configs:
  [ - , ... ]
hipchat_configs:
  [ - , ... ]
pagerduty_configs:
  [ - , ... ]
pushover_configs:
  [ - , ... ]
slack_configs:
  [ - , ... ]
opsgenie_configs:
  [ - , ... ]
webhook_configs:
  [ - , ... ]
victorops_configs:
  [ - , ... ]

目前官方内置的第三方通知集成包括:

  • 邮件
  • 即时通讯软件(如Slack、Hipchat)
  • 移动应用消息推送(如Pushover)
  • 自动化运维工具(例如:Pagerduty、Opsgenie、Victorops)
  • webhook,通过这种方式开发者可以实现更多个性化的扩展支持。

与SMTP邮件集成

每一个receiver可以对应一组邮件通知配置email_configs,如下所示:

name: 
email_configs:
  [ - , ... ]

email_config配置:

# Whether or not to notify about resolved alerts.
[ send_resolved:  | default = false ]
​
# The email address to send notifications to.
to: 
​
# The sender address.
[ from:  | default = global.smtp_from ]
​
# The SMTP host through which emails are sent.
[ smarthost:  | default = global.smtp_smarthost ]
​
# SMTP authentication information.
[ auth_username:  | default = global.smtp_auth_username ]
[ auth_password:  | default = global.smtp_auth_password ]
[ auth_secret:  | default = global.smtp_auth_secret ]
[ auth_identity:  | default = global.smtp_auth_identity ]
​
# The SMTP TLS requirement.
[ require_tls:  | default = global.smtp_require_tls ]
​
# The HTML body of the email notification.
[ html:  | default = '{{ template "email.default.html" . }}' ]
​
# Further headers email header key/value pairs. Overrides any headers
# previously set by the notification implementation.
[ headers: { : , ... } ]

如果所有的邮件配置使用了相同的SMTP配置,则可以直接定义全局的SMTP配置。

global:
  [ smtp_from:  ]
  [ smtp_smarthost:  ]
  [ smtp_auth_username:  ]
  [ smtp_auth_password:  ]
  [ smtp_auth_secret:  ]
  [ smtp_auth_identity:  ]
  [ smtp_require_tls:  | default = true ]

以Gmail邮箱为例:

global:
  smtp_smarthost: smtp.gmail.com:587
  smtp_from: 
  smtp_auth_username: 
  smtp_auth_identity: 
  smtp_auth_password: 
​
receivers:
  - name: default-receiver
    email_configs:
      - to: 

6.3.3 使用Webhook扩展Alertmanager

在某些情况下除了Alertmanager已经内置的集中告警通知方式以外,对于不同的用户和组织而言还需要一些自定义的告知方式支持。通过Alertmanager提供的webhook支持可以轻松实现这一类的扩展。除了用于支持额外的通知方式,webhook还可以与其他第三方系统集成实现运维自动化,或者弹性伸缩等。

在Alertmanager中可以使用如下配置定义基于webhook的告警接收器receiver。一个receiver可以对应一组webhook配置。

name: 
webhook_configs:
  [ - , ... ]

每一项webhook_config的具体配置格式如下:

# Whether or not to notify about resolved alerts.
[ send_resolved:  | default = true ]
​
# The endpoint to send HTTP POST requests to.
url: 
​
# The HTTP client's configuration.
[ http_config:  | default = global.http_config ]

send_resolved用于指定是否在告警消除时发送回执消息。url则是用于接收webhook请求的地址。http_configs则是在需要对请求进行SSL配置时使用。

当用户定义webhook用于接收告警信息后,当告警被触发时,Alertmanager会按照以下格式向这些url地址发送HTTP Post请求,请求内容如下:

{
  "version": "4",
  "groupKey": ,    // key identifying the group of alerts (e.g. to deduplicate)
  "status": "",
  "receiver": ,
  "groupLabels": ,
  "commonLabels": ,
  "commonAnnotations": ,
  "externalURL": ,  // backlink to the Alertmanager.
  "alerts": [
    {
      "labels": ,
      "annotations": ,
      "startsAt": "",
      "endsAt": ""
    }
  ]
}

更多webhook方式实例请参看:https://yunlzheng.gitbook.io/prometheus-book/parti-prometheus-ji-chu/alert/alert-manager-extension-with-webhook


更多K8S精彩内容,请订阅本人微信公众号:K8SPractice


如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

支付宝:                                                      微信:
Kubernetes平台监控方案之:Exporters+Prometheus+Grafana_第2张图片                       Kubernetes平台监控方案之:Exporters+Prometheus+Grafana_第3张图片

你可能感兴趣的:(kubernetes,监控)