【云原生•监控】基于Prometheus的云原生集群监控(理论+实践)-01
前言
「笔者已经在公有云上搭建了一套临时环境,可以先登录体验下:」
http://124.222.45.207:17000/login
账号:root/root.2020
云原生监控挑战
Prometheus 是用 Go 语言编写,从一开始就是开源的,到 2016 年 Prometheus 成为继 Kubernetes 之后,成为 CNCF 的第二个成员。近几年Prometheus的火热,和云原生日趋流行是密不可分的,并且现在已成为云原生生态中监控的事实标准。
传统的监控霸主Zabbix在面对云原生监控时,主要面临如下几个挑战:
监控对象数量增大:传统监控以单体应用为粒度,结合计算、存储、网络等基础设施监控进行运维保障。但在容器化微服务架构下,监控粒度细致到容器POD或微服务API级别,使得监控对象的数量相比单体应用呈指数级增长。
监控指标海量:
资源对象类型多,如:Container、Pod、Service、Deployment、ReplicaSet、Endpoint、Ingress、PV、PVC等等;
一个云原生集群可能有几万甚至几十万Pod;
一个云原生集群可能有几千上万级别的Service、Endpoint、Deployment、Ingress等;
海量的监控指标对数据写入、查询性能要求较高,以及存储空间占用优化等,才能承载海量的监控资源
对象动态变更较为频繁,资源对象的扩缩容、资源对象生命周期大大缩短,甚至有些对象朝生夕死等等,这就会带来两个问题:
采集目标不能使用传统的静态配置方式,而是要基于服务发现机制能及时的感知到变化,并进行快速调整;
资源对象扩缩容等导致的资源对象生命周期大大缩短是很容易导致指标数量的快速膨胀,久而久之会影响整个监控系统性能;
Zabbix出现得比较早,当时容器还没有诞生,自然对容器的支持比较差,而Prometheus的TSDB时序数据存储机制、丰富的服务发现机制等,基本就是为云原生量身定做的,Prometheus开始成为容器监控方面的标配,并且在未来可见的时间内会被广泛应用。
云原生监控方案
kubernetes
云原生集群非常复杂,概况总结下我们主要关注的无外乎下面五大块指标:
容器基础资源指标:组件运行的外部环境相关性能指标,传统场景下组件运行的外部环境是主机,而云原生环境下组件的外部环境是容器,主机我们需要关注CPU、内存、存储、磁盘IO、网络IO等相关指标,同理容器基础资源指标也存在类似相关指标,如:Container的CPU使用率、内存使用率、存储空间、磁盘读写IO和网络IO等。
k8s资源对象指标:容器是最底层的运行时组件,k8s作为强大的调度协调这些容器的平台,抽象定义出了很多资源对象,如Pod、Service、Deployment、ReplicaSet、DaemonSet、Ingress、StatefulSet、ConfigMap、ServiceAccount等等,资源对象指标就是对这些k8s定义的元数据信息进行监控。
k8s服务组件指标:k8s作为一个复杂的集群,自身存在很多组件,如master节点上运行着:api-server组件、etcd组件、kube-scheduler组件、kube-controller-manager组件、coredns组件等,node节点上有kubelet组件、kube-proxy组件等,作为云原生集群的运维人员,肯定要关注这些核心组件的运行状况,避免某些组件性能瓶颈、异常奔溃导致整个云原生集群性能低下甚至不可用。
k8s集群Node指标:云原生集群的各种组件、业务容器等最终都是运行在Node节点上,因此,Node节点的性能、异常情况等对整个云原生集群的影响也是非常大的,所以,我们也需要特别关注Node节点性能指标。
云原生上层业务指标:上面说的主要是云原生集群底层相关基础指标,云原生作为平台上面自然部署很多业务组件,比如PaaS中间件、业务应用服务等,这些组件运行在云原生上层本身也存在相关指标监控。
环境准备
为了监控上述云原生指标,首先,我们需要准备Prometheus
、Grafana
环境。
Prometheus部署
1、为了方便管理,我们将监控相关的所有资源对象都安装在 monitoring
这个 namespace
下面,没有的话可以提前创建:
[root@k8s-01 prometheus]# kubectl create ns monitoring
namespace/monitoring created
2、为了能够方便的管理配置文件,我们这里将 prometheus.yml
配置文件用 ConfigMap
的形式进行管理:
# prometheus-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitoring
data:
prometheus.yml: |
global:
scrape_interval: 15s
scrape_timeout: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
我们这里暂时只配置了对 prometheus
本身的监控,直接创建该资源对象:
[root@k8s-01 prometheus]# kubectl apply -f prometheus-config.yaml
configmap/prometheus-config created
配置文件创建完成了,以后如果我们有新的资源需要被监控,我们只需要将上面的 ConfigMap 对象更新即可。
3、现在我们来创建 prometheus
的 Pod
资源:
# prometheus-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: monitoring
labels:
app: prometheus
spec:
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
serviceAccountName: prometheus
containers:
- image: prom/prometheus:v2.31.1
name: prometheus
args:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus" # 指定tsdb数据路径
- "--storage.tsdb.retention.time=24h"
- "--web.enable-admin-api" # 控制对admin HTTP API的访问,其中包括删除时间序列等功能
- "--web.enable-lifecycle" # 支持热更新,直接执行localhost:9090/-/reload立即生效
ports:
- containerPort: 9090
name: http
volumeMounts:
- mountPath: "/etc/prometheus"
name: config-volume
- mountPath: "/prometheus"
name: data
resources:
requests:
cpu: 200m
memory: 1024Mi
limits:
cpu: 200m
memory: 1024Mi
- image: jimmidyson/configmap-reload:v0.4.0 #prometheus配置动态加载
name: prometheus-reload
securityContext:
runAsUser: 0
args:
- "--volume-dir=/etc/config"
- "--webhook-url=http://localhost:9090/-/reload"
volumeMounts:
- mountPath: "/etc/config"
name: config-volume
resources:
requests:
cpu: 100m
memory: 50Mi
limits:
cpu: 100m
memory: 50Mi
volumes:
- name: data
persistentVolumeClaim:
claimName: prometheus-data
- configMap:
name: prometheus-config
name: config-volume
持久化
另外为了 prometheus
的性能和数据持久化我们这里是直接将通过一个 LocalPV
来进行数据持久化的,**注意一定不能使用 nfs 来持久化数据(TSDB时序库不支持nfs存储,会存在丢失数据风险)**,通过 --storage.tsdb.path=/prometheus
指定数据目录,创建如下所示的一个 PVC
资源对象,注意是一个 LocalPV
,和 k8s-02
节点具有亲和性:
#prometheus-storage.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
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 #确保主机节点上存在该目录
persistentVolumeReclaimPolicy: Retain
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-02
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: prometheus-data
namespace: monitoring
spec:
selector:
matchLabels:
app: prometheus
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: local-storage
这里的volumeBindingMode: WaitForFirstConsumer
意思就是延迟绑定,当有符合PVC要求的PV不立即绑定。因为POD关联PVC,而绑定之后,POD被调度到其他节点,显然其他节点很有可能没有那个PV所以POD就挂起了,另外就算该节点有合适的PV,而POD被设置成不能运行在该节点,这时候就没法了,延迟绑定的好处是,POD的调度要参考卷的分布。当开始调度POD的时候看看它要求的LPV在哪里,然后就调度到该节点,然后进行PVC的绑定,最后在挂载到POD中,这样就保证了POD所在的节点就一定是LPV所在的节点。所以让PVC延迟绑定,就是等到使用这个PVC的POD出现在调度器上之后(真正被调度之前),然后根据综合评估再来绑定这个PVC。
[root@k8s-01 prometheus]# kubectl get pvc -n monitoring
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
prometheus-data Pending local-storage 4m9s
RBAC权限
由于 prometheus 可以访问 Kubernetes 的一些资源对象,所以需要配置 rbac 相关认证,这里我们使用了一个名为 prometheus 的 serviceAccount 对象:
# rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus
rules:
- apiGroups:
- ""
resources:
- nodes
- services
- endpoints
- pods
- nodes/proxy
verbs:
- get
- list
- watch
- apiGroups:
- "extensions"
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- configmaps
- nodes/metrics
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: monitoring
由于我们要获取的资源信息,在每一个 namespace
下面都有可能存在,所以我们这里使用的是 ClusterRole
的资源对象,值得一提的是我们这里的权限规则声明中有一个 nonResourceURLs
的属性,是用来对非资源型 metrics 进行操作的权限声明,这个在以前我们很少遇到过,然后直接创建上面的资源对象即可:
[root@k8s-01 prometheus]# kubectl apply -f rbac.yaml
serviceaccount/prometheus created
clusterrole.rbac.authorization.k8s.io/prometheus created
clusterrolebinding.rbac.authorization.k8s.io/prometheus created
[root@k8s-01 prometheus]# kubectl get sa -n monitoring
NAME SECRETS AGE
default 1 30m
prometheus 1 21s
现在我们就可以添加 promethues 的资源对象了:
[root@k8s-01 prometheus]# kubectl apply -f prometheus-deploy.yaml
deployment.apps/prometheus created
[root@k8s-01 prometheus]# kubectl get pod -n monitoring
NAME READY STATUS RESTARTS AGE
prometheus-5cdf864d9-z9bg4 0/1 CrashLoopBackOff 2 2m56s
[root@k8s-01 prometheus]# kubectl logs -f prometheus-5cdf864d9-z9bg4 -n monitoring
ts=2023-06-06T15:27:55.111Z caller=main.go:444 level=info msg="Starting Prometheus" version="(version=2.31.1, branch=HEAD, revision=411021ada9ab41095923b8d2df9365b632fd40c3)"
ts=2023-06-06T15:27:55.111Z caller=main.go:449 level=info build_context="(go=go1.17.3, user=root@9419c9c2d4e0, date=20211105-20:35:02)"
ts=2023-06-06T15:27:55.111Z caller=main.go:450 level=info host_details="(Linux 3.10.0-1127.10.1.el7.x86_64 #1 SMP Wed Jun 3 14:28:03 UTC 2020 x86_64 prometheus-5cdf864d9-z9bg4 (none))"
ts=2023-06-06T15:27:55.111Z caller=main.go:451 level=info fd_limits="(soft=1048576, hard=1048576)"
ts=2023-06-06T15:27:55.111Z caller=main.go:452 level=info vm_limits="(soft=unlimited, hard=unlimited)"
ts=2023-06-06T15:27:55.112Z caller=query_logger.go:87 level=error component=activeQueryTracker msg="Error opening query log file" file=/prometheus/queries.active err="open /prometheus/queries.active: permission denied"
panic: Unable to create mmap-ed active query log
goroutine 1 [running]:
github.com/prometheus/prometheus/promql.NewActiveQueryTracker({0x7ffc2fc58e01, 0xb}, 0x14, {0x34442c0, 0xc0001371d0})
/app/promql/query_logger.go:117 +0x3d7
main.main()
/app/cmd/prometheus/main.go:491 +0x6bbf
[root@k8s-01 prometheus]#
文件权限
创建 Pod 后,我们可以看到并没有成功运行,出现了 open /prometheus/queries.active: permission denied
这样的错误信息,这是因为我们的 prometheus
的镜像中是使用的 nobody 这个用户,然后现在我们通过 LocalPV
挂载到宿主机上面的目录的 ownership
却是 root
:
[root@k8s-02 k8s]# ls -alh
总用量 0
drwxr-xr-x 4 root root 39 6月 18 22:14 .
drwxr-xr-x. 5 root root 50 6月 6 23:23 ..
drwxr-xr-x 6 root root 138 6月 19 01:00 prometheus
所以当然会出现操作权限问题了,这个时候我们就可以通过 securityContext
来为 Pod 设置下 volumes 的权限,通过设置 runAsUser=0
指定运行的用户为 root:
containers:
- image: prom/prometheus:v2.31.1
name: prometheus
securityContext:
runAsUser: 0
也可以通过设置一个 initContainer 来修改数据目录权限:
......
initContainers:
- name: fix-permissions
image: busybox
command: [chown, -R, "nobody:nobody", /prometheus]
volumeMounts:
- name: data
mountPath: /prometheus
这个时候我们重新更新下 prometheus:
[root@k8s-01 prometheus]# kubectl apply -f prometheus-deploy.yaml
deployment.apps/prometheus created
[root@k8s-01 prometheus]# kubectl get pod -n monitoring
NAME READY STATUS RESTARTS AGE
prometheus-675dd5dc5b-ks9k4 1/1 Running 0 9s
[root@k8s-01 prometheus]# kubectl logs -f prometheus-675dd5dc5b-ks9k4 -n monitoring
ts=2023-06-06T15:33:41.415Z caller=main.go:444 level=info msg="Starting Prometheus" version="(version=2.31.1, branch=HEAD, revision=411021ada9ab41095923b8d2df9365b632fd40c3)"
ts=2023-06-06T15:33:41.415Z caller=main.go:449 level=info build_context="(go=go1.17.3, user=root@9419c9c2d4e0, date=20211105-20:35:02)"
ts=2023-06-06T15:33:41.418Z caller=web.go:542 level=info component=web msg="Start listening for connections" address=0.0.0.0:9090
ts=2023-06-06T15:33:41.500Z caller=main.go:839 level=info msg="Starting TSDB ..."
ts=2023-06-06T15:33:41.505Z caller=main.go:869 level=info msg="TSDB started"
ts=2023-06-06T15:33:41.505Z caller=main.go:996 level=info msg="Loading configuration file" filename=/etc/prometheus/prometheus.yml
ts=2023-06-06T15:33:41.506Z caller=main.go:1033 level=info msg="Completed loading of configuration file" filename=/etc/prometheus/prometheus.yml totalDuration=592.24µs db_storage=1.487µs remote_storage=6.209µs web_handler=900ns query_engine=1.736µs scrape=288.029µs scrape_sd=26.451µs notify=1.151µs notify_sd=1.487µs rules=3.679µs
ts=2023-06-06T15:33:41.506Z caller=main.go:811 level=info msg="Server is ready to receive web requests."
Pod 创建成功后,为了能够在外部访问到 prometheus 的 Web UI 服务,我们还需要创建一个 Service 对象:
# prometheus-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: monitoring
labels:
app: prometheus
spec:
selector:
app: prometheus
type: NodePort
ports:
- name: web
port: 9090
targetPort: http
为了方便测试,我们这里创建一个 NodePort
类型的服务,当然我们可以创建一个 Ingress
对象,通过域名来进行访问:
[root@k8s-01 prometheus]# kubectl apply -f prometheus-svc.yaml
service/prometheus created
[root@k8s-01 prometheus]# kubectl get svc -n monitoring -owide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
prometheus NodePort 10.96.219.107 <none> 9090:32478/TCP 20s app=prometheus
现在我们就可以通过 http://任意节点IP:32478
访问 prometheus 的 webui 服务了:
这个抓取job是用于抓取 prometheus 本身的一些监控数据了,比如我们这里就选择 process_resident_memory_bytes
这个指标,然后点击 Execute
,就可以看到类似于下面的图表数据了:
Grafana部署
Prometheus 采集了 Kubernetes 集群中的一些监控数据指标,我们也尝试使用 promQL 语句查询出了一些数据,并且在 Prometheus 的 Dashboard 中进行了展示,但是明显可以感觉到 Prometheus 的图表功能相对较弱,所以一般情况下我们还是会使用 Grafana 来进行展示,所以我们可以将 Grafana 安装到集群中来。
1、创建编排文件:
# grafana-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana
namespace: monitoring
spec:
selector:
matchLabels:
app: grafana
template:
metadata:
labels:
app: grafana
spec:
volumes:
- name: storage
persistentVolumeClaim:
claimName: grafana-data
containers:
- name: grafana
image: grafana/grafana:8.3.3
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 0
ports:
- containerPort: 3000
name: grafana
env:
- name: GF_SECURITY_ADMIN_USER
value: admin
- name: GF_SECURITY_ADMIN_PASSWORD
value: admin
readinessProbe:
failureThreshold: 10
httpGet:
path: /api/health
port: 3000
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 30
livenessProbe:
failureThreshold: 3
httpGet:
path: /api/health
port: 3000
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: 400m
memory: 1024Mi
requests:
cpu: 200m
memory: 512Mi
volumeMounts:
- mountPath: /var/lib/grafana
name: storage
---
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: monitoring
spec:
type: NodePort
ports:
- port: 3000
selector:
app: grafana
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: grafana-local
labels:
app: grafana
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
storageClassName: local-storage
local:
path: /data/k8s/grafana #保证节点上创建好该目录
persistentVolumeReclaimPolicy: Retain
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-02
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: grafana-data
namespace: monitoring
spec:
selector:
matchLabels:
app: grafana
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: local-storage
❝两个比较重要的环境变量
❞GF_SECURITY_ADMIN_USER
和GF_SECURITY_ADMIN_PASSWORD
,用来配置 grafana 的管理员用户和密码的,由于 grafana 将 dashboard、插件这些数据保存在/var/lib/grafana
这个目录下面的,所以我们这里如果需要做数据持久化的话,就需要针对这个目录进行 volume 挂载声明。
2、创建并验证Pod启动正常:
[root@k8s-01 prometheus]# kubectl apply -f grafana-deploy.yaml
[root@k8s-01 prometheus]# kubectl get pod -n monitoring
NAME READY STATUS RESTARTS AGE
grafana-7cfd74ccf5-crcnz 1/1 Running 0 3m57s
prometheus-675dd5dc5b-ks9k4 1/1 Running 0 11d
[root@k8s-01 prometheus]# kubectl get svc -n monitoring
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
grafana NodePort 10.96.57.144 <none> 3000:32052/TCP 13m
prometheus NodePort 10.96.219.107 <none> 9090:32478/TCP 11d
3、访问 grafana:http://192.168.31.160:32052
4、添加Prometheus数据源:
容器基础资源指标
说到容器监控我们自然会想到 cAdvisor
,之前分享过如何部署cAdvisor
组件监控Docker
容器,cAdvisor(Container Advisor)
是 Google
开源的一个容器监控工具,可用于对容器资源的使用情况和性能进行监控。它以守护进程方式运行,用于收集、聚合、处理和导出正在运行容器的有关信息。具体来说,该组件对每个容器都会记录其资源隔离参数、历史资源使用情况、完整历史资源使用情况的直方图和网络统计信息。
cAdvisor
是用于监控容器引擎的,由于其监控的实用性,Kubernetes
已经默认将其内置到 kubelet
组件中,所以我们无需再单独部署 cAdvisor
组件,直接使用 kubelet
组件提供的指标采集地址即可。
cAdvisor
的数据路径为 /api/v1/nodes/<node>/proxy/metrics
,但是我们不推荐使用这种方式,因为这种方式是通过 api-server
去代理访问的,对于大规模的集群会对 api-server
造成很大的压力,所以我们可以直接通过访问 kubelet
的 /metrics/cadvisor
这个端点来获取 cAdvisor
的数据。
cAdvisor接入
我们这里使用 node
的服务发现模式,因为每一个节点下面都有 kubelet
,自然都有 cAdvisor
采集到的数据指标,配置如下:
- job_name: "cadvisor"
kubernetes_sd_configs:
- role: node
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
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
replacement: $1
- replacement: /metrics/cadvisor # <nodeip>/metrics -> <nodeip>/metrics/cadvisor
target_label: __metrics_path__
# 下面的方式不推荐使用
# - target_label: __address__
# replacement: kubernetes.default.svc:443
# - target_label: __metrics_path__
# replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
cadvisor监控接入成功如下图:
k8s节点node指标
节点性能关键指标
1、主机基本信息
node_uname_info:主机基本信息,包括架构、主机名、操作系统类型等
2、CPU使用率:
(1 - avg(rate(node_cpu_seconds_total{mode="idle"}[$interval])) by (instance)) * 100
3、CPU负载:
node_load1:1分钟负载
node_load5:5分钟负载
node_load15:15分钟负载
4、内存使用率:
100 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100
5、磁盘指标:
node_filesystem_size_bytes:磁盘总量
node_filesystem_avail_bytes:磁盘可用量
6、磁盘IO:
sum(rate(node_disk_read_bytes_total[1m])) # 磁盘每秒读取量
sum(rate(node_disk_written_bytes_total[1m])) # 磁盘每秒写入量
7、网络IO:
sum(rate(node_network_receive_bytes_total[1m])) # 网卡IO(进)
sum(rate(node_network_transmit_bytes_total[1m])) # 网卡IO(出)
节点监控部署
物理节点性能监控一般是通过node_exporter
来获取,要监控云原生集群节点同样我们这里使用 node_exporter
,由于每个节点都需要获取到监控指标数据,所以我们可以通过 DaemonSet 控制器来部署该服务,这样每一个节点都会自动运行一个 node-exporter
的 Pod,如果我们从集群中删除或者添加节点后,也会进行自动扩展。
1、创建 DaemonSet
控制器的编排文件node-exporter-daemonset.yaml
:
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.3.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: 200Mi
limits:
cpu: 300m
memory: 400Mi
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
以及Network namespace
,这些 namespace 就是用于容器隔离的关键技术。
另外使用hostPath
存储卷技术将主机的 /dev
、/proc
、/sys
这些目录挂载到容器中,这些因为我们采集的很多节点数据都是通过这些文件夹下面的文件来获取到的,比如我们在使用 top
命令可以查看当前 cpu 使用情况,数据就来源于文件 /proc/stat
,使用 free
命令可以查看当前内存使用情况,其数据来源是来自 /proc/meminfo
文件。
2、通过DaemonSet
控制器创建Pod
:
kubectl apply -f node-exporter-daemonset.yaml
3、查看Pod是否运行正常:
[root@k8s-01 prometheus]# kubectl get pod -n kube-system -l app=node-exporter -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node-exporter-5brqt 1/1 Running 0 3m35s 192.168.31.162 k8s-03 <none> <none>
node-exporter-hqzbp 1/1 Running 0 3m35s 192.168.31.161 k8s-02 <none> <none>
node-exporter-tlc7r 1/1 Running 0 3m35s 192.168.31.160 k8s-01 <none> <none>
部署完成后,我们可以看到在 3 个节点上都运行了一个 node-exporter的Pod,且状态Running
,可以通过PodIP:9100
方式访问获取节点指标:
curl http://192.168.31.160:9100/metrics
curl http://192.168.31.161:9100/metrics
curl http://192.168.31.162:9100/metrics
❝由于我们指定了
hostNetwork=true
,所以PodIP
实际上就是节点IP
,指定的container port: 9100
也会在每个节点上就会绑定一个端口 9100:[root@k8s-01 ~]# netstat -antp|grep 9100
❞
tcp 0 0 192.168.31.160:9100 0.0.0.0:* LISTEN 39239/node_exporter
4、Prometheus接入配置:
- job_name: kubernetes-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_(.+)
成功接入后,在prometheus target页面可以看到采集正常:
5、导入8919 dashboard
,Kubernetes
云原生集群节点性能监控指标就展示到模板上,如下图:
[更多云原生监控运维,请关注微信公众号:云原生生态实验室]