MENU

Kubernetes 集群监控:Prometheus + alertmanager+ grafana

2019 年 07 月 17 日 • 应用服务器

现在开始重做K8S集群监控,之前做的不完善,如果你不了解Prometheus/alertmanager/node_exporter看本文会比较吃力,先来了解一下在k8s中要监控哪些东西,一般是分为两个指标,一个是K8S本身,在一个就是Pod监控。

  • Kubernetes本身监控

    • Node资源利用率
    • Node数量
    • pod数量
    • 资源对象状态
  • Pod 监控

    • Pod数量,指的是某个项目运行了多少个pod
    • 容器资源利用率
    • 应用程序,类型自定义监控

具体使用哪些东西去收集这些指标,如下。

指标实现举例
Pod性能cAdvisor,kubelet内置容器CPU,内存利用率
Node性能node-exporter节点CPU,内存利用率
K8S资源对象kube-state-metricsPod/Deployment/Service

比较重要的一点,K8S中所有资源都是动态的,拿Pod来说,很久之前就提过Pod是短暂的,你不可能在Prometheus中去配置我要去监控哪个pod,这个方法根本行不通的,不要想了,所以现在涉及到Prometheus基于kubernetes的服务发现,他这个是原生就支持的,他会在你kube-apiserver中去发现要抓取的目标,之前也提过,点击这里深入了解,下面看一下要实施的监控方案。

使用cAdvisor/node_exporter+Prometheus+Grafana来做,也是目前比较主流的方案,架构图如下,

cAdvisor已经集成到kubelet中了,所以我们不需要再借助外部插件对pod进行监控了,我们只需要拿到他自身暴露出的指标就可以了,拿到指标之后Prometheus进行分析存储,通过Grafana进行展示,对node进行监控还是用node_exports

还有一个k8s常见的资源状态,例如deployment运行的数量等等这些,你想要获取这些东西需要在apiserver中获取,这些东西都是存储在etcd数据库中,所以要通过一个名为kube-state-metrics的组件来实现监控资源状态,这个组件作用就是在k8s中获取到资源状态的监控指标给暴露出来,最后通过alertmanager进行告警,其实Grafana也可以告警,他的设置方式是对图表设置阈值,超了我就告警,比较麻烦,所以告警这块还是用alertmanager吧,下面开始在K8S集群中部署Prometheus

在K8S中部署Prometheus

这次部署主要是参考官方的部署方式,GitHub地址,这个链接的位置就是插件目录下,也就是这个位置,

YAML文件比较多,我要用最新的,所以我在clone一下,把文件复制到/root

[root@master-1 ~/k8s]# mkdir Prometheus && cd Prometheus/
[root@master-1 ~/k8s/Prometheus]# git clone https://github.com/kubernetes/kubernetes.git
[root@master-1 ~/k8s/Prometheus]# cp -r kubernetes-master/cluster/addons/prometheus /root/
[root@master-1 ~/prometheus]# cd /root/prometheus/

这些文件主要分为四部分,第一部分是prometheus的部署文件,包含了configmap&rbac&service&statefulset,共四个文件,第二部分是kube-state-metrics,获取资源对象的部署文件,第三部分为node-exporter部署文件,第四部分alertmanager部署文件,这个是用来告警的,开始部署吧,先部署prometheus

首先需要部署rbac&configmap

[root@master-1 ~/prometheus]# kubectl apply -f prometheus-rbac.yaml
[root@master-1 ~/prometheus]# kubectl apply -f prometheus-configmap.yaml

部署prometheus的方式为statefulset,也就是有状态服务,这个也用到了PV动态供给,它默认的模板里指定了storageClassName类名为standard,也就是这里定义的,

但是我这里莫得这个类哇,只有一个这个,

所以改成这个之后就可创建了,改这一处就够了,直接创建吧,

[root@master-1 ~/prometheus]# kubectl apply -f prometheus-statefulset.yaml 

