k8s节点资源用量和metric-server
写在前面
在资源监控层面,我们都知道prometheus是一个最佳的选择,它提供了各种维度的指标监控能力,并且支持自定义指标以及指标聚合,但是部署一个prometheus在有时候却不是一个最佳的选择,特别是在某些情况下,我们只需要获取资源的核心指标(指cpu和memory的使用量),我们有更便捷的选择。
一个非常常用的命令,kubectl top node
,即可获取节点的cpu和memory的使用量以及总量。
# kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
10.56.227.185 3034m 19% 23352Mi 69%
10.56.227.186 2509m 15% 25795Mi 76%
10.56.227.187 3866m 24% 26068Mi 77%
而kubectl top 底层则是调用了metrics-server的API来获取节点的cpu和memory的使用量,那metrics-server是什么呢?
本文基于:metrics-server-0.5.2
分析。
概述
从Kubernetes v1.8 开始,资源使用情况的监控可以通过 metrics API的形式获取,例如节点的CPU和内存使用量。这些度量可以由用户直接访问(例如,通过使用kubectl top命令),或者由集群中的控制器(例如,Horizontal Pod Autoscaler)使用来进行决策,具体的组件为metric-server,用来替换之前的heapster,heapster从1.11开始逐渐被废弃。
Metrics-Server是集群核心监控数据的聚合器。通俗地说,它存储了集群中各节点的监控数据,并且提供了API以供分析和使用。Metrics-Server作为一个 Deployment对象默认部署在Kubernetes集群中。不过准确地说,它是Deployment,Service,ClusterRole,ClusterRoleBinding,APIService,RoleBinding等资源对象的综合体。
需要注意的是:
- metrics-server提供的是实时的指标(实际是最近一次采集的数据,保存在内存中),并没有数据库来存储
- 这些数据指标并非由metrics-server本身采集,而是由每个节点上的cadvisor采集,metrics-server只是发请求给cadvisor并将metric格式的数据转换成aggregate api
- 由于需要通过aggregate api来提供接口,需要集群中的kube-apiserver开启该功能(开启方法可以参考官方社区的文档)
从metrics-server讲起,首先要讲apiservice的概念。
Apiservice
apiservice是kubernetes中的一类资源,和deployment,secret这些资源类似,我们使用kubectl get apiservice
可以查看集群中的apiservice。
# kubectl get apiservices
NAME SERVICE AVAILABLE AGE
v1. Local True 2y31d
v1.admissionregistration.k8s.io Local True 58d
v1.apiextensions.k8s.io Local True 58d
v1.apps Local True 2y31d
v1.authentication.k8s.io Local True 2y31d
v1.authorization.k8s.io Local True 2y31d
v1.autoscaling Local True 2y31d
v1.batch Local True 2y31d
当我们在集群中部署了metric-server之后,也可以使用kubectl get方法查询到对应的apiservice。
# kubectl get apiservice| grep v1beta1.metrics.k8s.io
v1beta1.metrics.k8s.io kube-system/metrics-server True 68d
原理
metrics-server一方面会调用kubelet
暴露的metrics接口获取当前的资源使用情况,另一方面会启动apiserver供kubectl top等客户端调用。
Metrics-server定时从Kubelet的Summary API(类似/ap1/v1/nodes/nodename/stats/summary)采集指标信息,这些聚合过的数据将存储在内存中,且以metric-api的形式暴露出去。
Metrics-server复用了api-server的库来实现自己的功能,比如鉴权、版本等,为了实现将数据存放在内存中,去掉了默认的etcd存储,引入了内存存储(即实现Storage interface)。
因为存放在内存中,因此监控数据是没有持久化的,可以通过第三方存储来拓展。
主要是起了两个client
,一个是kube-apiserver
的client
用来获取集群中node
资源,另一个client则是调用节点上cadvisor
的接口获取节点和pod
的cpu
和memory
监控数据,同时也创建了一个metricSink
用来保存获取的监控数据。
用法
话不多说,直接说一下如何使用。
我们可以先通过命令行调用的方式来体验一下效果:
# kubectl get nodes.v1beta1.metrics.k8s.io
NAME CPU MEMORY WINDOW
10.56.227.185 3209942504n 23144692Ki 21s
10.56.227.186 2312842680n 26126244Ki 32s
10.56.227.187 3987327962n 27829312Ki 31s
返回的三个指标分别是CPU,MEMORY和WINDOW,其中CPU和MEMORY分别表示cpu和内存的使用量,而WINDOW表示的是指标收集的时间间隔。在代码中是如此写的:
// The following fields define time interval from which metrics were
// collected from the interval [Timestamp-Window, Timestamp].
Timestamp metav1.Time `json:"timestamp" protobuf:"bytes,2,opt,name=timestamp"`
Window metav1.Duration `json:"window" protobuf:"bytes,3,opt,name=window"`
此外,关于CPU的单位是n(1m=1000*1000n,1core=1000m),内存的单位是Ki(1Mi=1024Ki,1Gi=1024Mi),也就是说3209942504n=3.21core。
Demo
这段代码demo展示了如何连接kubeClient和metricsClient来查询节点的cpu和内存的总量,可分配量以及使用量。
package main
import (
"context"
"fmt"
"os"
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/clientcmd"
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
metricsclientset "k8s.io/metrics/pkg/client/clientset/versioned"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func main() {
// 1. 读取kubeconfig文件
kubeconfig, err := os.ReadFile("kubeconfig.yaml")
if err != nil {
panic(err)
}
// 2. 根据kubeconfig文件生成kubeconfig对象
apiconfig, err := clientcmd.Load(kubeconfig)
if err != nil {
panic(err)
}
clientConfig := clientcmd.NewDefaultClientConfig(*apiconfig, &clientcmd.ConfigOverrides{})
cfg, err := clientConfig.ClientConfig()
if err != nil {
panic(err)
}
cfg.Timeout = 5 * time.Second
// 3. 注册scheme,我们要查询node和apiservice两类资源
scheme := runtime.NewScheme()
if err = corev1.AddToScheme(scheme); err != nil {
panic(err)
}
if err = apiregistrationv1.AddToScheme(scheme); err != nil {
panic(err)
}
// 4. 生成集群的kubeclient
kubeClient, err := client.New(cfg, client.Options{
Scheme: scheme,
})
if err != nil {
panic(err)
}
// 5. 查询metrics-server的apiservice是否存在,如果不存在,则说明集群没有安装metrics-server
apiservices := &apiregistrationv1.APIService{}
if err = kubeClient.Get(context.TODO(), client.ObjectKey{Name: "v1beta1.metrics.k8s.io"}, apiservices); err != nil {
panic(err)
}
// 6. 生成metrics-server的client
mclient, err := metricsclientset.NewForConfig(cfg)
if err != nil {
panic(err)
}
// 7. 查询节点的资源信息并输出,其中cpu和内存的总量以及可用量从node中获取,使用量需要从metrics-server中获取
nodeList := &corev1.NodeList{}
err = kubeClient.List(context.TODO(), nodeList, &client.ListOptions{})
if err != nil {
panic(err)
}
for _, node := range nodeList.Items {
fmt.Println("----------" + node.Name + "-----------")
metrics, err := mclient.MetricsV1beta1().NodeMetricses().Get(context.TODO(), node.Name, metav1.GetOptions{})
if err != nil {
panic(err)
}
fmt.Printf("CPU Total: %v , CPU Allocatable: %v, CPU Usage:%v\n", node.Status.Capacity.Cpu(), node.Status.Allocatable.Cpu(), metrics.Usage.Cpu())
fmt.Printf("Memory Total: %v ,Memory Allocatable: %v, Memory usage:%v\n", node.Status.Capacity.Memory(), node.Status.Allocatable.Memory(), metrics.Usage.Memory())
fmt.Println("______________________________________")
}
}
运行结果:
----------10.56.227.185-----------
CPU Total: 16 , CPU Allocatable: 15800m, CPU Usage:3356816604n
Memory Total: 35008528Ki ,Memory Allocatable: 34381840Ki, Memory usage:22874236Ki
______________________________________
----------10.56.227.186-----------
CPU Total: 16 , CPU Allocatable: 15800m, CPU Usage:3473613140n
Memory Total: 35008544Ki ,Memory Allocatable: 34381856Ki, Memory usage:26397832Ki
______________________________________
参考
转载自:https://juejin.cn/post/7231853294410104889