使用 Prometheus 监控 Kubernetes 集群

2019/8/4 posted in  Kubernetes

当你考虑基于 Kubernetes 的能力为自己的应用锦上添花的时候,就仿佛打开了一个潘多拉魔盒,你不知道这个盒子里到底有什么,就像你不知道你所依赖的 Kubernetes 集群和集群上的应用正在、将要发生什么。

无论选择什么架构,底层基于什么运行时,可观测性始终拥有极高的优先级。有一种说法是:如果你不知道怎么运维,就不要去尝试部署。这也算是一种非常朴实的,以终为始的思考方式。

话说回来,如果拥抱了 Kubernetes,那么我们所追求的 “可观测性” 是什么样子呢?对于微服务架构,我认为有几个方面可以作为及格线:

  1. 集群和应用状态的可观测性
  2. 集群和应用的日志
  3. 应用间流量、调用关系和请求状态的可观测性

简单来说,就是:监控、日志、跟踪,而 Prometheus 就是在 Kubernetes 监控比较成熟的解决方案。

Prometheus

Prometheus 一款基于时序数据库的开源监控告警系统,诞生自 SoundCloud 公司。Prometheus 的基本工作原理是通过 HTTP 协议,周期性的抓取被监控组件的状态,因此被监控组件只需要实现符合规范的 HTTP 接口便可以接入,对于默认没有提供 HTTP 接口的组件,比如 Linux、MySQL 等,Prometheus 支持使用 exporter 来收集信息,并代为提供 metrics 接口。

SoundCloud 公司博客上有一篇文章简单讲解了 Prometheus 架构以及工作原理。文章中认为 Prometheus 满足的四个特性:

  1. A multi-dimensional data model(多维度数据模型)
  2. Operational simplicity(方便的部署和维护)
  3. Scalable data collection and decentralized architecture(灵活的数据采集)
  4. A powerful query language(强大的查询语言)

第一项和第四项也是时序数据库的特点,但 Prometheus 为了方便部署,默认并没有内置任何额外的存储,而是选择自己实现,对于第四个特性 Prometheus 实现了 PromQL 查询语言,可以实现强大的查询规则。

随着版本的迭代,Prometheus 的特性早已不局限于此。

从 Prometheus 的架构图可以看到,有 4 个主要组件:

  1. Prometheus Server
  2. PushGateway
  3. AlertManager
  4. WebUI

其中只有 Prometheus Server 是最重要的组件,承担着数据的采集,Prometheus 采用 pull 的方式对被监控对象进行数据采集,但是如果需要被监控对象通过 Push 将自身的状态推送给 Prometheus,可以引入 PushGateway,被监控对象将主动的将状态 Push 到 PushGateway 上,而 Prometheus Server 定期去 PushGateway 采集。

AlertManager 和 WebUI 都不是必须组件,前者可以基于采集的数据设置报警,后者可以通过 Web 界面的方式实时展示监控数据。

Prometheus Operator

Prometheus 的部署方式非常多,得益于其简单的工作原理,因此只需要将 Prometheus Server 部署到能访问到被监控对象的环境里即可。

