• 使用Prometheus监控Kubernetes集群
    • 从Kubelet获取节点运行状态
    • 从Kubelet获取节点容器资源使用情况
    • 使用NodeExporter监控集群资源使用情况
    • 从kube-apiserver获取集群运行监控指标
    • 对Ingress和Service进行网络探测

    使用Prometheus监控Kubernetes集群

    上一小节中,我们介绍了Promtheus在Kubernetes下的服务发现能力,并且通过kubernetes_sd_config实现了对Kubernetes下各类资源的自动发现。在本小节中,我们将带领读者利用Promethues提供的服务发现能力,实现对Kubernetes集群以及其中部署的各类资源的自动化监控。

    下表中,梳理了监控Kubernetes集群监控的各个维度以及策略:

    目标 服务发现模式 监控方法 数据源
    从集群各节点kubelet组件中获取节点kubelet的基本运行状态的监控指标 node 白盒监控 kubelet
    从集群各节点kubelet内置的cAdvisor中获取,节点中运行的容器的监控指标 node 白盒监控 kubelet
    从部署到各个节点的Node Exporter中采集主机资源相关的运行资源 node 白盒监控 node exporter
    对于内置了Promthues支持的应用,需要从Pod实例中采集其自定义监控指标 pod 白盒监控 custom pod
    获取API Server组件的访问地址,并从中获取Kubernetes集群相关的运行监控指标 endpoints 白盒监控 api server
    获取集群中Service的访问地址,并通过Blackbox Exporter获取网络探测指标 service 黑盒监控 blackbox exporter
    获取集群中Ingress的访问信息,并通过Blackbox Exporter获取网络探测指标 ingress 黑盒监控 blackbox exporter

    从Kubelet获取节点运行状态

    Kubelet组件运行在Kubernetes集群的各个节点中,其负责维护和管理节点上Pod的运行状态。kubelet组件的正常运行直接关系到该节点是否能够正常的被Kubernetes集群正常使用。

    基于Node模式,Prometheus会自动发现Kubernetes中所有Node节点的信息并作为监控的目标Target。 而这些Target的访问地址实际上就是Kubelet的访问地址,并且Kubelet实际上直接内置了对Promtheus的支持。

    修改prometheus.yml配置文件,并添加以下采集任务配置:

    1. - job_name: 'kubernetes-kubelet'
    2. scheme: https
    3. tls_config:
    4. ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    5. bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
    6. kubernetes_sd_configs:
    7. - role: node
    8. relabel_configs:
    9. - action: labelmap
    10. regex: __meta_kubernetes_node_label_(.+)

    这里使用Node模式自动发现集群中所有Kubelet作为监控的数据采集目标,同时通过labelmap步骤,将Node节点上的标签,作为样本的标签保存到时间序列当中。

    重新加载promethues配置文件,并重建Promthues的Pod实例后,查看kubernetes-kubelet任务采集状态,我们会看到以下错误提示信息:

    1. Get https://192.168.99.100:10250/metrics: x509: cannot validate certificate for 192.168.99.100 because it doesn't contain any IP SANs

    这是由于当前使用的ca证书中,并不包含192.168.99.100的地址信息。为了解决该问题,第一种方法是直接跳过ca证书校验过程,通过在tls_config中设置
    insecure_skip_verify为true即可。 这样Promthues在采集样本数据时,将会自动跳过ca证书的校验过程,从而从kubelet采集到监控数据:

    1. - job_name: 'kubernetes-kubelet'
    2. scheme: https
    3. tls_config:
    4. ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    5. insecure_skip_verify: true
    6. bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
    7. kubernetes_sd_configs:
    8. - role: node
    9. relabel_configs:
    10. - action: labelmap
    11. regex: __meta_kubernetes_node_label_(.+)

    直接采集kubelet监控指标

    第二种方式,不直接通过kubelet的metrics服务采集监控数据,而通过Kubernetes的api-server提供的代理API访问各个节点中kubelet的metrics服务,如下所示:

    1. - job_name: 'kubernetes-kubelet'
    2. scheme: https
    3. tls_config:
    4. ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    5. bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
    6. kubernetes_sd_configs:
    7. - role: node
    8. relabel_configs:
    9. - action: labelmap
    10. regex: __meta_kubernetes_node_label_(.+)
    11. - target_label: __address__
    12. replacement: kubernetes.default.svc:443
    13. - source_labels: [__meta_kubernetes_node_name]
    14. regex: (.+)
    15. target_label: __metrics_path__
    16. replacement: /api/v1/nodes/${1}/proxy/metrics

    通过relabeling,将从Kubernetes获取到的默认地址__address__替换为kubernetes.default.svc:443。同时将__metrics_path__替换为api-server的代理地址/api/v1/nodes/${1}/proxy/metrics。

    通过api-server代理获取kubelet监控指标

    通过获取各个节点中kubelet的监控指标,用户可以评估集群中各节点的性能表现。例如,通过指标kubelet_pod_start_latency_microseconds可以获得当前节点中Pod启动时间相关的统计数据。

    1. kubelet_pod_start_latency_microseconds{quantile="0.99"}

    99%的Pod启动时间

    Pod平均启动时间大致为42s左右(包含镜像下载时间):

    1. kubelet_pod_start_latency_microseconds_sum / kubelet_pod_start_latency_microseconds_count

    Pod平均启动时间

    除此以外,监控指标kubeletdocker*还可以体现出kubelet与当前节点的docker服务的调用情况,从而可以反映出docker本身是否会影响kubelet的性能表现等问题。

    从Kubelet获取节点容器资源使用情况

    各节点的kubelet组件中除了包含自身的监控指标信息以外,kubelet组件还内置了对cAdvisor的支持。cAdvisor能够获取当前节点上运行的所有容器的资源使用情况,通过访问kubelet的/metrics/cadvisor地址可以获取到cadvisor的监控指标,因此和获取kubelet监控指标类似,这里同样通过node模式自动发现所有的kubelet信息,并通过适当的relabel过程,修改监控采集任务的配置。 与采集kubelet自身监控指标相似,这里也有两种方式采集cadvisor中的监控指标:

    方式一:直接访问kubelet的/metrics/cadvisor地址,需要跳过ca证书认证:

    1. - job_name: 'kubernetes-cadvisor'
    2. scheme: https
    3. tls_config:
    4. ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    5. bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
    6. kubernetes_sd_configs:
    7. - role: node
    8. relabel_configs:
    9. - target_label: __address__
    10. replacement: kubernetes.default.svc:443
    11. - source_labels: [__meta_kubernetes_node_name]
    12. regex: (.+)
    13. target_label: __metrics_path__
    14. replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
    15. - action: labelmap
    16. regex: __meta_kubernetes_node_label_(.+)

    使用api-server代理

    方式二:通过api-server提供的代理地址访问kubelet的/metrics/cadvisor地址:

    1. - job_name: 'kubernetes-cadvisor'
    2. scheme: https
    3. tls_config:
    4. ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    5. insecure_skip_verify: true
    6. bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
    7. kubernetes_sd_configs:
    8. - role: node
    9. relabel_configs:
    10. - source_labels: [__meta_kubernetes_node_name]
    11. regex: (.+)
    12. target_label: __metrics_path__
    13. replacement: metrics/cadvisor
    14. - action: labelmap
    15. regex: __meta_kubernetes_node_label_(.+)

    直接访问kubelet

    使用NodeExporter监控集群资源使用情况

    为了能够采集集群中各个节点的资源使用情况,我们需要在各节点中部署一个Node Exporter实例。在本章的“部署Prometheus”小节,我们使用了Kubernetes内置的控制器之一Deployment。Deployment能够确保Prometheus的Pod能够按照预期的状态在集群中运行,而Pod实例可能随机运行在任意节点上。而与Prometheus的部署不同的是,对于Node Exporter而言每个节点只需要运行一个唯一的实例,此时,就需要使用Kubernetes的另外一种控制器Daemonset。顾名思义,Daemonset的管理方式类似于操作系统中的守护进程。Daemonset会确保在集群中所有(也可以指定)节点上运行一个唯一的Pod实例。

    创建node-exporter-daemonset.yml文件,并写入以下内容:

    1. apiVersion: extensions/v1beta1
    2. kind: DaemonSet
    3. metadata:
    4. name: node-exporter
    5. spec:
    6. template:
    7. metadata:
    8. annotations:
    9. prometheus.io/scrape: 'true'
    10. prometheus.io/port: '9100'
    11. prometheus.io/path: 'metrics'
    12. labels:
    13. app: node-exporter
    14. name: node-exporter
    15. spec:
    16. containers:
    17. - image: prom/node-exporter
    18. imagePullPolicy: IfNotPresent
    19. name: node-exporter
    20. ports:
    21. - containerPort: 9100
    22. hostPort: 9100
    23. name: scrape
    24. hostNetwork: true
    25. hostPID: true

    由于Node Exporter需要能够访问宿主机,因此这里指定了hostNetwork和hostPID,让Pod实例能够以主机网络以及系统进程的形式运行。同时YAML文件中也创建了NodeExporter相应的Service。这样通过Service就可以访问到对应的NodeExporter实例。

    1. $ kubectl create -f node-exporter-daemonset.yml
    2. service "node-exporter" created
    3. daemonset "node-exporter" created

    查看Daemonset以及Pod的运行状态

    1. $ kubectl get daemonsets
    2. NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
    3. node-exporter 1 1 1 1 1 <none> 15s
    4. $ kubectl get pods
    5. NAME READY STATUS RESTARTS AGE
    6. ...
    7. node-exporter-9h56z 1/1 Running 0 51s

    由于Node Exporter是以主机网络的形式运行,因此直接访问MiniKube的虚拟机IP加上Pod的端口即可访问当前节点上运行的Node Exporter实例:

    1. $ minikube ip
    2. 192.168.99.100
    3. $ curl http://192.168.99.100:9100/metrics
    4. ...
    5. process_start_time_seconds 1.5251401593e+09
    6. # HELP process_virtual_memory_bytes Virtual memory size in bytes.
    7. # TYPE process_virtual_memory_bytes gauge
    8. process_virtual_memory_bytes 1.1984896e+08

    目前为止,通过Daemonset的形式将Node Exporter部署到了集群中的各个节点中。接下来,我们只需要通过Prometheus的pod服务发现模式,找到当前集群中部署的Node Exporter实例即可。 需要注意的是,由于Kubernetes中并非所有的Pod都提供了对Prometheus的支持,有些可能只是一些简单的用户应用,为了区分哪些Pod实例是可以供Prometheus进行采集的,这里我们为Node Exporter添加了注解:

    1. prometheus.io/scrape: 'true'

    由于Kubernetes中Pod可能会包含多个容器,还需要用户通过注解指定用户提供监控指标的采集端口:

    1. prometheus.io/port: '9100'

    而有些情况下,Pod中的容器可能并没有使用默认的/metrics作为监控采集路径,因此还需要支持用户指定采集路径:

    1. prometheus.io/path: 'metrics'

    为Prometheus创建监控采集任务kubernetes-pods,如下所示:

    1. - job_name: 'kubernetes-pods'
    2. kubernetes_sd_configs:
    3. - role: pod
    4. relabel_configs:
    5. - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
    6. action: keep
    7. regex: true
    8. - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
    9. action: replace
    10. target_label: __metrics_path__
    11. regex: (.+)
    12. - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
    13. action: replace
    14. regex: ([^:]+)(?::\d+)?;(\d+)
    15. replacement: $1:$2
    16. target_label: __address__
    17. - action: labelmap
    18. regex: __meta_kubernetes_pod_label_(.+)
    19. - source_labels: [__meta_kubernetes_namespace]
    20. action: replace
    21. target_label: kubernetes_namespace
    22. - source_labels: [__meta_kubernetes_pod_name]
    23. action: replace
    24. target_label: kubernetes_pod_name

    通过Pod模式自动发现Node Exporter实例

    通过以上relabel过程实现对Pod实例的过滤,以及采集任务地址替换,从而实现对特定Pod实例监控指标的采集。需要说明的是kubernetes-pods并不是只针对Node Exporter而言,对于用户任意部署的Pod实例,只要其提供了对Prometheus的支持,用户都可以通过为Pod添加注解的形式为其添加监控指标采集的支持。

    从kube-apiserver获取集群运行监控指标

    在开始正式内容之前,我们需要先了解一下Kubernetes中Service是如何实现负载均衡的,如下图所示,一般来说Service有两个主要的使用场景:

    Service负载均衡

    • 代理对集群内部应用Pod实例的请求:当创建Service时如果指定了标签选择器,Kubernetes会监听集群中所有的Pod变化情况,通过Endpoints自动维护满足标签选择器的Pod实例的访问信息;
    • 代理对集群外部服务的请求:当创建Service时如果不指定任何的标签选择器,此时需要用户手动创建Service对应的Endpoint资源。例如,一般来说,为了确保数据的安全,我们通常讲数据库服务部署到集群外。 这是为了避免集群内的应用硬编码数据库的访问信息,这是就可以通过在集群内创建Service,并指向外部的数据库服务实例。

    kube-apiserver扮演了整个Kubernetes集群管理的入口的角色,负责对外暴露Kubernetes API。kube-apiserver组件一般是独立部署在集群外的,为了能够让部署在集群内的应用(kubernetes插件或者用户应用)能够与kube-apiserver交互,Kubernetes会默认在命名空间下创建一个名为kubernetes的服务,如下所示:

    1. $ kubectl get svc kubernetes -o wide
    2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
    3. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 166d <none>

    而该kubernetes服务代理的后端实际地址通过endpoints进行维护,如下所示:

    1. $ kubectl get endpoints kubernetes
    2. NAME ENDPOINTS AGE
    3. kubernetes 10.0.2.15:8443 166d

    通过这种方式集群内的应用或者系统主机就可以通过集群内部的DNS域名kubernetes.default.svc访问到部署外部的kube-apiserver实例。

    因此,如果我们想要监控kube-apiserver相关的指标,只需要通过endpoints资源找到kubernetes对应的所有后端地址即可。

    如下所示,创建监控任务kubernetes-apiservers,这里指定了服务发现模式为endpoints。Promtheus会查找当前集群中所有的endpoints配置,并通过relabel进行判断是否为apiserver对应的访问地址:

    1. - job_name: 'kubernetes-apiservers'
    2. kubernetes_sd_configs:
    3. - role: endpoints
    4. scheme: https
    5. tls_config:
    6. ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    7. bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
    8. relabel_configs:
    9. - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
    10. action: keep
    11. regex: default;kubernetes;https
    12. - target_label: __address__
    13. replacement: kubernetes.default.svc:443

    在relabel_configs配置中第一步用于判断当前endpoints是否为kube-apiserver对用的地址。第二步,替换监控采集地址到kubernetes.default.svc:443即可。重新加载配置文件,重建Promthues实例,得到以下结果。

    apiserver任务状态

    对Ingress和Service进行网络探测

    为了能够对Ingress和Service进行探测,我们需要在集群部署Blackbox Exporter实例。 如下所示,创建blackbox-exporter.yaml用于描述部署相关的内容:

    1. apiVersion: v1
    2. kind: Service
    3. metadata:
    4. labels:
    5. app: blackbox-exporter
    6. name: blackbox-exporter
    7. spec:
    8. ports:
    9. - name: blackbox
    10. port: 9115
    11. protocol: TCP
    12. selector:
    13. app: blackbox-exporter
    14. type: ClusterIP
    15. ---
    16. apiVersion: extensions/v1beta1
    17. kind: Deployment
    18. metadata:
    19. labels:
    20. app: blackbox-exporter
    21. name: blackbox-exporter
    22. spec:
    23. replicas: 1
    24. selector:
    25. matchLabels:
    26. app: blackbox-exporter
    27. template:
    28. metadata:
    29. labels:
    30. app: blackbox-exporter
    31. spec:
    32. containers:
    33. - image: prom/blackbox-exporter
    34. imagePullPolicy: IfNotPresent
    35. name: blackbox-exporter

    通过kubectl命令部署Blackbox Exporter实例,这里将部署一个Blackbox Exporter的Pod实例,同时通过服务blackbox-exporter在集群内暴露访问地址blackbox-exporter.default.svc.cluster.local,对于集群内的任意服务都可以通过该内部DNS域名访问Blackbox Exporter实例:

    1. $ kubectl get pods
    2. NAME READY STATUS RESTARTS AGE
    3. blackbox-exporter-f77fc78b6-72bl5 1/1 Running 0 4s
    4. $ kubectl get svc
    5. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    6. blackbox-exporter ClusterIP 10.109.144.192 <none> 9115/TCP 3m

    为了能够让Prometheus能够自动的对Service进行探测,我们需要通过服务发现自动找到所有的Service信息。 如下所示,在Prometheus的配置文件中添加名为kubernetes-services的监控采集任务:

    1. - job_name: 'kubernetes-services'
    2. metrics_path: /probe
    3. params:
    4. module: [http_2xx]
    5. kubernetes_sd_configs:
    6. - role: service
    7. relabel_configs:
    8. - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
    9. action: keep
    10. regex: true
    11. - source_labels: [__address__]
    12. target_label: __param_target
    13. - target_label: __address__
    14. replacement: blackbox-exporter.default.svc.cluster.local:9115
    15. - source_labels: [__param_target]
    16. target_label: instance
    17. - action: labelmap
    18. regex: __meta_kubernetes_service_label_(.+)
    19. - source_labels: [__meta_kubernetes_namespace]
    20. target_label: kubernetes_namespace
    21. - source_labels: [__meta_kubernetes_service_name]
    22. target_label: kubernetes_name

    在该任务配置中,通过指定kubernetes_sd_config的role为service指定服务发现模式:

    1. kubernetes_sd_configs:
    2. - role: service

    为了区分集群中需要进行探测的Service实例,我们通过标签‘prometheus.io/probe: true’进行判断,从而过滤出需要探测的所有Service实例:

    1. - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
    2. action: keep
    3. regex: true

    并且将通过服务发现获取到的Service实例地址__address__转换为获取监控数据的请求参数。同时将__address执行Blackbox Exporter实例的访问地址,并且重写了标签instance的内容:

    1. - source_labels: [__address__]
    2. target_label: __param_target
    3. - target_label: __address__
    4. replacement: blackbox-exporter.default.svc.cluster.local:9115
    5. - source_labels: [__param_target]
    6. target_label: instance

    最后,为监控样本添加了额外的标签信息:

    1. - action: labelmap
    2. regex: __meta_kubernetes_service_label_(.+)
    3. - source_labels: [__meta_kubernetes_namespace]
    4. target_label: kubernetes_namespace
    5. - source_labels: [__meta_kubernetes_service_name]
    6. target_label: kubernetes_name

    对于Ingress而言,也是一个相对类似的过程,这里给出对Ingress探测的Promthues任务配置作为参考:

    1. - job_name: 'kubernetes-ingresses'
    2. metrics_path: /probe
    3. params:
    4. module: [http_2xx]
    5. kubernetes_sd_configs:
    6. - role: ingress
    7. relabel_configs:
    8. - source_labels: [__meta_kubernetes_ingress_annotation_prometheus_io_probe]
    9. action: keep
    10. regex: true
    11. - source_labels: [__meta_kubernetes_ingress_scheme,__address__,__meta_kubernetes_ingress_path]
    12. regex: (.+);(.+);(.+)
    13. replacement: ${1}://${2}${3}
    14. target_label: __param_target
    15. - target_label: __address__
    16. replacement: blackbox-exporter.default.svc.cluster.local:9115
    17. - source_labels: [__param_target]
    18. target_label: instance
    19. - action: labelmap
    20. regex: __meta_kubernetes_ingress_label_(.+)
    21. - source_labels: [__meta_kubernetes_namespace]
    22. target_label: kubernetes_namespace
    23. - source_labels: [__meta_kubernetes_ingress_name]
    24. target_label: kubernetes_name