Prometheus介绍及安装

监控和日志是大型分布式系统的重要基础设施,监控可以帮助开发者查看系统的运行状态,而日志可以协助问题的排查和诊断。

在 Kubernetes 中,监控和日志属于生态的一部分,它并不是核心组件,因此大部分的能力依赖上层的云厂商的适配。Kubernetes 定义了介入的接口标准和规范,任何符合接口标准的组件都可以快速集成。

一. 监控类型

从监控类型上划分,在 K8s 中可以分成四个不同的类型:

1.1 资源监控

比较常见的像 CPU、内存、网络这种资源类的一个指标,通常这些指标会以数值、百分比的单位进行统计,是最常见的一个监控方式。这种监控方式在常规的监控里面,类似项目 zabbix telegraph,这些系统都是可以做到的。

1.2 性能监控

性能监控指的就是 APM 监控,也就是说常见的一些应用性能类的监控指标的检查。通常是通过一些 Hook 的机制在虚拟机层、字节码执行层通过隐式调用,或者是在应用层显示注入,获取更深层次的一个监控指标,一般是用来应用的调优和诊断的。比较常见的类似像 jvm 或者 php 的 Zend Engine,通过一些常见的 Hook 机制,拿到类似像 jvm 里面的 GC 的次数,各种内存代的一个分布以及网络连接数的一些指标,通过这种方式来进行应用的性能诊断和调优。

1.3 安全监控

安全监控主要是对安全进行的一系列的监控策略,类似像越权管理、安全漏洞扫描等等。

1.4 事件监控

事件监控是 K8s 中比较另类的一种监控方式。在K8s中的有一个设计理念,就是基于状态机的一个状态转换。从正常的状态转换成另一个正常的状态的时候,会发生一个 normal 的事件,而从一个正常状态转换成一个异常状态的时候,会发生一个 warning 的事件。通常情况下,warning 的事件是我们比较关心的,而事件监控就是可以把 normal 的事件或者是 warning 事件离线到一个数据中心,然后通过数据中心的分析以及报警,把相应的一些异常通过像钉钉或者是短信、邮件的方式进行暴露,弥补常规监控的一些缺陷和弊端。

二. Kubernetes 的监控演进

在早期,也就是 1.10 以前的 K8s 版本。大家都会使用类似像 Heapster 这样的组件来去进行监控的采集,Heapster 的设计原理其实也比较简单。

首先,我们在每一个 Kubernetes 上面有一个包裹好的 cadvisor,这个 cadvisor 是负责数据采集的组件。当 cadvisor 把数据采集完成,Kubernetes 会把 cadvisor 采集到的数据进行包裹,暴露成相应的 API。在早期的时候,实际上是有三种不同的 API:

  • 第一种是 summary 接口;
  • 第二种是 kubelet 接口;
  • 第三种是 Prometheus 接口。

这三种接口,其实对应的数据源都是 cadvisor,只是数据格式有所不同。而在 Heapster 里面,其实支持了 summary 接口和 kubelet 两种数据采集接口,Heapster 会定期去每一个节点拉取数据,在自己的内存里面进行聚合,然后再暴露相应的 service,供上层的消费者进行使用。在 K8s 中比较常见的消费者,类似像 dashboard,或者是 HPA-Controller,它通过调用 service 获取相应的监控数据,来实现相应的弹性伸缩,以及监控数据的一个展示。

这是以前的一个数据消费链路,这条消费链路看上去很清晰,也没有太多的一个问题,那为什么 Kubernetes 会将 Heapster 放弃掉而转换到 metrics-service 呢?主要的一个动力来源是由于 Heapster 在做监控数据接口的标准化,所以K8s把Heapster放弃,然后做了一个精简版的监控采集组件,叫做 metrics-server。

metrics-server 大致的结构如上图那样,是非常简单的:有一个core层、中间的 source 层,以及简单的 API 层,额外增加了 API Registration 这层。这层的作用就是它可以把相应的数据接口注册到 K8s 的 API server 之上,以后客户不再需要通过API层去访问metrics-server,而是可以通过这个API注册层,通过API server访问API注册层,再到metrics-server。这样的话,真正的数据消费方可能感知到的并不是一个 metrics-server,而感知到的是实现了这样一个 API 的具体的实现,而这个具体实现就是 metrics-server。这就是 metrics-server 改动最大的一个地方。

三. Kubernetes 的监控接口标准

在 K8s 里面针对于监控,有三种不同的接口标准。它将监控的数据消费能力进行了标准化和解耦,实现了一个与社区的融合,社区里面主要分为三类:

3.1 Resource Metrice

对应的接口是 metrics.k8s.io,主要的实现就是 metrics-server,它提供的是资源的监控,比较常见的是节点级别、pod 级别、namespace 级别、class 级别。这类的监控指标都可以通过 metrics.k8s.io 这个接口获取到。

3.2 Custom Metrics

对应的 API 是 custom.metrics.k8s.io,主要的实现是 Prometheus。它提供的是资源监控和自定义监控,资源监控和上面的资源监控其实是有覆盖关系的,而这个自定义监控指的是:比如应用上面想暴露一个类似像在线人数,或者说调用后面的这个数据库的 MySQL 的慢查询。这些其实都是可以在应用层做自己的定义的,然后并通过标准的 Prometheus 的 client,暴露出相应的 metrics,然后再被 Prometheus 进行采集。

而这类的接口一旦采集上来也是可以通过类似像 custom.metrics.k8s.io 这样一个接口的标准来进行数据消费的,也就是说现在如果以这种方式接入的 Prometheus,那你就可以通过 custom.metrics.k8s.io 这个接口来进行 HPA,进行数据消费。

.3.3 External Metrics

External Metrics 其实是比较特殊的一类,因为我们知道 K8s 现在已经成为了云原生接口的一个实现标准。很多时候在云上打交道的是云服务,比如说在一个应用里面用到了前面的是消息队列,后面的是 RBS 数据库。那有时在进行数据消费的时候,同时需要去消费一些云产品的监控指标,类似像消息队列中消息的数目,或者是接入层 SLB 的 connection 数目,SLB 上层的 200 个请求数目等等,这些监控指标。