最后是Services,这里加一个NodePort,改完后直接创建了,

spec:
  type: NodePort
  ports:
    - name: http
      port: 9090
      protocol: TCP
      targetPort: 9090
      nodePort: 39090
  selector:
    k8s-app: prometheus
[root@master-1 ~/prometheus]# kubectl get service -n kube-system

这样就创建完了,访问一下Node:39090 ,看到这个页面就说明没啥子问题。

时间差的比较大忽略,一会再解决这个问题,现在进到容器里面去,看一下他的主配置文件,

基于K8S服务发现的配置解析

先看一下部署prometheus的部署文件,他是启动了两个容器,其中一个是这个,

        - name: prometheus-server-configmap-reload
          image: "jimmidyson/configmap-reload:v0.1"
          imagePullPolicy: "IfNotPresent"

这个容器的作用是这样,当他发现configmap有更新的时候,会自动重载prometheus,现在进入到prometheus-server容器中解析一下他的配置文件,先是这一段,采集本机的我就不贴了,

[root@master-1 ~]# kubectl exec -it -n kube-system prometheus-0 -c prometheus-server sh
/prometheus $ cat /etc/config/prometheus.yml
- job_name: kubernetes-apiservers
  kubernetes_sd_configs:
  - role: endpoints
  relabel_configs:
  - action: keep
    regex: default;kubernetes;https
    source_labels:
    - __meta_kubernetes_namespace
    - __meta_kubernetes_service_name
    - __meta_kubernetes_endpoint_port_name
  scheme: https
  tls_config:
    ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    insecure_skip_verify: true
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

job_namekubernetes-apiservers,动态发现类型为kubernetes_sd_configs,也就是基于kubernetes的服务发现,这一段的配置采集的是这个,

下面又使用了重新标记的标签,使用了keep,也就是只保留正则匹配的标签,源标签的值包含default;kubernetes;https,我只采集这些,所以这一段的作用就是采集kube-apiserver,使用的方法是https,最后就是普罗米修斯访问apiserver使用的CAtoken,这是两个默认的,任何一个pod都会有这两个文件,管理员权限,再下面,这一段,

- job_name: kubernetes-nodes-kubelet
  kubernetes_sd_configs:
  - role: node
  relabel_configs:
  - action: labelmap
    regex: __meta_kubernetes_node_label_(.+)
  scheme: https
  tls_config:
    ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    insecure_skip_verify: true
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

他是采集了每个节点的10250/metrics,这个10250就是kubelet集成的cAdvisor单独暴露的端口,这个接口是采集kubelet本身暴露出来的一些指标,他的角色为node,访问的方式还是https,和上面的方式是一样的,再往下看,

- job_name: kubernetes-service-endpoints
  kubernetes_sd_configs:
  - role: endpoints
  relabel_configs:
  - action: keep
    regex: true
    source_labels:
    - __meta_kubernetes_service_annotation_prometheus_io_scrape
  - action: replace
    regex: (https?)
    source_labels:
    - __meta_kubernetes_service_annotation_prometheus_io_scheme
    target_label: __scheme__
  - action: replace
    regex: (.+)
    source_labels:
    - __meta_kubernetes_service_annotation_prometheus_io_path
    target_label: __metrics_path__
  - action: replace
    regex: ([^:]+)(?::\d+)?;(\d+)
    replacement: $1:$2
    source_labels:
    - __address__
    - __meta_kubernetes_service_annotation_prometheus_io_port
    target_label: __address__
  - action: labelmap
    regex: __meta_kubernetes_service_label_(.+)
  - action: replace
    source_labels:
    - __meta_kubernetes_namespace
    target_label: kubernetes_namespace
  - action: replace
    source_labels:
    - __meta_kubernetes_service_name
    target_label: kubernetes_name

