【云原生•监控基于Prometheus的云原生集群监控(理论+实践)01

2023年 8月 13日 35.5k 0

【云原生•监控】基于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中间件、业务应用服务等,这些组件运行在云原生上层本身也存在相关指标监控。
  • 环境准备

    为了监控上述云原生指标,首先,我们需要准备PrometheusGrafana环境。

    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、现在我们来创建 prometheusPod 资源:

    # 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           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_USERGF_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            3000:32052/TCP   13m
    prometheus   NodePort   10.96.219.107           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//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 # /metrics -> /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: truehostIPC: truehostNetwork: true 3 个策略,用来使用主机的 PID namespaceIPC 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              
    node-exporter-hqzbp   1/1     Running   0          3m35s   192.168.31.161   k8s-02              
    node-exporter-tlc7r   1/1     Running   0          3m35s   192.168.31.160   k8s-01              
    

    部署完成后,我们可以看到在 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 dashboardKubernetes云原生集群节点性能监控指标就展示到模板上,如下图:

    相关文章

    KubeSphere 部署向量数据库 Milvus 实战指南
    探索 Kubernetes 持久化存储之 Longhorn 初窥门径
    征服 Docker 镜像访问限制!KubeSphere v3.4.1 成功部署全攻略
    那些年在 Terraform 上吃到的糖和踩过的坑
    无需 Kubernetes 测试 Kubernetes 网络实现
    Kubernetes v1.31 中的移除和主要变更

    发布评论