那怎么去消费呢?也是在K8s里面实现了一个标准,就是 external.metrics.k8s.io。主要的实现厂商就是各个云厂商的provider,通过这个provider 实现云资源的监控指标。

四. Promethues - 开源社区的监控“标准”

下面来看一个比较常见的开源社区里面的监控方案,就是 Prometheus。Prometheus 为什么说是开源社区的监控标准呢?

  • 一是因为 Prometheus 是 CNCF 云原生社区的一个毕业项目。现在有越来越多的开源项目都以 Prometheus 作为监控标准,比较常见的 Spark、Tensorflow、Flink 这些项目,都有标准的 Prometheus 的采集接口。
  • 第二个是对于比较常见的一些数据库、中间件这类的项目,它都有相应的 Prometheus 采集客户端。类似像 ETCD、zookeeper、MySQL 或者说 PostgreSQL,都有相应的 Prometheus 的接口,如果没有,社区里面也会有相应的 exporter 进行接口的一个实现。

4.1 特征

Prometheus 相比于其他传统监控工具主要有以下几个特点:

  • 多维数据模型,时间序列由metric名字和K/V标签标识
  • 灵活的查询语言(PromQL)
  • 单机模式,不依赖分布式存储
  • 基于HTTP采用pull方式收集数据
  • 支持push数据到中间件(pushgateway)
  • 通过服务发现或静态配置发现目标
  • 多种图表和仪表盘

4.2 组件

Prometheus 由多个组件组成,但是其中许多组件是可选的:

  • Prometheus Server:用于抓取指标、存储时间序列数据
  • exporter:暴露指标让任务来抓
  • pushgateway:push 的方式将指标数据推送到该网关
  • alertmanager:处理报警的报警组件
  • adhoc:用于数据查询

大多数 Prometheus 组件都是用 Go 编写的,因此很容易构建和部署为静态的二进制文件。

4.3 架构

下图是 Prometheus 官方提供的架构及其一些相关的生态系统组件:

整体流程比较简单,Prometheus 直接接收或者通过中间的 Pushgateway 网关被动获取指标数据,在本地存储所有的获取的指标数据,并对这些数据进行一些规则整理,用来生成一些聚合数据或者报警信息,Grafana 或者其他工具用来可视化这些数据。

什么时候合适?

Prometheus非常适合记录任何纯数字时间序列。它既适合以机器为中心的监视,也适合于高度动态的面向服务的体系结构的监视。在微服务世界中,它对多维数据收集和查询的支持是一种特别的优势。

Prometheus的设计旨在提高可靠性,使其成为中断期间要使用的系统,以使您能够快速诊断问题。每个Prometheus服务器都是独立的,而不依赖于网络存储或其他远程服务。当基础结构的其他部分损坏时,您可以依靠它,并且无需设置广泛的基础结构即可使用它。

什么时候不合适?

普罗米修斯重视可靠性。即使在故障情况下,您始终可以查看有关系统的可用统计信息。如果您需要100%的准确性(例如按请求计费),则Prometheus并不是一个不错的选择,因为所收集的数据可能不会足够详细和完整。在这种情况下,最好使用其他系统来收集和分析数据以进行计费,并使用Prometheus进行其余的监视。

4.4 安装

prometheus的安装方式有很多,为了兼容k8s环境,我们将prometheus搭建在k8s里,除了使用docker镜像的方式安装,还可以使用二进制的方式进行安装,支持mac、Linux、windows。

准备采用NFS挂载方式来存储数据,同时使用configMap管理配置文件,使用kube-system名称空间。

##建议将所有的prometheus yaml文件放到一个目录中
[root@k8s-master ~]# mkdir /opt/prometheus 
[root@k8s-master ~]# cd /opt/prometheus/

创建configmap来存放配置文件

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: kube-system
data:
  prometheus.yml: |
    global:
      scrape_interval: 15s
      scrape_timeout: 15s
    scrape_configs:
    - job_name: 'prometheus'
      static_configs:
      - targets: ['localhost:9090']

其中 prometheus.yml 文件的基本配置如下:

global:
  scrape_interval:     15s
  evaluation_interval: 15s

rule_files:
  # - "first.rules"
  # - "second.rules"

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ['localhost:9090']

如果是二进制安装Prometheus,则可以使用以下命令来启动

$ ./prometheus --config.file=prometheus.yml

配置文件解释(这里的configmap实际上就是prometheus的配置)上面包含了3个模块global、rule_files和scrape_configs。

其中global模块控制Prometheus Server的全局配置。

  • scrape_interval:表示prometheus抓取指标数据的频率,默认是15s,我们可以覆盖这个值;
  • evaluation_interval:用来控制评估规则的频率,prometheus使用规则产生新的时间序列数据或者产生警报;

rule_files模块制定了规则所在的位置,prometheus可以根据这个配置加载规则,用于生产新的时间序列数据或者报警信息,当前没有配置任何规则,后期会添加;

scrape_configs 用于控制 prometheus 监控哪些资源。由于 prometheus 通过 HTTP 的方式来暴露的它本身的监控数据,prometheus 也能够监控本身的健康情况。在默认的配置里有一个单独的 job,叫做prometheus,它采集 prometheus 服务本身的时间序列数据。这个 job 包含了一个单独的、静态配置的目标:监听 localhost 上的9090端口。prometheus 默认会通过目标的/metrics路径采集 metrics。所以,默认的 job 通过 URL:http://localhost:9090/metrics 采集 metrics。收集到的时间序列包含 prometheus 服务本身的状态和性能。如果我们还有其他的资源需要监控的话,直接配置在该模块下面就可以了。

创建configmap资源并检查:

[root@k8s-master prometheus]# kubectl apply -f prometheus_configmap.yaml 
configmap/prometheus-config created