这里是用来发现endpoints的,__开头的都是prometheus自有的,一般情况下不会直接使用,如果直接用的话需要重新打标签,看页面,目前只是采集到一个corednsendpoints,我目前coredns启动了两个,

为毛endpoints这么多只是收集到了一个,原因就是relabel_configs那里也做了一个keep,也就是这一段

  - action: keep
    regex: true
    source_labels:
    - __meta_kubernetes_service_annotation_prometheus_io_scrape

这里判断了你元数据里面有没有prometheus_io_scrape这个KEY,这个prometheus_io_scrape就定义了我要不要采集这个目标,所以需要在你创建的时候声明一下,如果prometheus_io_scrape值为true,我就采集他,如果没有任何声明,就默认不采集,所以说白了就是只保留目标源标签包含prometheus_io_scrape的,这东西具体在哪配的,看一下coredns service是怎么写的,主要内容,

[root@master-1 ~]# kubectl get service -n kube-system kube-dns -o yaml

所以你的endpoints想被prometheus抓取就需要加这个声明,是否被采集,采集的端口是什么,使用什么协议,都需要声明,再下面,

- job_name: kubernetes-services
  kubernetes_sd_configs:
  - role: service
  metrics_path: /probe
  params:
    module:
    - http_2xx
  relabel_configs:
  - action: keep
    regex: true
    source_labels:
    - __meta_kubernetes_service_annotation_prometheus_io_probe
  - source_labels:
    - __address__
    target_label: __param_target
  - replacement: blackbox
    target_label: __address__
  - source_labels:
    - __param_target
    target_label: instance
  - action: labelmap
    regex: __meta_kubernetes_service_label_(.+)
  - source_labels:
    - __meta_kubernetes_namespace
    target_label: kubernetes_namespace
  - source_labels:
    - __meta_kubernetes_service_name
    target_label: kubernetes_name

这里是黑盒探测services是否可用,这个之前没提过,他用了blackbox组件,探测路径为/probehttp探测,下面的就不贴了,都是类似的东西了,prometheus在采集你资源的时候用的值都是在被创建对象中动态获取的,也就是需要你去定义的,下面开始监控K8S集群中的pod

监控集群中pod

开始监控资源了,最重要的指标就是pod了,要想监控pod很简单,上面提到了监控pod需要使用cAdvisor,而cAdvisor已经集成到kubelet中了,cAdvisor会采集所有pod的指标,包括宿主机的内存CPUcAdvisor有两个暴露地址,分别是NodeIP:{10255,10250}/metrice/cadvisor,这是在kubelet配置文件中指定的,也就是这里,

port: 10250            #暴露kubelet自身指标端口
readOnlyPort: 10255    #单独cAdvisor端口

现在获取kubelet指标的端口是这个,

建议使用10250端口去获取指标,默认就是10250端口,现在已经有数据了,需要展示数据了,所以部署一下Grafana

部署Grafana

直接在K8S中部署吧,用StatefulSet去部署了,文件如下,我直接跑了,

[root@master-1 ~/prometheus]# cat grafana.yaml 
apiVersion: apps/v1 
kind: StatefulSet 
metadata:
  name: grafana
  namespace: kube-system
spec:
  serviceName: "grafana"
  replicas: 1
  selector:
    matchLabels:
      app: grafana
  template:
    metadata:
      labels:
        app: grafana
    spec:
      containers:
      - name: grafana
        image: grafana/grafana
        livenessProbe:
          httpGet:
            path: /login
            port: 3000
            scheme: HTTP
          initialDelaySeconds: 10
          timeoutSeconds: 10
          failureThreshold: 10
        readinessProbe:
          httpGet:
            path: /login
            port: 3000
            scheme: HTTP
          initialDelaySeconds: 10
          timeoutSeconds: 10
          failureThreshold: 10
        ports:
          - name: grafana
            containerPort: 3000
            protocol: TCP
        resources:
          limits:
            cpu: 300m            
            memory: 256Mi          
          requests:
            cpu: 100m            
            memory: 128Mi
        volumeMounts:
          - name: grafana-data
            mountPath: /var/lib/grafana
            subPath: grafana
  volumeClaimTemplates:
  - metadata:
      name: grafana-data
    spec:
      storageClassName: managed-nfs-storage 
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: "1Gi"

