跳到主要内容

监控集群基础设施

在第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文件,并写入以下内容:

apiVersion: v1
kind: Service
metadata:
annotations:
prometheus.io/scrape: 'true'
labels:
app: node-exporter
name: node-exporter
name: node-exporter
spec:
ports:
- name: scrape
port: 9100
protocol: TCP
selector:
app: node-exporter
type: ClusterIP
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
annotations:
prometheus.io/scrape: 'true'
name: node-exporter
spec:
template:
metadata:
labels:
app: node-exporter
name: node-exporter
spec:
containers:
- image: prom/node-exporter
name: node-exporter
ports:
- containerPort: 9100
hostPort: 9100
name: scrape
hostNetwork: true
hostPID: true

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

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

查看Daemonset以及Pod的运行状态

$ kubectl get daemonsets
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
node-exporter 1 1 1 1 1 <none> 15s

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
...
node-exporter-9h56z 1/1 Running 0 51s

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

$ minikube ip
192.168.99.100

$ curl http://192.168.99.100:9100/metrics
...
process_start_time_seconds 1.5251401593e+09
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
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地址:

$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
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,通过以下命令查看:

$ kubectl get endpoints node-exporter
NAME ENDPOINTS AGE
node-exporter 192.168.99.100:9100 4m

最后查看Service的详细信息:

$ kubectl describe svc node-exporter
Name: node-exporter
Namespace: default
Labels: app=node-exporter
name=node-exporter
Annotations: prometheus.io/scrape=true
Selector: app=node-exporter
Type: ClusterIP
IP: 10.100.42.83
Port: scrape 9100/TCP
TargetPort: 9100/TCP
Endpoints: 192.168.99.100:9100
Session Affinity: None
Events: <none>

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

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

apiVersion: v1
kind: Service
metadata:
name: mysql-production
spec:
ports:
- port: 3306
---
kind: Endpoints
apiVersion: v1
metadata:
name: mysql-production
subsets:
- addresses:
- ip: 192.168.1.25
ports:
- port: 3306

使用Endpoint发现Node Exporter实例

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

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

kubernetes_sd_configs:
- role: endpoints

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

metadata:
annotations:
prometheus.io/scrape: 'true'

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

__address__="192.168.99.100:9100"
__meta_kubernetes_endpoint_port_name="scrape"
__meta_kubernetes_endpoint_port_protocol="TCP"
__meta_kubernetes_endpoint_ready="true"
__meta_kubernetes_endpoints_name="node-exporter"
__meta_kubernetes_namespace="default"
__meta_kubernetes_pod_container_name="node-exporter"
__meta_kubernetes_pod_container_port_name="scrape"
__meta_kubernetes_pod_container_port_number="9100"
__meta_kubernetes_pod_container_port_protocol="TCP"
__meta_kubernetes_pod_host_ip="192.168.99.100"
__meta_kubernetes_pod_ip="192.168.99.100"
__meta_kubernetes_pod_label_app="node-exporter"
__meta_kubernetes_pod_label_controller_revision_hash="4286002507"
__meta_kubernetes_pod_label_pod_template_generation="1"
__meta_kubernetes_pod_name="node-exporter-st4cd"
__meta_kubernetes_pod_node_name="minikube"
__meta_kubernetes_pod_ready="true"
__meta_kubernetes_pod_uid="7fe1c063-4ce5-11e8-a82a-08002717c1c9"
__meta_kubernetes_service_annotation_prometheus_io_scrape="true"
__meta_kubernetes_service_label_app="node-exporter"
__meta_kubernetes_service_label_name="node-exporter"
__meta_kubernetes_service_name="node-exporter"
__metrics_path__="/metrics"
__scheme__="http"
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作为监控目标:

  - 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_endpoints_name]
target_label: job

Relabeling保留符合规则的Endpoint

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

 -  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
- source_labels: [__meta_kubernetes_endpoints_name]
target_label: job

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

apiVersion: v1
kind: Service
metadata:
annotations:
prometheus.io/scrape: 'true'
prometheus.io/scheme: 'https'
prometheus.io/path: '/custom_metrics'

通过Endpoint发现的Node Exporter实例