[root@k8s-master prometheus]# kubectl get configmap -n kube-system
NAME                                 DATA   AGE
coredns                              1      34d
extension-apiserver-authentication   6      34d
kube-flannel-cfg                     2      33d
kube-proxy                           2      34d
kubeadm-config                       2      34d
kubelet-config-1.17                  1      34d
prometheus-config                    1      13s

配置文件创建完成,如果以后我们有新的资源需要被监控,我们只需要将ConfigMap对象更新即可,现在我们开始创建prometheus的Pod资源。

要使用NFS存储实现数据持久化,所以需要先构建NFS Server,选择集群中任意一台服务器来安装,或者有单独的服务器也可以。

##我这里选择了node2节点来作为NFS Server
[root@k8s-node2 ~]# yum install nfs-utils -y

[root@k8s-node2 ~]# mkdir /data/k8s/prometheus -p
[root@k8s-node2 ~]# cat /etc/exports
/data/k8s/prometheus 192.168.154.0/24(rw,sync,no_root_squash)

[root@k8s-node2 ~]# systemctl enable nfs-server.service
[root@k8s-node2 ~]# systemctl start nfs-server.service

## 关闭防火墙或者放行nfs服务
[root@k8s-node2 ~]# firewall-cmd --add-service=nfs --permanent 
[root@k8s-node2 ~]# firewall-cmd --add-service=rpc-bind --permanent
[root@k8s-node2 ~]# firewall-cmd --add-service=mountd --permanent 
[root@k8s-node2 ~]# firewall-cmd --reload 
success

为了使用PV存储数据,所以需要先创建PV及PVC

apiVersion: v1
kind: PersistentVolume
metadata:
  name: prometheus
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    server: 192.168.154.220     ##修改成你自己NFS地址
    path: /data/k8s/prometheus

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: prometheus
  namespace: kube-system
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
##创建的pv和pvc大小都是10g,只是测试存储为10g。线上可以修改为200或者更多;
##一般prometheus数据保留15-30天就可以,如果数据量过大建议使用TSBD分布式存储

创建资源并查看是否生效:

[root@k8s-master prometheus]# kubectl apply -f prometheus_volume.yaml 
persistentvolume/prometheus created
persistentvolumeclaim/prometheus created

[root@k8s-master prometheus]# kubectl get pvc -n kube-system
NAME         STATUS   VOLUME       CAPACITY   ACCESS MODES   STORAGECLASS   AGE
prometheus   Bound    prometheus   10Gi       RWO                           12s

[root@k8s-master prometheus]# kubectl get pv -n kube-system
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   REASON   AGE
prometheus   10Gi       RWO            Recycle          Bound    kube-system/prometheus                           17s

创建RBAC认证,因为prometheus需要访问k8s集群内部的资源。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: prometheus
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
rules:
- apiGroups:
  - ""
  resources:
  - nodes
  - services
  - endpoints
  - pods
  - nodes/proxy
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - configmaps
  - nodes/metrics
  verbs:
  - get
- nonResourceURLs:
  - /metrics
  verbs:
  - get
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: prometheus
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: prometheus
subjects:
- kind: ServiceAccount
  name: prometheus
  namespace: kube-system

##由于要获取的资源,在每一个namespace下面都有可能存在,所以这里使用的是ClusterRole的资源对象;
##nonResourceURLs是用来对非资源型metrics进行操作的权限声明

创建rbac yaml文件中的资源

[root@k8s-master prometheus]# kubectl apply -f prometheus_rbac.yaml
serviceaccount/prometheus created
clusterrole.rbac.authorization.k8s.io/prometheus created
clusterrolebinding.rbac.authorization.k8s.io/prometheus created

创建prometheus的Pod资源

apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus
  namespace: kube-system
  labels:
    app: prometheus
spec:
  selector:
    matchLabels:
      app: prometheus
  template:
    metadata:
      labels:
        app: prometheus
    spec:
      serviceAccountName: prometheus
      containers:
      - image: prom/prometheus:v2.17.1
        name: prometheus
        command:
        - "/bin/prometheus"
        args:
        - "--config.file=/etc/prometheus/prometheus.yml"
        - "--storage.tsdb.path=/prometheus"
        - "--storage.tsdb.retention=30d"
        - "--web.enable-admin-api" 
        - "--web.enable-lifecycle" 
        ports:
        - containerPort: 9090
          protocol: TCP
          name: http
        volumeMounts:
        - mountPath: "/prometheus"
          subPath: prometheus
          name: data
        - mountPath: "/etc/prometheus"
          name: config-volume
        resources:
          requests:
            cpu: 100m
            memory: 512Mi
          limits:
            cpu: 100m
            memory: 512Mi
      securityContext:
        runAsUser: 0    
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: prometheus
      - configMap:
          name: prometheus-config
        name: config-volume

创建资源,并检查是否成功:

[root@k8s-master prometheus]# kubectl apply -f prometheus_deploy.yaml 
deployment.apps/prometheus created

[root@k8s-master prometheus]# kubectl get deployment -n kube-system
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
coredns      2/2     2            2           34d
prometheus   1/1     1            1           59s

[root@k8s-master prometheus]# curl http://10.244.1.103:9090
<a href="/graph">Found</a>.

现在我们prometheus服务状态是已经正常了,但是我们在浏览器是无法访问prometheus的 webui服务。那么我们还需要创建一个service:

apiVersion: v1
kind: Service
metadata:
  name: prometheus
  namespace: kube-system
  labels:
    app: prometheus
spec:
  selector:
    app: prometheus
  type: NodePort
  ports:
    - name: web
      port: 9090
      targetPort: http

这里使用的是NodePort,也可以创建一个Ingress对象使用域名访问。创建资源并检查:

[root@k8s-master prometheus]# kubectl apply -f prometheus_svc.yaml 
service/prometheus created

[root@k8s-master prometheus]# kubectl get svc -n kube-system
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                  AGE
kube-dns     ClusterIP   10.1.0.10      <none>        53/UDP,53/TCP,9153/TCP   34d
prometheus   NodePort    10.1.145.131   <none>        9090:32021/TCP           12s

在浏览器上输入任意节点ip+端口即可:

基本status界面:

默认prometheus会监控自己:Status-->Targets