---

apiVersion: v1
kind: Service
metadata:
  name: grafana
  namespace: kube-system
spec:
  type: NodePort
  ports:
  - port : 80
    name: grafana
    targetPort: 3000
    nodePort: 33000
  selector:
    app: grafana
[root@master-1 ~/prometheus]# kubectl apply -f grafana.yaml 
statefulset.apps/grafana created
service/grafana created

创建好之后访问node节点的33000就可以了,还是常规操作,添加数据源,自行添加吧,然后导入一个仪表盘,集群资源监控模板建议使用3119,先把这个导入进来吧,这些操作我就不贴图了,导入之后发现Cluster filesystem usage没有值,我看了一下他的sql,查的是设备名是xvda,我的是sda,所以把这个改一下就有数据了,下面两个操作方式一致,所以大概长这样,

这个新版的grafana图形显示这里貌似有点问题,先不管了,下面开始监控集群Node

监控K8S集群node

也是使用node_exports来收集指标,node_exports这里就不用daemonsets方式去部署了,如果使用daemonsets方式部署问题比较多,可能会收集不到某些指标,所以我直接在每个node上直接部署node_exports了,部署方式和之前的部署方式一样,写个脚本,如下,

[root@master-1 ~/prometheus]# cat node-exporter.sh 
#!/bin/bash

## 解压重命名
tar zxf /tmp/node_exporter-0.18.1.linux-amd64.tar.gz -C /usr/local/ 
mv /usr/local/node_exporter-0.18.1.linux-amd64 /usr/local/node_exporter

##添加系统服务
cat > /usr/lib/systemd/system/node_exporter.service <<OEF 
[Unit]
Description=node_exporter

[Service]
Restart=on-failure
ExecStart=/usr/local/node_exporter/node_exporter --collector.systemd --collector.systemd.unit-whitelist=(docker|kubelet|kube-proxy|flanneld).service

[Install]
WantedBy=multi-user.target
OEF

##启动node_exporter
systemctl daemon-reload
systemctl enable node_exporter
systemctl restart node_exporter

我顺便监控了node组件的状态,如果node_exporter是跑在容器里的这个功能是没办法实现的,这个脚本很简单粗暴,由于软件包下载很慢,我把之前的拿过来了,直接用ansible把软件包传到目标的/tmp目录,剩下的就交给脚本去跑就够了,

[root@master-1 ~/prometheus]# ansible node -m copy -a "src=./node_exporter-0.18.1.linux-amd64.tar.gz dest=/tmp/"
[root@master-1 ~/prometheus]# ansible node -m script -a "./node-exporter.sh"

这样就行了,现在去改一下Configmap吧,自动发现是没办法发现我的node了,我把我的nodemaster分开了,master顺便监控了master组件的运行状态,还有需要添加一下数据采集的间隔时间,默认是一分钟,现在改成30s,所以在Configmap里面加了这些东西,

    global:
      scrape_interval:     30s

    - job_name: k8s-nodes
      static_configs:
      - targets:
        - 192.168.1.202:9100
        - 192.168.1.203:9100
        - 192.168.1.204:9100
        - 192.168.1.165:9100
        - 192.168.1.169:9100
        - 192.168.1.172:9100

    - job_name: k8s-master
      static_configs:
      - targets:
        - 192.168.1.200:9100
        - 192.168.1.201:9100

更新一下ConfigMap

[root@master-1 ~/prometheus]# kubectl apply -f prometheus-configmap.yaml 
configmap/prometheus-config configured

