监控集群基础设施

在第1章的“初始Prometheus”小节,我们已经基本了解和使用过Node Exporter。Node Exporter能够采集和获取当前所在主机的运行状态数据。本节将带领读者在Kubernetes中部署Node Exporter,并且通过Prometheus自动监控集群中所有节点资源使用情况。

使用Daemonset部署Node Exporter

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

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

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. annotations:
  5. prometheus.io/scrape: 'true'
  6. labels:
  7. app: node-exporter
  8. name: node-exporter
  9. name: node-exporter
  10. spec:
  11. ports:
  12. - name: scrape
  13. port: 9100
  14. protocol: TCP
  15. selector:
  16. app: node-exporter
  17. type: ClusterIP
  18. ---
  19. apiVersion: extensions/v1beta1
  20. kind: DaemonSet
  21. metadata:
  22. annotations:
  23. prometheus.io/scrape: 'true'
  24. name: node-exporter
  25. spec:
  26. template:
  27. metadata:
  28. labels:
  29. app: node-exporter
  30. name: node-exporter
  31. spec:
  32. containers:
  33. - image: prom/node-exporter
  34. name: node-exporter
  35. ports:
  36. - containerPort: 9100
  37. hostPort: 9100
  38. name: scrape
  39. hostNetwork: true
  40. 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

Kubernetes下Service负载均衡原理

在Kubernetes下Service是作为一个内部负载均衡器的存在,它对外暴露一个唯一访问地址(ClusterIP),后端则通过Endpoint指向多个Pod实例。

Service负载均衡原理

在Kubernetes中Service和Endpoint是两个独立的资源。如果创建Service的时,指定了Selector选择器。那么Kubernetes会自动根据选择器的去匹配Pod实例,并根据这些Pod的访问信息,自动创建Endpoint资源。

例如,通过以下命令可以查看Node Exporter实例对应的IP地址:

  1. $ kubectl get pods -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE
  3. node-exporter-st4cd 1/1 Running 0 4m 192.168.99.100 minikube

由于Service中指定了标签选择器(app: node-exporter),Kubernetes就会自动找到该选择器对应的Pod实例的访问信息(这里是192.168.99.100:9100),并创建Service对应的Endpoint,通过以下命令查看:

  1. $ kubectl get endpoints node-exporter
  2. NAME ENDPOINTS AGE
  3. node-exporter 192.168.99.100:9100 4m

最后查看Service的详细信息:

  1. $ kubectl describe svc node-exporter
  2. Name: node-exporter
  3. Namespace: default
  4. Labels: app=node-exporter
  5. name=node-exporter
  6. Annotations: prometheus.io/scrape=true
  7. Selector: app=node-exporter
  8. Type: ClusterIP
  9. IP: 10.100.42.83
  10. Port: scrape 9100/TCP
  11. TargetPort: 9100/TCP
  12. Endpoints: 192.168.99.100:9100
  13. Session Affinity: None
  14. Events: <none>

将Service与Endpoint分离还带来另外一个好处,如果我们希望集群内的应用程序,能够通过Service的形式访问到集群外的资源(如,外部部署的MySQL)。这是我们可以创建一个不包含Selector的Service即可,并且手段创建该Service需要代理的外部服务即可。

例如,创建服务mysql-production,并且指向集群外运行的MySQL服务:

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: mysql-production
  5. spec:
  6. ports:
  7. - port: 3306
  8. ---
  9. kind: Endpoints
  10. apiVersion: v1
  11. metadata:
  12. name: mysql-production
  13. subsets:
  14. - addresses:
  15. - ip: 192.168.1.25
  16. ports:
  17. - port: 3306

使用Endpoint发现Node Exporter实例

在了解了Kubernetes下Service与Endpoint的关系以后,我们就能大概理解,在Kubernetes下部署应用程序时,通过Endpoint是能够找到特定服务的多个访问地址的。这样我们就可以通过这些地址获取到相应的监控指标。 而Service作为负载均衡器,则适用于作为服务可用性的探测标准,因此可以将Blackbox与Service相结合,监控服务的可用性。

在Prometheus中,通过设置kubernetes_sd_config的role为endpoints指定当前的服务发现模式:

  1. kubernetes_sd_configs:
  2. - role: endpoints

不过,为了区分集群中哪些Endpoint是可以采集的,而哪些是不可以采集的,我们可以通过为Service添加特定的标签进行标记。例如,Node Exporter的Service中包含了自定义的注解:

  1. metadata:
  2. annotations:
  3. prometheus.io/scrape: 'true'