查看一下,看看是否收集到数据

选择scrape_duration_seconds这个指标,然后点击Execute,如果这个时候没有查询到任何数据,我们可以切换到Graph这个 tab 下面重新选择下时间,选择到当前的时间点,重新执行,就可以看到类似于下面的图表数据了:

监控kubernetes

Prometheus的数据指标是通过一个公开的 HTTP(S) 数据接口获取到的,不需要单独安装监控的 agent,只需要暴露一个 metrics 接口,Prometheus 就会定期去拉取数据;对于一些普通的 HTTP 服务,我们完全可以直接重用这个服务,添加一个/metrics接口暴露给 Prometheus;而且获取到的指标数据格式是非常易懂的,不需要太高的学习成本。

现在很多服务从一开始就内置了一个/metrics接口,比如 Kubernetes 的各个组件、istio 服务网格都直接提供了数据指标接口。有一些服务即使没有原生集成该接口,也完全可以使用一些 exporter 来获取到指标数据,比如 mysqld_exporter、node_exporter,这些 exporter 就有点类似于传统监控服务中的 agent,作为服务一直存在,用来收集目标服务的指标数据然后直接暴露给 Prometheus。

一. 普通应用监控

我们前面学习了ingress的使用,我们使用的是Nginx的ingress控制器,如何让prometheus来监控它呢?

nginx ingress 官方有相应的yaml文件。修改prometheus的configmap,增加对ingress nginx的监控配置即可

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: kube-system
data:
  prometheus.yml: |
    global:
      scrape_interval: 15s
      scrape_timeout: 15s
    scrape_configs:
    - job_name: 'prometheus'
      static_configs:
      - targets: ['localhost:9090']
    - job_name: 'ingress-nginx-endpoints'
      kubernetes_sd_configs:
      - role: pod
        namespaces:
          names:
          - ingress-nginx
      relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme]
        action: replace
        target_label: __scheme__
        regex: (https?)
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
      - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        target_label: __address__
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
      - source_labels: [__meta_kubernetes_service_name]
        regex: prometheus-server
        action: drop

重新apply configmap即可,然后重启prometheus的Pod。

kubectl replace -f prometheus_configmap.yaml

打开prometheus界面,查看target中是否有ingress nginx的相关记录:

可以看到一些数据

二. 使用 exporter 监控应用

有一些应用可能没有自带/metrics接口供 Prometheus 使用,在这种情况下,我们就需要利用 exporter 服务来为 Prometheus 提供指标数据了。Prometheus 官方为许多应用就提供了对应的 exporter 应用,也有许多第三方的实现,我们可以前往官方网站进行查看:https://prometheus.io/docs/instrumenting/exporters/

下面通过一个 redis-exporter 的服务来监控 redis 服务,对于这类应用,我们一般会以 sidecar 的形式和主应用部署在同一个 Pod 中,比如我们这里来部署一个 redis 应用,并用 redis-exporter 的方式来采集监控数据供 Prometheus 使用,如下资源清单文件:(prome-redis.yaml)

apiVersion: v1
kind: Namespace
metadata:
  name: kube-ops
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: kube-ops
spec:
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9121"
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:4
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379
      - name: redis-exporter
        image: oliver006/redis_exporter:latest
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 9121
---
kind: Service
apiVersion: v1
metadata:
  name: redis
  namespace: kube-ops
spec:
  selector:
    app: redis
  ports:
  - name: redis
    port: 6379
    targetPort: 6379
  - name: prom
    port: 9121
    targetPort: 9121

创建资源并进行检查

[root@k8s-master k8s-yaml]# kubectl apply -f prome-redis.yaml
namespace/kube-ops created
deployment.apps/redis created
service/redis created

##创建完成后,我们可以看到 redis 的 Pod 里面包含有两个容器:
[root@k8s-master k8s-yaml]# kubectl get pod -n kube-ops
NAME                     READY   STATUS    RESTARTS   AGE
redis-7c8bdd45cc-l6ntb   2/2     Running   0          19s

[root@k8s-master k8s-yaml]# kubectl get svc -n kube-ops
NAME    TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)             AGE
redis   ClusterIP   10.1.250.7   <none>        6379/TCP,9121/TCP   78s

可以通过 9121 端口来校验是否能够采集到数据:

[root@k8s-master k8s-yaml]# curl http://10.1.250.7:9121/metrics
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 8
...略

更新 Prometheus configmap的配置文件

    - job_name: 'redis_exporter'
      static_configs:
      - targets: ['redis.kube-ops:9121']
##如何是在同一namespace中,只写服务名redis即可

配置文件更新后,重新加载prometheus服务:

[root@k8s-master prometheus]# kubectl replace -f prometheus_configmap.yaml

[root@k8s-master prometheus]# kubectl get svc -n kube-system
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                  AGE
kube-dns     ClusterIP   10.1.0.10      <none>        53/UDP,53/TCP,9153/TCP   36d
prometheus   NodePort    10.1.145.131   <none>        9090:32021/TCP           2d
[root@k8s-master prometheus]# curl -X POST "http://10.1.145.131:9090/-/reload"

再去到 Prometheus 的 Dashboard 中查看采集的目标数据:

选择任意一个指标,比如redis_exporter_scrapes_total,然后点击执行就可以看到对应的数据图表了:

三. 监控 Kubernetes 集群节点

我们需要时时刻刻了解集群的运行状态,所以对于 Kubernetes 集群本身的监控也是非常重要的,对于集群的监控一般我们需要考虑以下几个方面:

  • Kubernetes 节点的监控:比如节点的 cpu、load、disk、memory 等指标
  • 内部系统组件的状态:比如 kube-scheduler、kube-controller-manager、kubedns/coredns 等组件的详细运行状态
  • 编排级的 metrics:比如 Deployment 的状态、资源请求、调度和 API 延迟等数据指标

3.1 监控集群节点