这样就会自动重载prometheus,还有一个问题要解决,那就是prometheus时间的问题,差了八小时,查出来的数据也有问题,所以直接把宿主的localtime文件挂到容器内就行了,所以statefulset也要加点东西撒,

            - name: localtime
              mountPath: /etc/localtime

        - name: localtime
          hostPath:
            path: /etc/localtime

更新一下,等重启完了看一下web页面,能看到这些就对了,

然后在grafana导入一个模板,ID9276,稍等一会就有数据了,

这样就行了,下面开始监控K8S资源对象

监控K8S资源对象

这里监控的就是K8S创建资源对象的状态信息,譬如说service/deployment/replicaset/endpoints等等,前面也提到了要监控这些东西需要用到kube-state-metrics组件,这个组件就是专门采集K8S中各种资源对象信息,比较全面,官方地址,下面开始部署吧,先RBAC授权吧,

[root@master-1 ~/prometheus]# kubectl apply -f kube-state-metrics-rbac.yaml 

然后开始部署deployment,有个镜像的地址需要换一下撒,也就是这个,

        image: k8s.gcr.io/addon-resizer:1.8.5

国内无法拉取的撒,所以地址换成这个吧,

        image: bairuijie/addon-resizer:1.8.5

这样就可以了,直接部署,Services也直接创建了,

[root@master-1 ~/prometheus]# kubectl apply -f kube-state-metrics-deployment.yaml 
deployment.apps/kube-state-metrics created
configmap/kube-state-metrics-config created
[root@master-1 ~/prometheus]# kubectl apply -f kube-state-metrics-service.yaml 
service/kube-state-metrics created

他会滚动启动两个副本,第二启动后第一个被莫名其妙的Terminating了,不是很理解,总之最后就是启动成功了,在页面看一下,有没有采集到的数据,

已经有东西了,以kube_开头的指标就是kube-state-metrics采集的,再导入一个模板进来,ID6417,仪表盘有很多,具体的看这里,我只是导入了自己感觉不错的,导入模板之后就是这样,

还是发现有没数据的,这些东西只能去自行调整了,这里面列出了很多东西,我看了一下下面的pod情况,当前有22pod正在运行,有一个pod出问题了,页面显示是这样,

我用命令看了一下,有个pod居然被驱逐了,我擦,

直接删了就好了,说明这里的数据是没啥子问题的,目前仪表盘里面只有一个deployment,像是statefulset什么的还没有图表,数据已经有了,所以你得手动去创建图表了,照葫芦画瓢吧,我尝试加了一下statefulset,大概是这种效果,

我没找到statefulset不可用的指标,就随便加了个指标,就是这种效果,复制然后改改里面的sql就行了,开始准备做告警吧,还是用alertmanager来做告警

在K8S中部署alertmanager

直接在K8S中部署alertmanager吧,Configmap文件存的就是alertmanager主配置文件,现在是默认的,暂时不改,一会再改,deployment自行看一下吧,用了PVC,这个存储类得改一下,改成你自己的存储类,如果你没有动态供给就改成别的方式吧,我这里已经改掉了,我直接部署了,

[root@master-1 ~/prometheus]# kubectl apply -f alertmanager-configmap.yaml 
configmap/alertmanager-config created
[root@master-1 ~/prometheus]# kubectl apply -f alertmanager-pvc.yaml 
persistentvolumeclaim/alertmanager created
[root@master-1 ~/prometheus]# kubectl apply -f alertmanager-deployment.yaml 
deployment.apps/alertmanager created
[root@master-1 ~/prometheus]# kubectl apply -f alertmanager-service.yaml 
service/alertmanager created

现在需要配置prometheusalertmanager进行通讯,去编辑prometheusConfigmap吧,你会发现目前配置的是动态发现,直接改了吧,改成静态的,

    alerting:
      alertmanagers:
      - static_configs:
          - targets: ["alertmanager:80"]
