当前位置 博文首页 > mark's technic world:kubectl源码分析之top node

    mark's technic world:kubectl源码分析之top node

    作者:[db:作者] 时间:2021-09-14 13:23

    发布一个k8s部署视频:https://edu.csdn.net/course/detail/26967

    课程内容:各种k8s部署方式。包括minikube部署,kubeadm部署,kubeasz部署,rancher部署,k3s部署。包括开发测试环境部署k8s,和生产环境部署k8s。

    腾讯课堂连接地址https://ke.qq.com/course/478827?taid=4373109931462251&tuin=ba64518

    第二个视频发布??https://edu.csdn.net/course/detail/27109

    腾讯课堂连接地址https://ke.qq.com/course/484107?tuin=ba64518

    介绍主要的k8s资源的使用配置和命令。包括configmap,pod,service,replicaset,namespace,deployment,daemonset,ingress,pv,pvc,sc,role,rolebinding,clusterrole,clusterrolebinding,secret,serviceaccount,statefulset,job,cronjob,podDisruptionbudget,podSecurityPolicy,networkPolicy,resourceQuota,limitrange,endpoint,event,conponentstatus,node,apiservice,controllerRevision等。

    第三个视频发布:https://edu.csdn.net/course/detail/27574

    详细介绍helm命令,学习helm chart语法,编写helm chart。深入分析各项目源码,学习编写helm插件

    第四个课程发布:https://edu.csdn.net/course/detail/28488

    本课程将详细介绍k8s所有命令,以及命令的go源码分析,学习知其然,知其所以然

    ?

    加qq群,请联系:


    ————————————————

    //创建top命令
    func NewCmdTop(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
    	cmd := &cobra.Command{//创建cobra命令
    		Use:   "top",
    		Short: i18n.T("Display Resource (CPU/Memory/Storage) usage."),
    		Long:  topLong,
    		Run:   cmdutil.DefaultSubCommandRun(streams.ErrOut),
    	}
    
    	// create subcommands
    	cmd.AddCommand(NewCmdTopNode(f, nil, streams))//添加node子命令
    	cmd.AddCommand(NewCmdTopPod(f, nil, streams))//添加pod子命令
    
    	return cmd
    }
    type TopNodeOptions struct {//top node结构体
    	ResourceName    string
    	Selector        string
    	SortBy          string
    	NoHeaders       bool
    	NodeClient      corev1client.CoreV1Interface
    	HeapsterOptions HeapsterTopOptions
    	Client          *metricsutil.HeapsterMetricsClient
    	Printer         *metricsutil.TopCmdPrinter
    	DiscoveryClient discovery.DiscoveryInterface
    	MetricsClient   metricsclientset.Interface
    
    	genericclioptions.IOStreams
    }
    
    type HeapsterTopOptions struct {//Heapster结构体
    	Namespace string
    	Service   string
    	Scheme    string
    	Port      string
    }
    func (o *HeapsterTopOptions) Bind(flags *pflag.FlagSet) {//设置heapster选项
    	if len(o.Namespace) == 0 {
    		o.Namespace = metricsutil.DefaultHeapsterNamespace
    	}
    	if len(o.Service) == 0 {
    		o.Service = metricsutil.DefaultHeapsterService
    	}
    	if len(o.Scheme) == 0 {
    		o.Scheme = metricsutil.DefaultHeapsterScheme
    	}
    	if len(o.Port) == 0 {
    		o.Port = metricsutil.DefaultHeapsterPort
    	}
    
    	flags.StringVar(&o.Namespace, "heapster-namespace", o.Namespace, "Namespace Heapster service is located in")
    	flags.StringVar(&o.Service, "heapster-service", o.Service, "Name of Heapster service")
    	flags.StringVar(&o.Scheme, "heapster-scheme", o.Scheme, "Scheme (http or https) to connect to Heapster as")
    	flags.StringVar(&o.Port, "heapster-port", o.Port, "Port name in service to use")
    }
    //创建top node命令
    func NewCmdTopNode(f cmdutil.Factory, o *TopNodeOptions, streams genericclioptions.IOStreams) *cobra.Command {
    	if o == nil {
    		o = &TopNodeOptions{//初始化结构体
    			IOStreams: streams,
    		}
    	}
    
    	cmd := &cobra.Command{//创建cobra命令
    		Use:                   "node [NAME | -l label]",
    		DisableFlagsInUseLine: true,
    		Short:                 i18n.T("Display Resource (CPU/Memory/Storage) usage of nodes"),
    		Long:                  topNodeLong,
    		Example:               topNodeExample,
    		Run: func(cmd *cobra.Command, args []string) {
    			cmdutil.CheckErr(o.Complete(f, cmd, args))//准备
    			cmdutil.CheckErr(o.Validate())//校验
    			cmdutil.CheckErr(o.RunTopNode())//运行
    		},
    		Aliases: []string{"nodes", "no"},// 别名
    	}
    	cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")//selector选项
    	cmd.Flags().StringVar(&o.SortBy, "sort-by", o.Selector, "If non-empty, sort nodes list using specified field. The field can be either 'cpu' or 'memory'.")//sort-by选项
    	cmd.Flags().BoolVar(&o.NoHeaders, "no-headers", o.NoHeaders, "If present, print output without headers")//no-headers选项
    
    	o.HeapsterOptions.Bind(cmd.Flags())//heapster选项
    	return cmd
    }
    //准备
    func (o *TopNodeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
    	if len(args) == 1 {
    		o.ResourceName = args[0]//设置resource
    	} else if len(args) > 1 {
    		return cmdutil.UsageErrorf(cmd, "%s", cmd.Use)
    	}
    
    	clientset, err := f.KubernetesClientSet()//获取clientset
    	if err != nil {
    		return err
    	}
    
    	o.DiscoveryClient = clientset.DiscoveryClient//设置discoveryclient
    
    	config, err := f.ToRESTConfig()//获取rest config
    	if err != nil {
    		return err
    	}
    	o.MetricsClient, err = metricsclientset.NewForConfig(config)//设置MetricsClient
    	if err != nil {
    		return err
    	}
    
    	o.NodeClient = clientset.CoreV1()//设置NodeClient 
    	o.Client = metricsutil.NewHeapsterMetricsClient(clientset.CoreV1(), o.HeapsterOptions.Namespace, o.HeapsterOptions.Scheme, o.HeapsterOptions.Service, o.HeapsterOptions.Port)//设置Heapster client
    
    	o.Printer = metricsutil.NewTopCmdPrinter(o.Out)//设置printer
    	return nil
    }
    
    //校验函数
    func (o *TopNodeOptions) Validate() error {
    	if len(o.SortBy) > 0 {//排序
    		if o.SortBy != sortByCPU && o.SortBy != sortByMemory {//只能是cpu或momery
    			return errors.New("--sort-by accepts only cpu or memory")
    		}
    	}
    	if len(o.ResourceName) > 0 && len(o.Selector) > 0 {//资源名称和selector不能同时指定
    		return errors.New("only one of NAME or --selector can be provided")
    	}
    	return nil
    }
    //运行
    func (o TopNodeOptions) RunTopNode() error {
    	var err error
    	selector := labels.Everything()//设置selector
    	if len(o.Selector) > 0 {//如果--selector有值
    		selector, err = labels.Parse(o.Selector)//设置selector
    		if err != nil {
    			return err
    		}
    	}
    
    	apiGroups, err := o.DiscoveryClient.ServerGroups()//获取apigroup
    	if err != nil {
    		return err
    	}
    
    	metricsAPIAvailable := SupportedMetricsAPIVersionAvailable(apiGroups)//判断Metrics是否支持这个apiGroup
    
    	metrics := &metricsapi.NodeMetricsList{}
    	if metricsAPIAvailable {//如果Metrics支持这个apiGroup
    		metrics, err = getNodeMetricsFromMetricsAPI(o.MetricsClient, o.ResourceName, selector)//获取metrics
    		if err != nil {
    			return err
    		}
    	} else {//如果不支持
    		metrics, err = o.Client.GetNodeMetrics(o.ResourceName, selector.String())//通过client获取metrics
    		if err != nil {
    			return err
    		}
    	}
    
    	if len(metrics.Items) == 0 {//如果没有metrics
    		return errors.New("metrics not available yet")
    	}
    
    	var nodes []v1.Node
    	if len(o.ResourceName) > 0 {//如果资源有值
    		node, err := o.NodeClient.Nodes().Get(o.ResourceName, metav1.GetOptions{})//获取资源名称对应节点
    		if err != nil {
    			return err
    		}
    		nodes = append(nodes, *node)//append节点
    	} else {//如果selector有值
    		nodeList, err := o.NodeClient.Nodes().List(metav1.ListOptions{//获取selector对应节点
    			LabelSelector: selector.String(),
    		})
    		if err != nil {
    			return err
    		}
    		nodes = append(nodes, nodeList.Items...)//append节点
    	}
    
    	allocatable := make(map[string]v1.ResourceList)//可分配资源map
    
    	for _, n := range nodes {//遍历节点,设置可分配资源
    		allocatable[n.Name] = n.Status.Allocatable
    	}
    
    	return o.Printer.PrintNodeMetrics(metrics.Items, allocatable, o.NoHeaders, o.SortBy)//打印结果
    }
    
    func getNodeMetricsFromMetricsAPI(metricsClient metricsclientset.Interface, resourceName string, selector labels.Selector) (*metricsapi.NodeMetricsList, error) {//获取metrics
    	var err error
    	versionedMetrics := &metricsV1beta1api.NodeMetricsList{}//构造metricsList对象
    	mc := metricsClient.MetricsV1beta1()//获取client
    	nm := mc.NodeMetricses()//获取node metrics client
    	if resourceName != "" {//如果资源名称不为空
    		m, err := nm.Get(resourceName, metav1.GetOptions{})//获取metrics
    		if err != nil {
    			return nil, err
    		}
    		versionedMetrics.Items = []metricsV1beta1api.NodeMetrics{*m}//包装metricsList
    	} else {//如果selector有值
    		versionedMetrics, err = nm.List(metav1.ListOptions{LabelSelector: selector.String()})//通过selector获取metrics
    		if err != nil {
    			return nil, err
    		}
    	}
    	metrics := &metricsapi.NodeMetricsList{}
    	err = metricsV1beta1api.Convert_v1beta1_NodeMetricsList_To_metrics_NodeMetricsList(versionedMetrics, metrics, nil)//把metrics转成NodeMetricsList
    	if err != nil {
    		return nil, err
    	}
    	return metrics, nil//返回NodeMetricsList
    }
    //打印node metrics
    func (printer *TopCmdPrinter) PrintNodeMetrics(metrics []metricsapi.NodeMetrics, availableResources map[string]v1.ResourceList, noHeaders bool, sortBy string) error {
    	if len(metrics) == 0 {//metrics为空返回
    		return nil
    	}
    	w := printers.GetNewTabWriter(printer.out)//包装tab Writer
    	defer w.Flush()//defer刷新
    
    	n, err := NewNodeMetricsSorter(metrics, sortBy)//metrics排序
    	if err != nil {
    		return err
    	}
    	sort.Sort(n)//执行排序
    
    	if !noHeaders {//如果不是noHeaders,打印headers
    		printColumnNames(w, NodeColumns)
    	}
    	var usage v1.ResourceList
    	for _, m := range metrics {//遍历metrics
    		err := scheme.Scheme.Convert(&m.Usage, &usage, nil)//转换usage
    		if err != nil {
    			return err
    		}
    		printMetricsLine(w, &ResourceMetricsInfo{//打印一行metrics info
    			Name:      m.Name,
    			Metrics:   usage,
    			Available: availableResources[m.Name],
    		})
    		delete(availableResources, m.Name)//删除availableResources 中已经打印的
    	}
    
    	// print lines for nodes of which the metrics is unreachable.
    	for nodeName := range availableResources {//遍历availableResources,打印没有metrics的node
    		printMissingMetricsNodeLine(w, nodeName)
    	}
    	return nil
    }
    //判断metrics是否支持apiGroup
    func SupportedMetricsAPIVersionAvailable(discoveredAPIGroups *metav1.APIGroupList) bool {
    	for _, discoveredAPIGroup := range discoveredAPIGroups.Groups {
    		if discoveredAPIGroup.Name != metricsapi.GroupName {//如果groupname不同则跳过
    			continue
    		}
    		for _, version := range discoveredAPIGroup.Versions {
    			for _, supportedVersion := range supportedMetricsAPIVersions {
    				if version.Version == supportedVersion {//如果version为supportedVersion 则返回true
    					return true
    				}
    			}
    		}
    	}
    	return false//返回false
    }

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    cs