首先来监控集群的节点,要监控节点其实已经有很多非常成熟的方案了,比如 Nagios、zabbix,甚至自己来收集数据也可以,我们这里通过 Prometheus 来采集节点的监控指标数据,可以通过 node_exporter 来获取,顾名思义,node_exporter就是抓取用于采集服务器节点的各种运行指标,目前 node_exporter 支持几乎所有常见的监控点,比如 conntrack,cpu,diskstats,filesystem,loadavg,meminfo,netstat等,详细的监控点列表可以参考其: https://github.com/prometheus/node_exporter

可以通过 DaemonSet 控制器来部署该服务,这样每一个节点都会自动运行一个这样的 Pod,如果我们从集群中删除或者添加节点后,也会进行自动扩展。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: kube-ops
  labels:
    name: node-exporter
spec:
  selector:
    matchLabels:
      name: node-exporter
  template:
    metadata:
      labels:
        name: node-exporter
    spec:
      hostPID: true
      hostIPC: true
      hostNetwork: true
      containers:
      - name: node-exporter
        image: prom/node-exporter:v0.18.0
        ports:
        - containerPort: 9100
        resources:
          requests:
            cpu: 0.15
        securityContext:
          privileged: true
        args:
        - --path.procfs
        - /host/proc
        - --path.sysfs
        - /host/sys
        - --collector.filesystem.ignored-mount-points
        - '"^/(sys|proc|dev|host|etc)($|/)"'
        volumeMounts:
        - name: dev
          mountPath: /host/dev
        - name: proc
          mountPath: /host/proc
        - name: sys
          mountPath: /host/sys
        - name: rootfs
          mountPath: /rootfs
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"
        effect: "NoSchedule"
      volumes:
        - name: proc
          hostPath:
            path: /proc
        - name: dev
          hostPath:
            path: /dev
        - name: sys
          hostPath:
            path: /sys
        - name: rootfs
          hostPath:
            path: /
  • 由于要获取到的数据是主机的监控指标数据,而 node-exporter 是运行在容器中的,所以在 Pod 中需要配置一些 Pod 的安全策略,这里我们就添加了hostPID: true、hostIPC: true、hostNetwork: true3个策略,用来使用主机的 PID namespace、IPC namespace 以及主机网络,这些 namespace 就是用于容器隔离的关键技术,要注意这里的 namespace 和集群中的 namespace 是两个完全不相同的概念。
  • 另外还将主机的/dev、/proc、/sys这些目录挂载到容器中,这些因为采集的很多节点数据都是通过这些文件夹下面的文件来获取到的,比如使用top命令可以查看当前cpu使用情况,数据就来源于文件/proc/stat,使用free命令可以查看当前内存使用情况,其数据来源是来自/proc/meminfo文件。
  • 另外由于集群使用的是 kubeadm 搭建的,所以如果希望 master 节点也一起被监控,则需要添加相应的容忍。

然后直接创建上面的资源对象即可:

[root@k8s-master k8s-yaml]# kubectl apply -f prome-node-exporter.yaml
daemonset.apps/node-exporter created

[root@k8s-master k8s-yaml]# kubectl get pods -n kube-ops -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP                NODE         NOMINATED NODE   READINESS GATES
node-exporter-8fr4l      1/1     Running   0          47s     192.168.154.200   k8s-master   <none>           <none>
node-exporter-vbmdw      1/1     Running   0          47s     192.168.154.210   k8s-node1    <none>           <none>
node-exporter-x4jsk      1/1     Running   0          47s     192.168.154.220   k8s-node2    <none>           <none>

由于指定了hostNetwork=true,所以在每个节点上就会绑定一个端口 9100,可以通过这个端口去获取到监控指标数据:

[root@k8s-master k8s-yaml]# curl 127.0.0.1:9100/metrics
...略
node_cpu_seconds_total{cpu="0",mode="idle"} 55567.25
node_cpu_seconds_total{cpu="0",mode="iowait"} 24.85
node_cpu_seconds_total{cpu="0",mode="irq"} 0
node_cpu_seconds_total{cpu="0",mode="nice"} 0.02
node_cpu_seconds_total{cpu="0",mode="softirq"} 145.77
...略

3.2 服务发现

由于这里3个节点上面都运行了 node-exporter 程序,如果通过一个 Service 来将数据收集到一起用静态配置的方式配置到 Prometheus 去中,就只会显示一条数据,还得自己在指标数据中去过滤每个节点的数据,那么有没有一种方式可以让 Prometheus 去自动发现我们节点的 node-exporter 程序,并且按节点进行分组呢?服务发现就可以做到这一点。

在 Kubernetes 下,Promethues 通过与 Kubernetes API 集成,目前主要支持5种服务发现模式,分别是:

  • Node
  • Service
  • Pod
  • Endpoints
  • Ingress

这里使用Node的服务发现模式,修改Prometheus的configmap配置

    - job_name: 'kubernetes-nodes'
      kubernetes_sd_configs:
      - role: node

通过指定kubernetes_sd_configs的模式为node,Prometheus 就会自动从 Kubernetes 中发现所有的 node 节点并作为当前 job 监控的目标实例,发现的节点/metrics接口是默认的 kubelet 的 HTTP 接口。

prometheus 的 ConfigMap 更新完成后,执行 reload 操作,让配置生效:

[root@k8s-master prometheus]# kubectl replace -f prometheus_configmap.yaml
configmap/prometheus-config replaced

[root@k8s-master prometheus]# kubectl get svc -n kube-system
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                  AGE
kube-dns     ClusterIP   10.1.0.10      <none>        53/UDP,53/TCP,9153/TCP   37d
prometheus   NodePort    10.1.145.131   <none>        9090:32021/TCP           2d6h
[root@k8s-master prometheus]# curl -X POST "http://10.1.145.131:9090/-/reload"

配置生效后,我们再去 prometheus 的 dashboard 中查看 Targets 是否能够正常抓取数据

可以看到上面的kubernetes-nodes这个 job 任务已经自动发现了我们3个 node 节点,但是在获取数据的时候失败了。