[root@master-1 ~/prometheus]# kubectl apply -f prometheus-configmap.yaml 
configmap/prometheus-config configured

这样就行了哇,看一下配置文件,已经生效了,

下面开始写告警规则吧,得先指定一个rules目录,改Configmap

    rule_files:
    - /etc/config/rules/*.rules

改完之后应用Configmap,然后还需要写configmap,这个是来存储告警规则的,我直接把之前写的copy过来了,顺便加了两条,存在了configmap里,YAML文件如下,

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-rules
  namespace: kube-system
data:
  collector.rules: |
    groups:
    - name: collector.rules
      rules:
    
      - alert: 采集器凉了
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.instance }} 采集器凉了撒"
          description: " {{ $labels.instance }} 采集器凉了有一分钟了撒"
  node.rules: |
    groups:
    - name: memeory_rules
      rules:
      - alert: 内存炸了
        expr: (node_memory_MemTotal_bytes - node_memory_MemFree_bytes - node_memory_Buffers_bytes - node_memory_Cached_bytes) / (node_memory_MemTotal_bytes )* 100 > 80
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.instance }} 内存炸了"
          description: "{{ $labels.instance }} 内存炸了,当前使用率为 {{ $value }}"

      - alert: CPU炸了
        expr: 100 - (avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) * 100) > 80 
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: "Instance {{ $labels.instance }} CPU炸了"       
          description: "{{ $labels.instance }}CPU炸了 当前使用率为 {{ $value }}"

      - alert: 磁盘剩余空间过低
        expr: 100 - (node_filesystem_free_bytes{fstype=~"ext4|xfs"} / node_filesystem_size_bytes{fstype=~"ext4|xfs"} * 100) > 80 
        for: 1m
        labels:
          severity: warning 
        annotations:
          summary: "Instance {{ $labels.instance }} : {{ $labels.mountpoint }} 分区剩余空间较小"
          description: "{{ $labels.instance }}: {{ $labels.mountpoint }} 分区剩余空间较小,当前使用率为 {{ $value }}"

我直接创建了,创建完了还不算完事,得把这个configmap挂载到prometheus/etc/config/rules/目录下,去改部署文件吧,

            - name: prometheus-rules
              mountPath: /etc/config/rules
        - name: prometheus-rules
          configMap:
            name: prometheus-rules

重新应用正常启动后看这里能看到的撒,

莫得问题,接下来就是要去改alertmanagerConfigmap了,也就是主配置文件,最后一步了,写了一个最简单的,如下,

apiVersion: v1
kind: ConfigMap
metadata:
  name: alertmanager-config
  namespace: kube-system
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: EnsureExists
data:
  alertmanager.yml: |
     global:
       resolve_timeout: 5m
       smtp_smarthost: 'smtp.163.com:25'
       smtp_from: 'xxx@163.com'
       smtp_auth_username: 'xxx@163.com'
       smtp_auth_password: 'xxx'
       smtp_require_tls: false
     
     route:
        group_by: ['alertname']
        group_wait: 10s
        group_interval: 10s
        repeat_interval: 1m
        receiver: 'email'
     receivers:
      - name: 'email'
        email_configs:
        - to: 'xxx@aliyun.com'

然后我让某个节点的CPU炸一次,

收到的邮件,

就是这种效果撒,没什么问题,告警信息已经能正常发送出来了。

上面就算是将监控K8S集群的流程过了一遍,其实现在监控指标已经很多了,接下来就是要去写告警规则画图表了,再就是自定义监控,当收集器收集的指标无法满足你需求的时候你就需要进行自定义监控了,自定义监控说白了就是需要你自己写收集器了,获取到你想要的东西把指标暴露出来,然后Prometheus去收集,收集到指标之后写告警规则,这个可没有zabbix自定义监控那么简单了,本篇就到这里了,也算是入门了。

返回文章列表 文章二维码 打赏
本页链接的二维码
打赏二维码