通过Kubernetes获取到的Endpoint对象,如下所示,是通过Kubernetes自动发现的Endpoint对象的所有metadata标签:

  1. __address__="192.168.99.100:9100"
  2. __meta_kubernetes_endpoint_port_name="scrape"
  3. __meta_kubernetes_endpoint_port_protocol="TCP"
  4. __meta_kubernetes_endpoint_ready="true"
  5. __meta_kubernetes_endpoints_name="node-exporter"
  6. __meta_kubernetes_namespace="default"
  7. __meta_kubernetes_pod_container_name="node-exporter"
  8. __meta_kubernetes_pod_container_port_name="scrape"
  9. __meta_kubernetes_pod_container_port_number="9100"
  10. __meta_kubernetes_pod_container_port_protocol="TCP"
  11. __meta_kubernetes_pod_host_ip="192.168.99.100"
  12. __meta_kubernetes_pod_ip="192.168.99.100"
  13. __meta_kubernetes_pod_label_app="node-exporter"
  14. __meta_kubernetes_pod_label_controller_revision_hash="4286002507"
  15. __meta_kubernetes_pod_label_pod_template_generation="1"
  16. __meta_kubernetes_pod_name="node-exporter-st4cd"
  17. __meta_kubernetes_pod_node_name="minikube"
  18. __meta_kubernetes_pod_ready="true"
  19. __meta_kubernetes_pod_uid="7fe1c063-4ce5-11e8-a82a-08002717c1c9"
  20. __meta_kubernetes_service_annotation_prometheus_io_scrape="true"
  21. __meta_kubernetes_service_label_app="node-exporter"
  22. __meta_kubernetes_service_label_name="node-exporter"
  23. __meta_kubernetes_service_name="node-exporter"
  24. __metrics_path__="/metrics"
  25. __scheme__="http"
  26. job="kubernetes-service-endpoints"

由于该Endpoint属于特定的Servie,并且backend指向了具体的Pod实例,所以返回的metadata标签中包含了关联的Service的信息(以__meta_kubernetes_service作为前缀)以及后端Pod的相关信息(以__meta_kubernetes_pod作为浅醉)。

通过relabeling的keep模式,选择只获取包含了标签__meta_kubernetes_service_annotation_prometheus_io_scrape并且其值为true的Endpoint作为监控目标:

  1. - job_name: 'kubernetes-service-endpoints'
  2. kubernetes_sd_configs:
  3. - role: endpoints
  4. relabel_configs:
  5. - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
  6. action: keep
  7. regex: true
  8. - source_labels: [__meta_kubernetes_endpoints_name]
  9. target_label: job

Relabeling保留符合规则的Endpoint

这种基于Service的annotations来控制Prometheus的方式,还可以扩展出更多的玩法。例如,如果应用程序并没有通过/metrics暴露监控样本数据。 下面是一个更完整的采集任务配置如下所示:

  1. - job_name: 'kubernetes-service-endpoints'
  2. kubernetes_sd_configs:
  3. - role: endpoints
  4. relabel_configs:
  5. - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
  6. action: keep
  7. regex: true
  8. - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
  9. action: replace
  10. target_label: __scheme__
  11. regex: (https?)
  12. - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
  13. action: replace
  14. target_label: __metrics_path__
  15. regex: (.+)
  16. - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
  17. action: replace
  18. target_label: __address__
  19. regex: ([^:]+)(?::\d+)?;(\d+)
  20. replacement: $1:$2
  21. - action: labelmap
  22. regex: __meta_kubernetes_service_label_(.+)
  23. - source_labels: [__meta_kubernetes_namespace]
  24. action: replace
  25. target_label: kubernetes_namespace
  26. - source_labels: [__meta_kubernetes_service_name]
  27. action: replace
  28. target_label: kubernetes_name
  29. - source_labels: [__meta_kubernetes_endpoints_name]
  30. target_label: job

通过以上步骤,用户可以通过在Service添加注解的形式,更灵活的控制Prometheus的任务采集信息,例如,通过添加注解自定义采集数据的相关配置:

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. annotations:
  5. prometheus.io/scrape: 'true'
  6. prometheus.io/scheme: 'https'
  7. prometheus.io/path: '/custom_metrics'

通过Endpoint发现的Node Exporter实例