这是因为 prometheus 去发现 Node 模式的服务的时候,访问的端口默认是10250,而应该是要去配置上面通过node-exporter抓取到的节点指标数据,而我们是通过指定 hostNetwork=true,在每个节点上绑定一个端口9100,所以应该将这里的10250替换成9100,但应该怎样替换呢?

这里就需要使用到 Prometheus 提供的relabel_configs中的replace能力了,relabel 可以在 Prometheus 采集数据之前,通过Target 实例的 Metadata 信息,动态重新写入 Label 的值。除此之外,还能根据 Target 实例的 Metadata 信息选择是否采集或者忽略该 Target 实例。比如这里就可以去匹配__address__这个 Label 标签,然后替换掉其中的端口:

    - job_name: 'kubernetes-nodes'
      kubernetes_sd_configs:
      - role: node
      relabel_configs:
      - source_labels: [__address__]
        regex: '(.*):10250'
        replacement: '${1}:9100'
        target_label: __address__
        action: replace

这里就是一个正则表达式,去匹配address,然后将 host 部分保留下来,port 替换成了9100,现在重新更新配置文件,执行 reload 操作,然后再去看 Prometheus 的 Dashboard 的 Targets 路径下面 kubernetes-nodes 这个 job 任务是否正常了:

还有一个问题就是采集的指标数据 Label 标签就只有一个节点的 hostname 内容,其余的就没有了,这对于我们在进行监控分组分类查询的时候带来了很多不方便的地方,如何才能将集群中 Node 节点的 Label 标签获取到呢?

可以通过labelmap这个属性来将 Kubernetes 的 Label 标签添加为 Prometheus 的指标标签:

    - job_name: 'kubernetes-nodes'
      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_(.+)

添加了一个 action 为labelmap,正则表达式是__meta_kubernetes_node_label_(.+)的配置,这里的意思就是表达式中匹配到的数据也添加到指标数据的 Label 标签中去。

对于 kubernetes_sd_configs 下面可用的标签如下: (可用元标签)

  • __meta_kubernetes_node_name:节点对象的名称
  • _meta_kubernetes_node_label:节点对象中的每个标签
  • _meta_kubernetes_node_annotation:来自节点对象的每个注释
  • _meta_kubernetes_node_address:每个节点地址类型的第一个地址(如果存在)

参考:https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config

由于 kubelet 也自带了一些监控指标数据,所以这里也把 kubelet 的监控任务也一并配置上:

    - job_name: 'kubernetes-kubelet'
      kubernetes_sd_configs:
      - role: node
      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
      relabel_configs:
      - action: labelmap
        regex: __meta_kubernetes_node_label_(.+)

再去更新下配置文件configmap,执行 reload 操作,让配置生效,然后访问 Prometheus 的 Dashboard 查看 Targets 路径:

可以看到上面添加的kubernetes-kubelet和kubernetes-nodes这两个 job 任务都已经配置成功了,而且二者的 Labels 标签都和集群的 node 节点标签保持一致了。

可以看到将3个 node 节点对应的 node_load1 指标数据都查询出来了,还可以使用 PromQL 语句来进行更复杂的一些聚合查询操作,还可以根据 Labels 标签对指标数据进行聚合,比如只查询 node03 节点的数据,可以使用表达式node_load1{instance=“k8s-node2”}来进行查询:

四. 监控 Kubernetes 常用资源对象

使用 Prometheus 来自动发现 Kubernetes 集群的节点,用到了 Prometheus 针对 Kubernetes 的服务发现机制 kubernetes_sd_configs 的使用,下面来学习如何在 Prometheus 中来自动监控 Kubernetes 中的一些常用资源对象。

可以在 Prometheus 中用静态的方式来监控 Kubernetes 集群中的普通应用,但是如果针对集群中众多的资源对象都采用静态的方式来进行配置的话显然是不现实的,所以我们需要使用到 Prometheus 提供的其他类型的服务发现机制。

4.1 容器监控

说到容器监控我们自然会想到cAdvisor,cAdvisor已经内置在了 kubelet 组件之中,所以不需要单独去安装,cAdvisor的数据路径为/api/v1/nodes//proxy/metrics,同样可以使用 node 的服务发现模式,因为每一个节点下面都有 kubelet,自然都有cAdvisor采集到的数据指标,配置如下:

- job_name: 'kubernetes-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

上面的配置和之前配置 node-exporter 的时候几乎是一样的,区别是我们这里使用了 https 的协议,另外需要注意的是配置了 ca.cart 和 token 这两个文件,这两个文件是 Pod 启动后自动注入进来的,通过这两个文件我们可以在 Pod 中访问 apiserver,这里的address不在是 nodeip 了,而是 kubernetes 在集群中的服务地址,然后加上metrics_path的访问路径:/api/v1/nodes/${1}/proxy/metrics/cadvisor

更新下配置文件configmap,执行 reload 操作,让配置生效,然后访问 Prometheus 的 Dashboard 查看 Targets 路径:

查看图形:

4.2 apiserver 监控

apiserver 作为 Kubernetes 最核心的组件,对它的监控是非常有必要的,对于 apiserver 的监控我们可以直接通过 kubernetes 的 Service 来获取:

[root@k8s-master prometheus]# kubectl get svc
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.1.0.1      <none>        443/TCP   38d
my-service   ClusterIP   10.1.251.21   <none>        80/TCP    18d

上面这个名字为kubernetes的 Service 就是我们集群的 apiserver 在集群内部的 Service 地址,要自动发现 Service 类型的服务,我们就需要用到 role 为 Endpoints 的 kubernetes_sd_configs,我们可以在 ConfigMap 对象中添加上一个 Endpoints 类型的服务的监控任务:

    - job_name: 'kubernetes-apiservers'
      kubernetes_sd_configs:
      - role: endpoints

更新configmap配置,重新加载,这样会监控到所有的 endpoints,所以需要过滤出我们想监控的endpoints来。

需要使用relabel_configs这个配置,只是不是使用replace这个动作,而是keep,就是只把符合要求的给保留下来,哪些才是符合我们要求的呢?可以把鼠标放置在任意一个 target 上,可以查看到Before relabeling里面所有的元数据,比如我们要过滤的服务是 default 这个 namespace 下面,服务名为 kubernetes 的元数据,所以这里我们就可以根据对应的__meta_kubernetes_namespace__meta_kubernetes_service_name这两个元数据来 relabel。

