Prometheus简介 Prometheus 最初是 SoundCloud 构建的开源系统监控和报警工具,是一个独立的开源项目,于 2016 年加入了 CNCF 基金会,作为继 Kubernetes 之后的第二个托管项目。Prometheus 相比于其他传统监控工具主要有以下几个特点:
具有由 metric 名称和键/值对标识的时间序列数据的多维数据模型 有一个灵活的查询语言 不依赖分布式存储,只和本地磁盘有关 通过 HTTP 的服务拉取时间序列数据 也支持推送的方式来添加时间序列数据 还支持通过服务发现或静态配置发现目标 多种图形和仪表板支持 Prometheus 由多个组件组成,但是其中有些组件是可选的:
Prometheus Server
:用于抓取指标、存储时间序列数据exporter
:暴露指标让任务来抓pushgateway
:push 的方式将指标数据推送到该网关alertmanager
:处理报警的报警组件 adhoc
:用于数据查询大多数 Prometheus 组件都是用 Go 编写的,因此很容易构建和部署为静态的二进制文件。下图是 Prometheus 官方提供的架构及其一些相关的生态系统组件:
整体流程比较简单,Prometheus 直接接收或者通过中间的 Pushgateway 网关被动获取指标数据,在本地存储所有的获取的指标数据,并对这些数据进行一些规则整理,用来生成一些聚合数据或者报警信息,Grafana 或者其他工具用来可视化这些数据。
部署Prometheus 创建资源配置清单 ConfigMap Deployment PV/PVC RBAC Service Ingress 为了能够方便的管理配置文件,这里将 prometheus.yml
文件用 ConfigMap 的形式进行管理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 apiVersion: v1 kind: ConfigMap metadata: name: prometheus-config namespace: infra data: prometheus.yml: | global: scrape_interval: 15s # 每隔15秒抓取一次 scrape_timeout: 15s # 抓取数据超时时间 scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090']
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 apiVersion: apps/v1 kind: Deployment metadata: labels: name: prometheus name: prometheus namespace: infra spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 7 selector: matchLabels: app: prometheus strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 type: RollingUpdate template: metadata: labels: app: prometheus spec: nodeName: k8s-master1 tolerations: - key: "node.kubernetes.io/unschedulable" operator: "Exists" effect: "NoSchedule" initContainers: - name: fix-permissions image: busybox command: ["sh" , "-c" , "chown -R nobody:nobody /data" ] volumeMounts: - name: data mountPath: /data containers: - name: prometheus image: prom/prometheus:v2.47.2 imagePullPolicy: IfNotPresent command: - /bin/prometheus args: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/data/prom-db' - '--storage.tsdb.retention=24h' - '--storage.tsdb.min-block-duration=10m' - '--web.enable-admin-api' - '--web.enable-lifecycle' ports: - containerPort: 9090 protocol: TCP name: http volumeMounts: - mountPath: /etc/prometheus/ name: config-volume - mountPath: /data/ name: data resources: requests: cpu: "100m" memory: "512Mi" limits: cpu: "500m" memory: "1Gi" serviceAccountName: prometheus volumes: - name: data persistentVolumeClaim: claimName: prometheus-data - name: config-volume configMap: name: prometheus-config
prometheus 的性能和数据持久化这里是直接将通过一个 LocalPV 来进行数据持久化的,通过 --storage.tsdb.path=/data/prom-db
指定数据目录,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 apiVersion: v1 kind: PersistentVolume metadata: name: prometheus-local labels: app: prometheus spec: accessModes: - ReadWriteOnce capacity: storage: 20Gi storageClassName: local-storage local: path: /data/k8s/prometheus nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - k8s-master1 persistentVolumeReclaimPolicy: Retain --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: prometheus-data namespace: infra spec: selector: matchLabels: app: prometheus accessModes: - ReadWriteOnce resources: requests: storage: 5Gi storageClassName: local-storage
prometheus 可以访问 Kubernetes 的一些资源对象,所以需要配置 rbac 相关认证,这里,使用了一个名为 prometheus 的 serviceAccount 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 apiVersion: v1 kind: ServiceAccount metadata: name: prometheus namespace: infra --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: prometheus rules: - apiGroups: - "" resources: - nodes - nodes/metrics - services - endpoints - pods verbs: - get - list - watch - apiGroups: - "" resources: - configmaps verbs: - get - nonResourceURLs: - /metrics verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: prometheus roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: prometheus subjects: - kind: ServiceAccount name: prometheus namespace: infra
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 apiVersion: v1 kind: Service metadata: annotations: prometheus.io/port: "9090" prometheus.io/scrape: "true" prometheus.io/path: "/metrics" name: prometheus namespace: infra spec: ports: - port: 9090 protocol: TCP targetPort: 9090 selector: app: prometheus
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: prometheus-web namespace: infra spec: entryPoints: - web routes: - match: Host(`prometheus.od.com`) kind: Rule services: - name: prometheus port: 9090
应用资源配置清单 1 2 3 4 5 6 7 8 # 创建挂载目录 mkdir /data/k8s/prometheus/ -p # 创建NameSpaces kubectl create namespace infra # 应用资源配置清单 kubectl apply -f .
创建完成可以通过本地hosts文件绑定域名进行访问:http://prometheus.od.com
现在可以查看当前监控系统中的一些监控目标(Status -> Targets):
应用监控 Prometheus 的数据指标是通过一个公开的 HTTP(S) 数据接口获取到的,不需要单独安装监控的 agent,只需要暴露一个 metrics 接口,Prometheus 就会定期去拉取数据;对于一些普通的 HTTP 服务,完全可以直接重用这个服务,添加一个 /metrics
接口暴露给 Prometheus;而且获取到的指标数据格式是非常易懂的,不需要太高的学习成本。
现在很多服务从一开始就内置了一个 /metrics
接口,比如 Kubernetes 的各个组件、istio 服务网格都直接提供了数据指标接口。有一些服务即使没有原生集成该接口,也完全可以使用一些 exporter
来获取到指标数据,比如 mysqld_exporter
、node_exporter
,这些 exporter
就有点类似于传统监控服务中的 agent,作为服务一直存在,用来收集目标服务的指标数据然后直接暴露给 Prometheus。
静态监控 官方抓取配置文档: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config
官方静态配置文档: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#static_config
应用只需要能够提供一个满足 prometheus 格式要求的 /metrics
接口就可以让 Prometheus 来接管监控,比如 Kubernetes 集群中非常重要的 ETCD集群,一般默认情况下就开启了 /metrics
接口,将/metrics
接口配置到prometheus.yml
中去,直接加到默认的 prometheus 这个 job
下面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 apiVersion: v1 kind: ConfigMap metadata: name: prometheus-config namespace: infra data: prometheus.yml: | global: scrape_interval: 15s scrape_timeout: 15s scrape_configs: - job_name: 'etcd' tls_config: # 配置抓取所有证书 ca_file: /data/ssl/ca.pem cert_file: /data/ssl/server.pem key_file: /data/ssl/server-key.pem scheme: https # 通过HTTPS协议抓取 static_configs: - targets: - '10.1.1.100:2379' labels: instance: etcd-server-1 # 创建instance标签 - targets: - '10.1.1.120:2379' labels: instance: etcd-server-2 - targets: - '10.1.1.130:2379' labels: instance: etcd-server-3
重新更新这个 ConfigMap 资源对象
1 kubectl apply -f prometheus-cm.yaml
由于之前的 Prometheus 启动参数中添加了 --web.enable-lifecycle
参数,所以现在只需要执行一个 reload
命令即可让配置生效:
1 curl -X POST "http://prometheus.od.com/-/reload"
由于 ConfigMap 通过 Volume 的形式挂载到 Pod 中去的热更新需要一定的间隔时间才会生效,所以需要稍微等一小会儿。
可以看到刚刚添加的 etcd 这个任务已经出现了,然后同样的可以切换到 Graph 下面去,可以找到一些 etcd 的指标数据,
使用 exporter 监控 上面也说过有一些应用可能没有自带 /metrics
接口供 Prometheus 使用,在这种情况下,就需要利用 exporter
服务来为 Prometheus 提供指标数据了。Prometheus 官方为许多应用就提供了对应的 exporter
应用,也有许多第三方的实现,可以前往官方网站进行查看:exporters ,当然如果你的应用本身也没有 exporter 实现,那么就要自己想办法去实现一个 /metrics
接口了,只要你能提供一个合法的 /metrics
接口,Prometheus 就可以监控你的应用。
比如这里通过一个 redis-exporter 的服务来监控 redis 服务,对于这类应用,一般会以 sidecar
的形式和主应用部署在同一个 Pod 中,比如这里来部署一个 redis 应用,并用 redis-exporter 的方式来采集监控数据供 Prometheus 使用,如下资源清单文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 apiVersion: apps/v1 kind: Deployment metadata: name: redis namespace: monitor spec: selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:4 resources: requests: cpu: 100m memory: 100Mi ports: - containerPort: 6379 - name: redis-exporter image: oliver006/redis_exporter:latest resources: requests: cpu: 100m memory: 100Mi ports: - containerPort: 9121 --- kind: Service apiVersion: v1 metadata: name: redis namespace: monitor spec: selector: app: redis ports: - name: redis port: 6379 targetPort: 6379 - name: prom port: 9121 targetPort: 9121
可以看到上面在 redis 这个 Pod 中包含了两个容器,一个就是 redis 本身的主应用,另外一个容器就是 redis_exporter。现在直接创建上面的应用:
1 2 3 $ kubectl apply -f prome-redis.yaml deployment.apps/redis created service/redis created
创建完成后,可以看到 redis 的 Pod 里面包含有两个容器:
1 2 3 4 5 6 $ kubectl get pods -n monitor NAME READY STATUS RESTARTS AGE redis-7c8bdd45cc-ssjbz 2/2 Running 0 2m1s $ kubectl get svc -n monitor NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE redis ClusterIP 192.168.11.251 <none> 6379/TCP,9121/TCP 2m14s
可以通过 9121 端口来校验是否能够采集到数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ curl 172.16.120.15:9121/metrics # HELP go_gc_duration_seconds A summary of the GC invocation durations. # TYPE go_gc_duration_seconds summary go_gc_duration_seconds{quantile="0"} 0 go_gc_duration_seconds{quantile="0.25"} 0 go_gc_duration_seconds{quantile="0.5"} 0 go_gc_duration_seconds{quantile="0.75"} 0 go_gc_duration_seconds{quantile="1"} 0 go_gc_duration_seconds_sum 0 go_gc_duration_seconds_count 0 ...... # HELP redis_up Information about the Redis instance # TYPE redis_up gauge redis_up 1 # HELP redis_uptime_in_seconds uptime_in_seconds metric # TYPE redis_uptime_in_seconds gauge redis_uptime_in_seconds 100
同样的,现在只需要更新 Prometheus 的配置文件:
1 2 3 - job_name: 'redis' static_configs: - targets: ['redis.monitor:9121' ]
由于这里是通过 Service 去配置的 redis 服务,当然直接配置 Pod IP 也是可以的,因为和 Prometheus 处于同一个 namespace,所以直接使用 servicename 即可。配置文件更新后,重新加载:
1 2 3 4 $ kubectl apply -f prometheus-cm.yaml configmap/prometheus-config configured # 隔一会儿执行reload操作 $ curl -X POST "http://prometheus.od.com/-/reload"
这个时候再去看 Prometheus 的 Dashboard 中查看采集的目标数据:
可以看到配置的 redis 这个 job 已经生效了。切换到 Graph 下面可以看到很多关于 redis 的指标数据,选择任意一个指标,比如 redis_exporter_scrapes_total
,然后点击执行就可以看到对应的数据图表了:
集群节点 对于集群的监控一般需要考虑以下几个方面:
Kubernetes 节点的监控:比如节点的 cpu、load、disk、memory 等指标 内部系统组件的状态:比如 kube-scheduler、kube-controller-manager、kubedns/coredns 等组件的详细运行状态 编排级的 metrics:比如 Deployment 的状态、资源请求、调度和 API 延迟等数据指标 Kubernetes 集群的监控方案目前主要有以下几种方案:
Heapster:Heapster 是一个集群范围的监控和数据聚合工具,以 Pod 的形式运行在集群中。 heapster 除了 Kubelet/cAdvisor 之外,还可以向 Heapster 添加其他指标源数据,比如 kube-state-metrics,需要注意的是 Heapster 已经被废弃了,后续版本中会使用 metrics-server 代替。 cAdvisor:cAdvisor 是 Google 开源的容器资源监控和性能分析工具,它是专门为容器而生,本身也支持 Docker 容器。 kube-state-metrics:kube-state-metrics 通过监听 API Server 生成有关资源对象的状态指标,比如 Deployment、Node、Pod,需要注意的是 kube-state-metrics 只是简单提供一个 metrics 数据,并不会存储这些指标数据,所以可以使用 Prometheus 来抓取这些数据然后存储。 metrics-server:metrics-server 也是一个集群范围内的资源数据聚合工具,是 Heapster 的替代品,同样的,metrics-server 也只是显示数据,并不提供数据存储服务。 不过 kube-state-metrics 和 metrics-server 之间还是有很大不同的,二者的主要区别如下:
kube-state-metrics 主要关注的是业务相关的一些元数据,比如 Deployment、Pod、副本状态等 metrics-server 主要关注的是资源度量 API 的实现,比如 CPU、文件描述符、内存、请求延时等指标。 监控集群节点 Prometheus 采集节点的监控指标数据,可以通过 node_exporter 来获取,顾名思义,node_exporter
就是抓取用于采集服务器节点的各种运行指标,目前 node_exporter
支持几乎所有常见的监控点,比如 conntrack,cpu,diskstats,filesystem,loadavg,meminfo,netstat 等,详细的监控点列表可以参考其 Github 仓库 。
可以通过 DaemonSet 控制器来部署该服务,这样每一个节点都会自动运行一个这样的 Pod,如果从集群中删除或者添加节点后,也会进行自动扩展。
在部署 node-exporter
的时候有一些细节需要注意,如下资源清单文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 apiVersion: apps/v1 kind: DaemonSet metadata: name: node-exporter namespace: kube-system labels: app: node-exporter spec: selector: matchLabels: app: node-exporter template: metadata: labels: app: node-exporter spec: hostPID: true hostIPC: true hostNetwork: true nodeSelector: kubernetes.io/os: linux containers: - name: node-exporter image: prom/node-exporter:v1.6.1 args: - --web.listen-address=$(HOSTIP):9100 - --path.procfs=/host/proc - --path.sysfs=/host/sys - --path.rootfs=/host/root - --no-collector.hwmon - --no-collector.nfs - --no-collector.nfsd - --no-collector.nvme - --no-collector.dmi - --no-collector.arp - --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/containerd/.+|/var/lib/docker/.+|var/lib/kubelet/pods/.+)($|/) - --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$ ports: - containerPort: 9100 env: - name: HOSTIP valueFrom: fieldRef: fieldPath: status.hostIP resources: requests: cpu: 150m memory: 180Mi limits: cpu: 150m memory: 180Mi securityContext: runAsNonRoot: true runAsUser: 65534 volumeMounts: - name: proc mountPath: /host/proc - name: sys mountPath: /host/sys - name: root mountPath: /host/root mountPropagation: HostToContainer readOnly: true tolerations: - operator: 'Exists' volumes: - name: proc hostPath: path: /proc - name: dev hostPath: path: /dev - name: sys hostPath: path: /sys - name: root hostPath: path: /
要获取到的数据是主机的监控指标数据,而 node-exporter
是运行在容器中的,所以在 Pod 中需要配置一些 Pod 的安全策略,这里就添加了 hostPID: true
、hostIPC: true
、hostNetwork: true
3 个策略,用来使用主机的 PID namespace
、IPC namespace
以及主机网络,这些 namespace 就是用于容器隔离的关键技术,要注意这里的 namespace 和集群中的 namespace 是两个完全不相同的概念。
另外还将主机的 /dev
、/proc
、/sys
这些目录挂载到容器中,这些因为采集的很多节点数据都是通过这些文件夹下面的文件来获取到的,比如在使用 top
命令可以查看当前 cpu 使用情况,数据就来源于文件 /proc/stat
,使用 free
命令可以查看当前内存使用情况,其数据来源是来自 /proc/meminfo
文件。
1 2 3 4 5 $ kubectl get pods -n kube-system -l app=node-exporter -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES node-exporter-q9bq8 1/1 Running 1 (27h ago) 27h 10.1.1.130 k8s-node2 <none> <none> node-exporter-wlwk9 1/1 Running 1 (27h ago) 27h 10.1.1.100 k8s-master1 <none> <none> node-exporter-xr47x 1/1 Running 1 (27h ago) 27h 10.1.1.120 k8s-node1 <none> <none>
部署完成后,可以看到在几个节点上都运行了一个 Pod,由于指定了 hostNetwork=true
,所以在每个节点上就会绑定一个端口 9100,可以通过这个端口去获取到监控指标数据:
1 2 3 4 $ curl 10.1.1.100:9100/metrics ...... # TYPE node_os_info gauge node_os_info{build_id="",id="centos",id_like="rhel fedora",image_id="",image_version="",name="CentOS Linux",pretty_name="CentOS Linux 7 (Core)",variant="",variant_id="",version="7 (Core)",version_codename="",version_id="7"} 1
自动服务发现 在 Kubernetes 下,Promethues 通过与 Kubernetes API 集成,主要支持 5 中服务发现模式,分别是:Node
、Service
、Pod
、Endpoints
、Ingress
。不同的服务发现模式适用于不同的场景,例如:Node
适用于与主机相关的监控资源,如节点中运行的Kubernetes 组件状态、节点上运行的容器状态等;Service
和 Ingress
适用于通过黑盒监控的场景,如对服务的可用性以及服务质量的监控;Endpoints
和 Pod
均可用于获取 Pod 实例的监控数据,如监控用户或者管理员部署的支持 Prometheus 的应用。
通过 kubectl 命令可以很方便的获取到当前集群中的所有节点信息:
1 2 3 4 5 $ kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master1 Ready,SchedulingDisabled <none> 44d v1.23.6 k8s-node1 Ready <none> 44d v1.23.6 k8s-node2 Ready <none> 44d v1.23.6
但是要让 Prometheus 也能够获取到当前集群中的所有节点信息的话,就需要利用 Node 的服务发现模式,同样的,在 prometheus.yml
文件中配置如下的 job 任务即可:
1 2 3 - job_name: 'k8s-nodes' kubernetes_sd_configs: - role: node
通过指定 kubernetes_sd_configs
的模式为 node
,Prometheus 就会自动从 Kubernetes 中发现所有的 node 节点并作为当前 job 监控的目标实例,发现的节点 /metrics
接口是默认的 kubelet 的 HTTP 接口。
prometheus 的 ConfigMap 更新完成后,同样的执行 reload 操作,让配置生效:
1 2 3 4 $ kubectl apply -f prometheus-cm.yaml configmap/prometheus-config configured # 隔一会儿执行reload操作 $ curl -X POST "http://prometheus.od.com/-/reload"
可以看到上面的 k8s-nodes
这个 job 任务已经自动发现了 3 个 node 节点,但是在获取数据的时候失败了。
这个是因为 prometheus 去发现 Node 模式的服务的时候,访问的端口默认是 10250,而默认是需要认证的 https 协议才有权访问的,但实际上并不是希望让去访问 10250 端口的 /metrics
接口,而是 node-exporter
绑定到节点的 9100 端口,所以应该将这里的 10250
替换成 9100
端口。
需要使用到 Prometheus 提供的 relabel_configs
中的 replace
能力了,relabel
可以在 Prometheus 采集数据之前,通过 Target 实例的 Metadata
信息,动态重新写入 Label 的值。除此之外,还能根据 Target 实例的 Metadata
信息选择是否采集或者忽略该 Target 实例。比如这里就可以去匹配 __address__
这个 Label 标签,然后替换掉其中的端口,如果你不知道有哪些 Label 标签可以操作的话,可以在 Service Discovery
页面获取到相关的元标签,这些标签都是可以进行 Relabel 的标签:
1 2 3 4 5 6 7 8 9 10 11 - job_name: 'k8s-nodes' kubernetes_sd_configs: - role: node relabel_configs: - source_labels: [__address__ ] regex: '(.*):10250' replacement: '${1}:9100' target_label: __address__ action: replace - action: labelmap regex: __meta_kubernetes_node_label_(.+)
这个正则表达式表示:去匹配 __address__
这个标签,然后将 host 部分保留下来,port 替换成了 9100,重新更新配置文件,执行 reload 操作,然后再去看 Prometheus 的 Dashboard 的 Targets 路径下面 kubernetes-nodes 这个 job 任务是否正常了:
可以看到现在已经正常了,但是还有一个问题就是采集的指标数据 Label 标签中内容缺少,这对于在进行监控分组分类查询的时候带来了很多不方便的地方,要是能够将集群中 Node 节点的 Label 标签也能获取到就很好了。这里可以通过 labelmap
这个属性来将 Kubernetes 的 Label 标签添加为 Prometheus 的指标数据的标签:
1 2 3 4 5 6 7 8 9 10 11 - job_name: 'k8s-nodes' kubernetes_sd_configs: - role: node relabel_configs: - source_labels: [__address__ ] regex: '(.*):10250' replacement: '${1}:9100' target_label: __address__ action: replace - action: labelmap regex: __meta_kubernetes_node_label_(.+)
添加了一个 action 为 labelmap
,正则表达式是 __meta_kubernetes_node_label_(.+)
的配置,这里的意思就是表达式中匹配都的数据也添加到指标数据的 Label 标签中去。
对于 kubernetes_sd_configs
下面可用的元信息标签如下:
__meta_kubernetes_node_name
:节点对象的名称_meta_kubernetes_node_label
:节点对象中的每个标签_meta_kubernetes_node_annotation
:来自节点对象的每个注释_meta_kubernetes_node_address
:每个节点地址类型的第一个地址(如果存在)关于 kubernets_sd_configs 更多信息可以查看官方文档:kubernetes_sd_config
kubelet 监控 kubelet 也自带了一些监控指标数据,就上面提到的 10250 端口,所以这里也把 kubelet 的监控任务也一并配置上:
1 2 3 4 5 6 7 8 9 10 11 - job_name: 'kubelet' kubernetes_sd_configs: - role: node scheme: https tls_config: insecure_skip_verify: true ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+)
这里需要特别注意的是这里必须使用 https
协议访问,这样就必然需要提供证书,这里是通过配置 insecure_skip_verify: true
来跳过了证书校验,但是除此之外,要访问集群的资源,还必须要有对应的权限才可以,也就是对应的 ServiceAccount 棒的 权限允许才可以,这里部署的 prometheus 关联的 ServiceAccount 对象前面已经提到过了,这里只需要将 Pod 中自动注入的 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
和 /var/run/secrets/kubernetes.io/serviceaccount/token
文件配置上,就可以获取到对应的权限了。
现在再去更新下配置文件,执行 reload 操作,让配置生效,然后访问 Prometheus 的 Dashboard 查看 Targets 路径:
现在可以看到上面添加的 kubernetes-kubelet
和 kubernetes-nodes
这两个 job 任务都已经配置成功了,而且二者的 Labels 标签都和集群的 node 节点标签保持一致了。
现在就可以切换到 Graph 路径下面查看采集的一些指标数据了,比如查询 node_load1 指标:
可以看到将几个节点对应的 node_load1
指标数据都查询出来了,同样的,还可以使用 PromQL
语句来进行更复杂的一些聚合查询操作,还可以根据的 Labels 标签对指标数据进行聚合,比如这里只查询 node1
节点的数据,可以使用表达式 node_load1{instance="k8s-master1"}
来进行查询:
容器监控 说到容器监控自然会想到 cAdvisor
,前面也说过 cAdvisor 已经内置在了 kubelet 组件之中,所以不需要单独去安装,cAdvisor
的数据路径为 /api/v1/nodes/<node>/proxy/metrics
,但是不推荐使用这种方式,因为这种方式是通过 APIServer 去代理访问的,对于大规模的集群比如会对 APIServer 造成很大的压力,所以可以直接通过访问 kubelet 的 /metrics/cadvisor
这个路径来获取 cAdvisor 的数据, 同样这里使用 node 的服务发现模式,因为每一个节点下面都有 kubelet,自然都有 cAdvisor
采集到的数据指标,配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 - job_name: 'kubernetes-cadvisor' kubernetes_sd_configs: - role: node scheme: https metrics_path: /metrics/cadvisor tls_config: insecure_skip_verify: true ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+) replacement: $1
上面的配置和之前配置 node-exporter
的时候几乎是一样的,区别是这里使用了 https 的协议,另外需要注意的是配置了 ca.cart 和 token 这两个文件,这两个文件是 Pod 启动后自动注入进来的,然后加上 __metrics_path__
的访问路径 /metrics/cadvisor
,现在同样更新下配置,然后查看 Targets 路径:
可以切换到 Graph 路径下面查询容器相关数据,比如这里来查询集群中所有 Pod 的 CPU 使用情况,kubelet 中的 cAdvisor 采集的指标和含义,可以查看 Monitoring cAdvisor with Prometheus 说明,其中有一项:
1 container_cpu_usage_seconds_total Counter Cumulative cpu time consumed seconds
container_cpu_usage_seconds_total
是容器累计使用的 CPU 时间,用它除以 CPU 的总时间,就可以得到容器的 CPU 使用率了:
首先计算容器的 CPU 占用时间,由于节点上的 CPU 有多个,所以需要将容器在每个 CPU 上占用的时间累加起来,Pod 在 1m 内累积使用的 CPU 时间为:(根据 pod 和 namespace 进行分组查询)
1 sum(rate(container_cpu_usage_seconds_total{image!="",pod!=""}[1m])) by (namespace, pod)
然后计算 CPU 的总时间,这里的 CPU 数量是容器分配到的 CPU 数量,container_spec_cpu_quota
是容器的 CPU 配额,它的值是容器指定的 CPU 个数 * 100000
,所以 Pod 在 1s 内 CPU 的总时间为:Pod 的 CPU 核数 * 1s:
1 sum(container_spec_cpu_quota{image!="", pod!=""}) by(namespace, pod) / 100000
CPU 配额
由于 container_spec_cpu_quota
是容器的 CPU 配额,所以只有配置了 resource-limit CPU 的 Pod 才可以获得该指标数据。
将上面这两个语句的结果相除,就得到了容器的 CPU 使用率:
1 2 3 (sum(rate(container_cpu_usage_seconds_total{image!="",pod!=""}[1m])) by (namespace, pod)) / (sum(container_spec_cpu_quota{image!="", pod!=""}) by(namespace, pod) / 100000) * 100
在 promethues 里面执行上面的 promQL 语句可以得到下面的结果:
Pod 内存使用率的计算就简单多了,直接用内存实际使用量除以内存限制使用量即可:
1 sum(container_memory_rss{image!=""}) by(namespace, pod) / sum(container_spec_memory_limit_bytes{image!=""}) by(namespace, pod) * 100 != +inf
在 promethues 里面执行上面的 promQL 语句可以得到下面的结果:
监控 Pod 为解决服务发现的问题,prometheus 为提供了一个额外的抓取配置来解决这个问题,可以通过添加额外的配置来进行服务发现进行自动监控。可以在 kubernetes 当中去自动发现并监控具有 prometheus.io/scrape=true 这个 annotations 的 Service是专门发现普通类型的 Endpoint,其实就是 Service 关联的 Pod 列表。 其中通过 kubernetes_sd_configs 支持监控其各种资源。kubernetes SD 配置允许从 kubernetes REST API 接受搜集指标,且总是和集群保持同步状态,任何一种 role 类型都能够配置来发现想要的对象。
规则配置使用 yaml 格式,下面是文件中一级配置项。自动发现 k8s Metrics 接口是通过 scrape_configs 来实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 - job_name: 'kubernetes-pods' 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_pod_name ] action: replace target_label: kubernetes_pod_name
要想自动发现集群中的 Endpoint,就需要在 Service 的 annotation
区域添加 prometheus.io/scrape=true
的声明,现在先将上面的配置更新,在部署prometheus时,已经将这些声明都加到Service中了,查看下效果:
1 2 3 4 5 metadata: annotations: prometheus.io/port: "9090" prometheus.io/scrape: "true" prometheus.io/path: "/metrics"
kube-state-metrics 上面配置了自动发现 Endpoints 的监控,但是这些监控数据都是应用内部的监控,需要应用本身提供一个 /metrics
接口,或者对应的 exporter 来暴露对应的指标数据,但是在 Kubernetes 集群上 Pod、DaemonSet、Deployment、Job、CronJob 等各种资源对象的状态也需要监控,这也反映了使用这些资源部署的应用的状态。比如:
我调度了多少个副本?现在可用的有几个? 多少个 Pod 是 running/stopped/terminated
状态? Pod 重启了多少次? 我有多少 job 在运行中等等 通过查看前面从集群中拉取的指标(这些指标主要来自 apiserver 和 kubelet 中集成的 cAdvisor),并没有具体的各种资源对象的状态指标。对于 Prometheus 来说,当然是需要引入新的 exporter 来暴露这些指标,Kubernetes 提供了一个kube-state-metrics 就是需要的。
与 metric-server 的对比 metric-server
是从 APIServer 中获取 cpu、内存使用率这种监控指标,并把他们发送给存储后端,如 influxdb 或云厂商,当前的核心作用是为 HPA 等组件提供决策指标支持。kube-state-metrics
关注于获取 Kubernetes 各种资源的最新状态,如 deployment 或者 daemonset,metric-server 仅仅是获取、格式化现有数据,写入特定的存储,实质上是一个监控系统。而 kube-state-metrics 是获取集群最新的指标。像 Prometheus 这种监控系统,并不会去用 metric-server 中的数据,他都是自己做指标收集、集成的,但 Prometheus 可以监控 metric-server 本身组件的监控状态并适时报警,这里的监控就可以通过 kube-state-metrics
来实现,如 metric-server pod 的运行状态。 创建资源配置清单 kube-state-metrics 已经给出了在 Kubernetes 部署的 manifest 定义文件,直接将代码 Clone 到集群中(能用 kubectl 工具操作就行),不过需要注意兼容的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: app.kubernetes.io/component: exporter app.kubernetes.io/name: kube-state-metrics app.kubernetes.io/version: 2.10 .0 name: kube-state-metrics rules: - apiGroups: - "" resources: - configmaps - secrets - nodes - pods - services - serviceaccounts - resourcequotas - replicationcontrollers - limitranges - persistentvolumeclaims - persistentvolumes - namespaces - endpoints verbs: - list - watch - apiGroups: - apps resources: - statefulsets - daemonsets - deployments - replicasets verbs: - list - watch - apiGroups: - batch resources: - cronjobs - jobs verbs: - list - watch - apiGroups: - autoscaling resources: - horizontalpodautoscalers verbs: - list - watch - apiGroups: - authentication.k8s.io resources: - tokenreviews verbs: - create - apiGroups: - authorization.k8s.io resources: - subjectaccessreviews verbs: - create - apiGroups: - policy resources: - poddisruptionbudgets verbs: - list - watch - apiGroups: - certificates.k8s.io resources: - certificatesigningrequests verbs: - list - watch - apiGroups: - discovery.k8s.io resources: - endpointslices verbs: - list - watch - apiGroups: - storage.k8s.io resources: - storageclasses - volumeattachments verbs: - list - watch - apiGroups: - admissionregistration.k8s.io resources: - mutatingwebhookconfigurations - validatingwebhookconfigurations verbs: - list - watch - apiGroups: - networking.k8s.io resources: - networkpolicies - ingressclasses - ingresses verbs: - list - watch - apiGroups: - coordination.k8s.io resources: - leases verbs: - list - watch - apiGroups: - rbac.authorization.k8s.io resources: - clusterrolebindings - clusterroles - rolebindings - roles verbs: - list - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/component: exporter app.kubernetes.io/name: kube-state-metrics app.kubernetes.io/version: 2.10 .0 name: kube-state-metrics roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: kube-state-metrics subjects: - kind: ServiceAccount name: kube-state-metrics namespace: kube-system --- apiVersion: v1 automountServiceAccountToken: false kind: ServiceAccount metadata: labels: app.kubernetes.io/component: exporter app.kubernetes.io/name: kube-state-metrics app.kubernetes.io/version: 2.10 .0 name: kube-state-metrics namespace: kube-system
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 apiVersion: apps/v1 kind: Deployment metadata: labels: app.kubernetes.io/component: exporter app.kubernetes.io/name: kube-state-metrics app.kubernetes.io/version: 2.10 .0 name: kube-state-metrics namespace: kube-system spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: kube-state-metrics template: metadata: annotations: prometheus.io/path: "/metrics" prometheus.io/scheme: "kube-state-metrics" prometheus.io/port: "8080" labels: app.kubernetes.io/component: exporter app.kubernetes.io/name: kube-state-metrics app.kubernetes.io/version: 2.10 .0 spec: automountServiceAccountToken: true containers: - image: wangxiansen/kube-state-metrics:v2.10.0 livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 timeoutSeconds: 5 name: kube-state-metrics ports: - containerPort: 8080 name: http-metrics - containerPort: 8081 name: telemetry readinessProbe: httpGet: path: / port: 8081 initialDelaySeconds: 5 timeoutSeconds: 5 securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 65534 seccompProfile: type: RuntimeDefault nodeSelector: kubernetes.io/os: linux serviceAccountName: kube-state-metrics
默认的镜像为 gcr 的,这里可以将 deployment.yaml
下面的镜像替换成 wangxiansen/kube-state-metrics:v2.10.0
,此外上面为 Prometheus 配置了 Pod的自动发现,所以可以给 kube-state-metrics 的 Pod 配置上对应的 annotations 来自动被发现,然后直接创建即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 - job_name: 'kube-state-metrics' kubernetes_sd_configs: - role: pod # 针对单独Pod做自动发现 relabel_configs: - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme] action: keep regex: kube-state-metrics - 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_namespace] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_pod_name] action: replace target_label: kubernetes_pod_name metric_relabel_configs: - target_label: cluster replacement: k8s-version-1.23
部署完成后正常就可以被 Prometheus 采集到指标了:
水平缩放(分片) kube-state-metrics
已经内置实现了一些自动分片功能,可以通过 --shard
和 --total-shards
参数进行配置。现在还有一个实验性功能,如果将 kube-state-metrics
部署在 StatefulSet 中,它可以自动发现 其命名位置,以便自动配置分片,这是一项实验性功能,可能以后会被移除。
要启用自动分片,必须运行一个 kube-state-metrics 的 StatefulSet,并且必须通过 --pod
和 --pod-namespace
标志将 pod 名称和名称空间传递给 kube-state-metrics
进程。可以参考 /examples/autosharding
目录下面的示例清单文件进行说明。
使用 使用 kube-state-metrics 的一些典型场景:
存在执行失败的 Job: kube_job_status_failed
集群节点状态错误: kube_node_status_condition{condition="Ready", status!="true"}==1
集群中存在启动失败的 Pod:kube_pod_status_phase{phase=~"Failed|Unknown"}==1
最近 30 分钟内有 Pod 容器重启: changes(kube_pod_container_status_restarts_total[30m])>0
关于 kube-state-metrics 的更多用法可以查看官方 GitHub 仓库:https://github.com/kubernetes/kube-state-metrics
最终Prometheus配置文件 Prometheus配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 apiVersion: v1 kind: ConfigMap metadata: name: prometheus-config namespace: infra data: prometheus.yml: | global: scrape_interval: 15s scrape_timeout: 15s scrape_configs: - job_name: 'etcd' tls_config: ca_file: /data/ssl/ca.pem cert_file: /data/ssl/server.pem key_file: /data/ssl/server-key.pem scheme: https static_configs: - targets: - '10.1.1.100:2379' labels: instance: etcd-server-1 - targets: - '10.1.1.120:2379' labels: instance: etcd-server-2 - targets: - '10.1.1.130:2379' labels: instance: etcd-server-3 - job_name: 'kubernetes-apiservers' kubernetes_sd_configs: - role: endpoints scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] action: keep regex: default;kubernetes;https - job_name: 'k8s-nodes' kubernetes_sd_configs: - role: node relabel_configs: - source_labels: [__address__] regex: '(.*):10250' replacement: '${1}:9100' target_label: __address__ action: replace - action: labelmap regex: __meta_kubernetes_node_label_(.+) - job_name: 'kubelet' kubernetes_sd_configs: - role: node scheme: https tls_config: insecure_skip_verify: true ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+) - job_name: 'kubernetes-cadvisor' kubernetes_sd_configs: - role: node scheme: https metrics_path: /metrics/cadvisor tls_config: insecure_skip_verify: true ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+) replacement: $1 - job_name: 'kubernetes-pods' 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+) # RE2 正则规则,+是一次多多次,?是0次或1次,其中?:表示非匹配组(意思就是不获取匹配结果) 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_pod_name] action: replace target_label: kubernetes_pod_name - job_name: 'traefik' kubernetes_sd_configs: - role: pod relabel_configs: - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme] action: keep regex: traefik - 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_namespace] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_pod_name] action: replace target_label: kubernetes_pod_name - job_name: 'kube-state-metrics' kubernetes_sd_configs: - role: pod relabel_configs: - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme] action: keep regex: kube-state-metrics - 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_namespace] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_pod_name] action: replace target_label: kubernetes_pod_name metric_relabel_configs: - target_label: cluster replacement: k8s-version-1.23