使用Prometheus完成Kubernetes集群监控

当你完成了Kubernetes集群的最初搭建后,集群监控的需求随之而来。 集群内的N台服务器在Kubernetes的管理下自动的创建和销毁着Pod, 但所有Pod和服务器的运行状态以及消耗的资源却不能方便的获得和展示, 给人一种驾驶着一辆没有仪表板的跑车在高速公路飞驰的感觉。

对于单机的Linux服务器监控,已经有了NagiosZabbix这些成熟的方案。 在Kubernetes集群中,我们使用新一代的监控系统Prometheus来完成集群的监控。

Prometheus简介

Prometheus是SoundCloud开源的一款监控软件。它的实现参考了Google内部的监控实现, 与同样源自Google的Kubernetes项目搭配起来非常合拍。同时它也是继Kubernetes之后 第二款捐赠给CNCF的开源软件。相信有CNCF的推广,它将逐步成为集群时代的重要底层组件。

Prometheus集成了数据采集,存储,异常告警多项功能,是一款一体化的完整方案。 它针对大规模的集群环境设计了拉取式的数据采集方式、多维度数据存储格式以及服务发现等创新功能。

今后我们会进一步探讨Prometheus的特性以及使用技巧,在这里我们直接演示在Kubernetes集群中 使用Prometheus的方式。

使用服务发现简化监控系统配置

与传统的先启动监控系统,然后配置所有服务器将运行数据发往监控系统不同。Prometheus 可以通过服务发现掌握集群内部已经暴露的监控点,然后主动拉取所有监控数据。 通过这样的架构设计,我们仅仅只需要向Kubernetes集群中部署一份Prometheus实例, 它就可以通过向apiserver查询集群状态,然后向所有已经支持Prometheus metrics的kubelet 获取所有Pod的运行数据。如果我们想采集底层服务器运行状态,通过DaemonSet在所有服务器上运行 配套的node-exporter之后,Prometheus就可以自动采集到新的这部分数据。

这种动态发现的架构,非常适合服务器和程序都不固定的Kubernetes集群环境,同时 也大大降低了运维的负担。

启动Prometheus服务

首先将Prometheus的配置文件,存为ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
data:
  prometheus.yml: |
    global:
      scrape_interval: 30s
      scrape_timeout: 30s
    scrape_configs:
    - job_name: 'prometheus'
      static_configs:
        - targets: ['localhost:9090']
    - job_name: 'kubernetes-cluster'
      scheme: https
      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:
      - api_servers:
        - 'https://kubernetes.default.svc'
        in_cluster: true
        role: apiserver
    - job_name: 'kubernetes-nodes'
      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
      kubernetes_sd_configs:
      - api_servers:
        - 'https://kubernetes.default.svc'
        in_cluster: true
        role: node
      relabel_configs:
      - action: labelmap
        regex: __meta_kubernetes_node_label_(.+)
    - job_name: 'kubernetes-service-endpoints'
      scheme: https
      kubernetes_sd_configs:
      - api_servers:
        - 'https://kubernetes.default.svc'
        in_cluster: true
        role: endpoint
      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_service_namespace]
        action: replace
        target_label: kubernetes_namespace
      - source_labels: [__meta_kubernetes_service_name]
        action: replace
        target_label: kubernetes_name
    - job_name: 'kubernetes-services'
      scheme: https
      metrics_path: /probe
      params:
        module: [http_2xx]
      kubernetes_sd_configs:
      - api_servers:
        - 'https://kubernetes.default.svc'
        in_cluster: true
        role: service
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
        action: keep
        regex: true
      - source_labels: [__address__]
        target_label: __param_target
      - target_label: __address__
        replacement: blackbox
      - source_labels: [__param_target]
        target_label: instance
      - action: labelmap
        regex: __meta_kubernetes_service_label_(.+)
      - source_labels: [__meta_kubernetes_service_namespace]
        target_label: kubernetes_namespace
      - source_labels: [__meta_kubernetes_service_name]
        target_label: kubernetes_name
    - job_name: 'kubernetes-pods'
      scheme: https
      kubernetes_sd_configs:
      - api_servers:
        - 'https://kubernetes.default.svc'
        in_cluster: true
        role: pod
      relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - 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
        regex: (.+):(?:\d+);(\d+)
        replacement: ${1}:${2}
        target_label: __address__
      - action: labelmap
        regex: __meta_kubernetes_pod_label_(.+)
      - source_labels: [__meta_kubernetes_pod_namespace]
        action: replace
        target_label: kubernetes_namespace
      - source_labels: [__meta_kubernetes_pod_name]
        action: replace
        target_label: kubernetes_pod_name

将以上配置文件文件保存为prometheus-config.yaml,再执行

$ kubectl create -f prometheus-config.yaml

接下来通过Deployment部署Prometheus

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    name: prometheus-deployment
  name: prometheus
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: prometheus
    spec:
      containers:
      - image: prom/prometheus:v1.0.1
        name: prometheus
        command:
        - "/bin/prometheus"
        args:
        - "-config.file=/etc/prometheus/prometheus.yml"
        - "-storage.local.path=/prometheus"
        - "-storage.local.retention=24h"
        ports:
        - containerPort: 9090
          protocol: TCP
        volumeMounts:
        - mountPath: "/prometheus"
          name: data
        - mountPath: "/etc/prometheus"
          name: config-volume
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
          limits:
            cpu: 500m
            memory: 2500Mi
      volumes:
      - emptyDir: {}
        name: data
      - configMap:
          name: prometheus-config
        name: config-volume

将以上文件保存为prometheus-deployment.yaml,接着运行

$ kubectl create -f prometheus-deployment.yaml

如果是在国内环境,可以用registry.cn-hangzhou.aliyuncs.com/tryk8s/prometheus:v1.0.1 代替上面的image设置。

为了在本地访问Prometheus的web界面,我们利用kubectl port-forward将它暴露到本地

$ POD=`kubectl get pod -l app=prometheus -o go-template --template '{{range .items}}{{.metadata.name}}{{end}}'`
$ kubectl port-forward $POD 9090:9090

这时我们用浏览器访问http://127.0.0.1:9090来访问Prometheus的界面,查看已经搜集到的数据。

查询监控数据

Prometheus提供API方式的数据查询接口,用户可以使用query语言完成复杂的查询任务。 为了方便调试,web界面上提供基本的查询和图形化展示功能,我们用它做一些基本的查询。

首先查询每个容器的内存使用情况,查询container_memory_usage_bytes{image=~".+"}

接下来查询各个Pod的CPU使用情况,查询条件是sum(rate(container_cpu_usage_seconds_total{kubernetes_pod_name=~".+", job="kubernetes-nodes"}[1m])) by (kubernetes_pod_name, kubernetes_namespace)

更多的查询条件可以参考Prometheus的文档,将来也会逐步介绍,这里就不详细展开了。

总结

通过向Kubernetes集群内部署Prometheus,我们在不修改任何集群配置的状态下,利用Prometheus 的服务发现功能获得了基本的集群监控能力,并通过web界面对监控系统获取到的数据做了基本的查询。

未来我们将进一步完善Prometheus的使用:

在本文的写作过程中,CoreOS在博客上发布了一篇相同主题的文章, 本文的Prometheus配置文件根据CoreOS的分享做了修改。