另外由于 kubernetes 这个服务对应的端口是443,需要使用 https 协议,所以这里我们需要使用 https 的协议,对应的就需要将对应的 ca 证书配置上:

    - job_name: 'kubernetes-apiservers'
      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

重新更新配置文件、重新加载 Prometheus,切换到 Prometheus 的 Targets 路径下查看:

查看图形:

4.3 Service 的监控

apiserver 实际上就是一种特殊的 Service,现在来配置一个任务用来专门发现普通类型的 Service:

    - 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

relabel_configs区域做了大量的配置,特别是第一个保留__meta_kubernetes_service_annotation_prometheus_io_scrape为true的才保留下来,这就是说要想自动发现集群中的 Service,就需要在 Service 的annotation区域添加prometheus.io/scrape=true的声明,现在我们先将上面的配置更新,查看下效果:

上面出现了2个,这是kube-dns服务,在kube-system名称空间中,查看其annotation,可以看到 prometheus.io/scrape: "true"

[root@k8s-master prometheus]# kubectl get svc -n kube-system kube-dns -o yaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/port: "9153"
    prometheus.io/scrape: "true"
  creationTimestamp: "2020-03-13T09:43:52Z"
...略

现在我们在之前创建的 redis 这个 Service 中添加上prometheus.io/scrape=true这个 annotation:(prome-redis.yaml)

kind: Service
apiVersion: v1
metadata:
  name: redis
  namespace: kube-ops
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "9121"
spec:
  selector:
    app: redis
  ports:
  - name: redis
    port: 6379
    targetPort: 6379
  - name: prom
    port: 9121
    targetPort: 9121

由于 redis 服务的 metrics 接口在9121这个 redis-exporter 服务上面,所以我们还需要添加一个prometheus.io/port=9121这样的annotations,然后更新这个 Service,可以看到其已经被监控。

这样以后有了新的服务,且服务本身提供了/metrics接口,我们就完全不需要用静态的方式去配置了,到这里我们就可以将之前配置的 redis 的静态配置去掉了。

4.4 kube-state-metrics

上面配置了自动发现 Service(Pod也是一样的)的监控,但是这些监控数据都是应用内部的监控,需要应用本身提供一个/metrics接口,或者对应的 exporter 来暴露对应的指标数据,但是在 Kubernetes 集群上 Pod、DaemonSet、Deployment、Job、CronJob 等各种资源对象的状态也需要监控,这也反映了使用这些资源部署的应用的状态。但通过查看前面从集群中拉取的指标(这些指标主要来自 apiserver 和 kubelet 中集成的 cAdvisor),并没有具体的各种资源对象的状态指标。对于 Prometheus 来说,当然是需要引入新的 exporter 来暴露这些指标。Kubernetes 提供了一个 kube-state-metrics 就是我们需要的。

  • 我调度了多少个replicas?现在可用的有几个?
  • 多少个Pod是running/stopped/terminated状态?
  • Pod重启了多少次?
  • 我有多少job在运行中

而这些则是kube-state-metrics提供的内容,它基于client-go开发,轮询Kubernetes API,并将Kubernetes的结构化信息转换为metrics。

其能监控的指标类别包括:

  • CronJob Metrics
  • DaemonSet Metrics
  • Deployment Metrics
  • Job Metrics
  • LimitRange Metrics
  • Node Metrics
  • PersistentVolume Metrics
  • PersistentVolumeClaim Metrics
  • Pod Metrics
  • Pod Disruption Budget Metrics
  • ReplicaSet Metrics
  • ReplicationController Metrics
  • ResourceQuota Metrics
  • Service Metrics
  • StatefulSet Metrics
  • Namespace Metrics
  • Horizontal Pod Autoscaler Metrics
  • Endpoint Metrics
  • Secret Metrics
  • ConfigMap Metrics

以pod为例:

  • kube_pod_info
  • kube_pod_owner
  • kube_pod_status_phase
  • kube_pod_status_ready
  • kube_pod_status_scheduled
  • kube_pod_container_status_waiting
  • kube_pod_container_status_terminated_reason

kube-state-metrics 已经给出了在 Kubernetes 部署的 manifest 定义文件,我们直接将代码 Clone 到集群中部署即可:

##下载源码
[root@k8s-master k8s-yaml]# git clone https://github.com/kubernetes/kube-state-metrics.git

##部署
[root@k8s-master k8s-yaml]# cd kube-state-metrics/
[root@k8s-master kube-state-metrics]# kubectl apply -f examples/standard
clusterrolebinding.rbac.authorization.k8s.io/kube-state-metrics created
clusterrole.rbac.authorization.k8s.io/kube-state-metrics created
deployment.apps/kube-state-metrics created
serviceaccount/kube-state-metrics created
service/kube-state-metrics created

##检查
[root@k8s-master prometheus]# kubectl get svc -n kube-system 
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                  AGE
kube-dns             ClusterIP   10.1.0.10      <none>        53/UDP,53/TCP,9153/TCP   39d
kube-state-metrics   ClusterIP   None           <none>        8080/TCP,8081/TCP        38m
prometheus           NodePort    10.1.145.131   <none>        9090:32021/TCP           4d15h

修改prometheus的configmap配置,并重新加载

    - job_name: 'kube-state-metrics'
      kubernetes_sd_configs:
      - role: endpoints
        namespaces:
          names:
          - kube-system
      relabel_configs:
      - source_labels: [__meta_kubernetes_endpoints_name]
        action: keep
        regex: kube-state-metrics

在Prometheus中进行查看:

可以查看一些监控指标

关于 kube-state-metrics 暴露的所有监控指标可以参考 kube-state-metrics 的文档:https://github.com/kubernetes/kube-state-metrics/tree/master/docs

Grafana 的安装使用

Prometheus 的图表功能相对较弱,所以一般情况下会使用一个第三方的工具来展示这些数据,就是使用 grafana。