但对于 K8s 来言,因为集群内的网络环境相对封闭、 Pod 的 IP 易变等特点,CoreOS 开源了通过 Operator(CRD) 的方式管理和部署 Prometheus (https://github.com/coreos/prometheus-operator )。

之前介绍 CRD 的文章(如何使用 CRD 拓展 Kubernetes 集群)也讲到,CRD 提供的能力取决于 CRD Controller,Prometheus Operator 便是这么一种 Controller,承担了监听自定义资源的变更,并完成后续的管理工作的职责。

安装 Operator

Prometheus Operator 的安装非常简单,在 Git 仓库的根目录下,直接 kubectl apply 其中的 bundle.yaml(镜像在 quay.io 下,需要科学上网):

git clone https://github.com/coreos/prometheus-operator.git
kubectl apply -f prometheus-operator/bundle.yaml

基本概念

Prometheus Operator 将会托管 Prometheus 的部署、管理工作,而且基于 K8s 中的 CRD,Prometheus Operator 新引入了若干 CR(自定义资源):

  1. Prometheus:描述将要部署的 Prometheus Server 集群
  2. ServiceMonitor/PodMonitor:描述了 Prometheus Server 的 Target 列表
  3. Alertmanager:描述 Alertmanager 集群
  4. PrometheusRule:描述 Prometheus 的告警规则

Prometheus Operator 的设计理念可以参考文档:https://github.com/coreos/prometheus-operator/blob/master/Documentation/design.md

工作原理

Prometheus Operator 通过监听上面的自定义资源(CR)的变动,并执行后续管理逻辑,入下图:

通过创建类型为 Prometheus 的资源(此处的 Prometheus 指的是 Prometheus Operator 定义的自定义资源),该 Prometheus 会通过 label selector 选择相关的 ServiceMonitor,而 ServiceMonitor 通过定义选择 Service 的 label selector 来选定需要监控的 Service ,并通过该 Service 对应的 Endpoints 获得需要监控的 Pod ip 列表。

监控应用 Demo

我们根据官方 User Guides 简单介绍一下如何使用 prometheus-operator 对应用进行监控,更多细节可以参考:https://github.com/coreos/prometheus-operator/blob/master/Documentation/user-guides/getting-started.md

部署被监控对象

通过 Deployment 部署有 3 个副本的应用:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: example-app
  template:
    metadata:
      labels:
        app: example-app
    spec:
      containers:
      - name: example-app
        image: fabxc/instrumented_app
        ports:
        - name: web
          containerPort: 8080

然后创建 Service 使得该服务可以提供稳定访问入口:

kind: Service
apiVersion: v1
metadata:
  name: example-app
  labels:
    app: example-app
spec:
  selector:
    app: example-app
  ports:
  - name: web
    port: 8080

注意 Service 中定义了 app=example-app 的 label,这是 ServiceMonitor 的选择依据。

部署监控

根据 Service 中定义的 Label,我们可以定义 ServiceMonitor

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: example-app
  labels:
    team: frontend
spec:
  selector:
    matchLabels:
      app: example-app
  endpoints:
  - port: web

ServiceMonitor 定义了 team=frontend 的 label,这也是 Prometheus 选择 ServiceMonitor 的依据。因此可以创建 Prometheus

apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
spec:
  serviceAccountName: prometheus
  serviceMonitorSelector:
    matchLabels:
      team: frontend
  resources:
    requests:
      memory: 400Mi
  enableAdminAPI: false

这时就发现,已经有 prometheus 实例被启动了:

# kubectl get po
NAME                                   READY   STATUS    RESTARTS   AGE
example-app-66db748757-bfqx4           1/1     Running   0          103m
example-app-66db748757-jqsh5           1/1     Running   0          103m
example-app-66db748757-jtbpc           1/1     Running   0          103m
prometheus-operator-7447bf4dcb-lzbf4   1/1     Running   0          18h
prometheus-prometheus-0                3/3     Running   0          100m

prometheus 本身提供了 WebUI,因此我们可以创建 SVC 是其暴露到集群外部访问(最好不要在公网环境做这样的操作):

apiVersion: v1
kind: Service
metadata:
  name: prometheus
spec:
  type: NodePort
  ports:
  - name: web
    nodePort: 30900
    port: 9090
    protocol: TCP
    targetPort: web
  selector:
    prometheus: prometheus

这时就可以看到集群内,Demo 应用的监控信息:

集群监控

通过这个自定义的 Demo 应该可以了解到,Prometheus 是通过 SVC 发起 HTTP 访问来获取数据,而集群监控,只不过是让 Prometheus 有能力获得 Kubernetes 组件的监控接口。同时,Prometheus 也支持以 DaemonSet 的形式部署 Node exporter,来直接收集集群节点信息。

而 Kubernetes 组件的监控数据的采集形式,则取决于集群的部署方式。如果是二进制文件方式部署,可以直接在 Node 上安装 Prometheus 并采集数据;而如果是容器部署,可以为 Kubernetes 组件创建 SVC,后续操作便和集群应用的监控方式一致了,相关文档可以参考:https://coreos.com/operators/prometheus/docs/latest/user-guides/cluster-monitoring.html