grafana 是一个可视化面板,有着非常漂亮的图表和布局展示,功能齐全的度量仪表盘和图形编辑器,支持 Graphite、zabbix、InfluxDB、Prometheus、OpenTSDB、Elasticsearch 等作为数据源,比 Prometheus 自带的图表展示功能强大太多,更加灵活,有丰富的插件,功能更加强大。

一. 安装

grafana 将 dashboard、插件这些数据保存在/var/lib/grafana这个目录下面的,所以这里如果需要做数据持久化的话,就需要针对这个目录进行 volume 挂载声明,使用了NFS存储类型的volumes,要注意修改成自己的NFS地址。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: "grafana-data-pv"
  labels:
    name: grafana-data-pv
    release: stable
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /data/k8s/grafana     ##修改成你自己的 NFS 共享目录
    server: 192.168.154.220     ##修改成你自己的 NFS 服务器地址
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: grafana-data-pvc
  namespace: kube-system
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  selector:
    matchLabels:
      name: grafana-data-pv
      release: stable
---
kind: Deployment
apiVersion: apps/v1
metadata:
  labels:
    app: grafana
  name: grafana
  namespace: kube-system
spec:
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: grafana
  template:
    metadata:
      labels:
        app: grafana
    spec:
      securityContext:
        runAsUser: 0
      containers:
        - name: grafana
          image: grafana/grafana:latest
          imagePullPolicy: IfNotPresent
          env:
            - name: GF_AUTH_BASIC_ENABLED
              value: "true"
            - name: GF_AUTH_ANONYMOUS_ENABLED
              value: "false"
          readinessProbe:
            httpGet:
              path: /login
              port: 3000
          volumeMounts:
            - mountPath: /var/lib/grafana
              name: grafana-data-volume
          ports:
            - containerPort: 3000
              protocol: TCP
      volumes:
        - name: grafana-data-volume
          persistentVolumeClaim:
            claimName: grafana-data-pvc
---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: grafana
  name: grafana-service
  namespace: kube-system
spec:
  ports:
    - port: 3000
      targetPort: 3000
  selector:
    app: grafana
  type: NodePort

创建资源并进行检查:

[root@k8s-master k8s-yaml]# kubectl apply -f grafana.yaml

[root@k8s-master k8s-yaml]# kubectl get svc -n kube-system
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                  AGE
grafana-service      NodePort    10.1.253.122   <none>        3000:32446/TCP           9m36s
kube-dns             ClusterIP   10.1.0.10      <none>        53/UDP,53/TCP,9153/TCP   39d
kube-state-metrics   ClusterIP   None           <none>        8080/TCP,8081/TCP        4h8m
prometheus           NodePort    10.1.145.131   <none>        9090:32021/TCP           4d19h

使用浏览器进行访问,默认用户名admin,密码admin,第一次登陆需要修改密码。

登录完成后就可以进入到下面 Grafana 的首页:

二. 配置

成功安装Grafana后,接下来点击Add data source进入添加数据源界面。

3.1 数据源

这个地方配置的数据源是 Prometheus,所以选择这个 Type 即可,给该数据源添加一个 name:prometheus-ds,最主要的是下面HTTP区域是配置数据源的访问模式。

访问模式是用来控制如何处理对数据源的请求的:

  • 服务器(Server)访问模式(默认):所有请求都将从浏览器发送到 Grafana 后端的服务器,后者又将请求转发到数据源,通过这种方式可以避免一些跨域问题,其实就是在 Grafana 后端做了一次转发,需要从Grafana 后端服务器访问该 URL。
  • 浏览器(Browser)访问模式:所有请求都将从浏览器直接发送到数据源,但是有可能会有一些跨域的限制,使用此访问模式,需要从浏览器直接访问该 URL。

由于我们的 Prometheus 是通过 NodePort 的方式的对外暴露的服务,如果使用浏览器访问模式直接访问 Prometheus 的外网地址,相当于走的是外网,不是最好的方式,由于 Prometheus 和 Grafana 都处于 kube-system 这同一个 namespace 下面,在集群内部直接通过 DNS 的形式就可以访问了,而且还都是走的内网流量,所以这里用服务器访问模式显然更好。

[root@k8s-master ~]# kubectl get svc -n kube-system
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                  AGE
grafana-service      NodePort    10.1.253.122   <none>        3000:32446/TCP           19h
kube-dns             ClusterIP   10.1.0.10      <none>        53/UDP,53/TCP,9153/TCP   40d
kube-state-metrics   ClusterIP   None           <none>        8080/TCP,8081/TCP        23h
prometheus           NodePort    10.1.145.131   <none>        9090:32021/TCP           5d14h

数据源地址:http://prometheus:9090(因为在同一个 namespace 下面所以直接用 Service 名也可以),然后其他的配置信息就根据实际情况了,比如 Auth 认证,我们这里没有,所以跳过即可,点击最下方的Save & Test提示成功证明我们的数据源配置正确:

数据源添加完成后,就可以来添加 Dashboard 了。

3.2 Dashboard

切换到主页,我们可以根据自己的需求手动新建一个 Dashboard,除此之外,grafana 的官方网站上还有很多公共的 Dashboard 可以供我们使用:https://grafana.com/grafana/dashboards

这里可以使用Node Exporter for Prometheus Dashboard CN v20191102(dashboard id 为8919)这个 Dashboard 来展示 Kubernetes 集群的监控信息。

选择import:

选择load:

选择 prometheus-ds 这个名字的数据源,执行 import 操作:

可以进入到 dashboard 页面:

除此之外,我们也可以前往 grafana dashboard 的页面去搜索其他的关于 Kubernetes 的监控页面,地址:https://grafana.com/dashboards, 比如id为747和741的这两个 dashboard,分别针对Pod和Deployment的监控。

3.3 添加用户

选择configuration->users->invite

输入用户信息,因为没有配置邮件服务器,所以不选择发送邮件。

复制设置密码的链接地址:

将地址栏地址设置正确,然后设置密码即可。