github.com/galamsiva2020/kubernetes-heapster-monitoring@v0.0.0-20210823134957-3c1baa7c1e70/metrics/sources/kubelet/kubelet.go (about)

     1  // Copyright 2015 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package kubelet
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"net/url"
    21  	"strings"
    22  	"time"
    23  
    24  	. "k8s.io/heapster/metrics/core"
    25  
    26  	"github.com/golang/glog"
    27  	cadvisor "github.com/google/cadvisor/info/v1"
    28  	"github.com/prometheus/client_golang/prometheus"
    29  	kube_api "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/labels"
    32  	kube_client "k8s.io/client-go/kubernetes"
    33  	v1listers "k8s.io/client-go/listers/core/v1"
    34  	"k8s.io/client-go/tools/cache"
    35  	"k8s.io/heapster/metrics/util"
    36  )
    37  
    38  const (
    39  	infraContainerName = "POD"
    40  	// TODO: following constants are copied from k8s, change to use them directly
    41  	kubernetesPodNameLabel      = "io.kubernetes.pod.name"
    42  	kubernetesPodNamespaceLabel = "io.kubernetes.pod.namespace"
    43  	kubernetesPodUID            = "io.kubernetes.pod.uid"
    44  	kubernetesContainerLabel    = "io.kubernetes.container.name"
    45  )
    46  
    47  var (
    48  	// The Kubelet request latencies in microseconds.
    49  	kubeletRequestLatency = prometheus.NewSummaryVec(
    50  		prometheus.SummaryOpts{
    51  			Namespace: "heapster",
    52  			Subsystem: "kubelet",
    53  			Name:      "request_duration_milliseconds",
    54  			Help:      "The Kubelet request latencies in milliseconds.",
    55  		},
    56  		[]string{"node"},
    57  	)
    58  )
    59  
    60  func init() {
    61  	prometheus.MustRegister(kubeletRequestLatency)
    62  }
    63  
    64  // Kubelet-provided metrics for pod and system container.
    65  type kubeletMetricsSource struct {
    66  	host          Host
    67  	kubeletClient *KubeletClient
    68  	nodename      string
    69  	hostname      string
    70  	hostId        string
    71  	schedulable   string
    72  }
    73  
    74  func NewKubeletMetricsSource(host Host, client *KubeletClient, nodeName string, hostName string, hostId string, schedulable string) MetricsSource {
    75  	return &kubeletMetricsSource{
    76  		host:          host,
    77  		kubeletClient: client,
    78  		nodename:      nodeName,
    79  		hostname:      hostName,
    80  		hostId:        hostId,
    81  		schedulable:   schedulable,
    82  	}
    83  }
    84  
    85  func (this *kubeletMetricsSource) Name() string {
    86  	return this.String()
    87  }
    88  
    89  func (this *kubeletMetricsSource) String() string {
    90  	return fmt.Sprintf("kubelet:%s:%d", this.host.IP, this.host.Port)
    91  }
    92  
    93  func (this *kubeletMetricsSource) handleSystemContainer(c *cadvisor.ContainerInfo, cMetrics *MetricSet) string {
    94  	glog.V(8).Infof("Found system container %v with labels: %+v", c.Name, c.Spec.Labels)
    95  	cName := c.Name
    96  	if strings.HasPrefix(cName, "/") {
    97  		cName = cName[1:]
    98  	}
    99  	cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeSystemContainer
   100  	cMetrics.Labels[LabelContainerName.Key] = cName
   101  	return NodeContainerKey(this.nodename, cName)
   102  }
   103  
   104  func (this *kubeletMetricsSource) handleKubernetesContainer(cName, ns, podName string, c *cadvisor.ContainerInfo, cMetrics *MetricSet) string {
   105  	var metricSetKey string
   106  	if cName == infraContainerName {
   107  		metricSetKey = PodKey(ns, podName)
   108  		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePod
   109  	} else {
   110  		metricSetKey = PodContainerKey(ns, podName, cName)
   111  		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePodContainer
   112  		cMetrics.Labels[LabelContainerName.Key] = cName
   113  		cMetrics.Labels[LabelContainerBaseImage.Key] = c.Spec.Image
   114  	}
   115  	cMetrics.Labels[LabelPodId.Key] = c.Spec.Labels[kubernetesPodUID]
   116  	cMetrics.Labels[LabelPodName.Key] = podName
   117  	cMetrics.Labels[LabelNamespaceName.Key] = ns
   118  	return metricSetKey
   119  }
   120  
   121  func (this *kubeletMetricsSource) decodeMetrics(c *cadvisor.ContainerInfo) (string, *MetricSet) {
   122  	if len(c.Stats) == 0 {
   123  		return "", nil
   124  	}
   125  
   126  	var metricSetKey string
   127  	cMetrics := &MetricSet{
   128  		CollectionStartTime: c.Spec.CreationTime,
   129  		ScrapeTime:          c.Stats[0].Timestamp,
   130  		MetricValues:        map[string]MetricValue{},
   131  		Labels: map[string]string{
   132  			LabelNodename.Key: this.nodename,
   133  			LabelHostname.Key: this.hostname,
   134  			LabelHostID.Key:   this.hostId,
   135  		},
   136  		LabeledMetrics: []LabeledMetric{},
   137  	}
   138  
   139  	if isNode(c) {
   140  		metricSetKey = NodeKey(this.nodename)
   141  		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeNode
   142  		cMetrics.Labels[LabelNodeSchedulable.Key] = this.schedulable
   143  	} else {
   144  		cName := c.Spec.Labels[kubernetesContainerLabel]
   145  		ns := c.Spec.Labels[kubernetesPodNamespaceLabel]
   146  		podName := c.Spec.Labels[kubernetesPodNameLabel]
   147  
   148  		// Support for kubernetes 1.0.*
   149  		if ns == "" && strings.Contains(podName, "/") {
   150  			tokens := strings.SplitN(podName, "/", 2)
   151  			if len(tokens) == 2 {
   152  				ns = tokens[0]
   153  				podName = tokens[1]
   154  			}
   155  		}
   156  		if cName == "" {
   157  			// Better this than nothing. This is a temporary hack for new heapster to work
   158  			// with Kubernetes 1.0.*.
   159  			// TODO: fix this with POD list.
   160  			// Parsing name like:
   161  			// k8s_kube-ui.7f9b83f6_kube-ui-v1-bxj1w_kube-system_9abfb0bd-811f-11e5-b548-42010af00002_e6841e8d
   162  			pos := strings.Index(c.Name, ".")
   163  			if pos >= 0 {
   164  				// remove first 4 chars.
   165  				cName = c.Name[len("k8s_"):pos]
   166  			}
   167  		}
   168  
   169  		// No Kubernetes metadata so treat this as a system container.
   170  		if cName == "" || ns == "" || podName == "" {
   171  			metricSetKey = this.handleSystemContainer(c, cMetrics)
   172  		} else {
   173  			metricSetKey = this.handleKubernetesContainer(cName, ns, podName, c, cMetrics)
   174  		}
   175  	}
   176  
   177  	for _, metric := range StandardMetrics {
   178  		if metric.HasValue != nil && metric.HasValue(&c.Spec) {
   179  			cMetrics.MetricValues[metric.Name] = metric.GetValue(&c.Spec, c.Stats[0])
   180  		}
   181  	}
   182  
   183  	for _, metric := range LabeledMetrics {
   184  		if metric.HasLabeledMetric != nil && metric.HasLabeledMetric(&c.Spec, c.Stats[0]) {
   185  			labeledMetrics := metric.GetLabeledMetric(&c.Spec, c.Stats[0])
   186  			cMetrics.LabeledMetrics = append(cMetrics.LabeledMetrics, labeledMetrics...)
   187  		}
   188  	}
   189  
   190  	if !c.Spec.HasCustomMetrics {
   191  		return metricSetKey, cMetrics
   192  	}
   193  
   194  metricloop:
   195  	for _, spec := range c.Spec.CustomMetrics {
   196  		cmValue, ok := c.Stats[0].CustomMetrics[spec.Name]
   197  		if !ok || cmValue == nil || len(cmValue) == 0 {
   198  			continue metricloop
   199  		}
   200  
   201  		newest := cmValue[0]
   202  		for _, metricVal := range cmValue {
   203  			if newest.Timestamp.Before(metricVal.Timestamp) {
   204  				newest = metricVal
   205  			}
   206  		}
   207  		mv := MetricValue{}
   208  		switch spec.Type {
   209  		case cadvisor.MetricGauge:
   210  			mv.MetricType = MetricGauge
   211  		case cadvisor.MetricCumulative:
   212  			mv.MetricType = MetricCumulative
   213  		default:
   214  			glog.V(4).Infof("Skipping %s: unknown custom metric type: %v", spec.Name, spec.Type)
   215  			continue metricloop
   216  		}
   217  
   218  		switch spec.Format {
   219  		case cadvisor.IntType:
   220  			mv.ValueType = ValueInt64
   221  			mv.IntValue = newest.IntValue
   222  		case cadvisor.FloatType:
   223  			mv.ValueType = ValueFloat
   224  			mv.FloatValue = newest.FloatValue
   225  		default:
   226  			glog.V(4).Infof("Skipping %s: unknown custom metric format %v", spec.Name, spec.Format)
   227  			continue metricloop
   228  		}
   229  
   230  		cMetrics.MetricValues[CustomMetricPrefix+spec.Name] = mv
   231  	}
   232  
   233  	return metricSetKey, cMetrics
   234  }
   235  
   236  func (this *kubeletMetricsSource) ScrapeMetrics(start, end time.Time) (*DataBatch, error) {
   237  	containers, err := this.scrapeKubelet(this.kubeletClient, this.host, start, end)
   238  
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  
   243  	glog.V(2).Infof("successfully obtained stats from %s for %v containers", this.host, len(containers))
   244  
   245  	result := &DataBatch{
   246  		Timestamp:  end,
   247  		MetricSets: map[string]*MetricSet{},
   248  	}
   249  
   250  	for _, c := range containers {
   251  		name, metrics := this.decodeMetrics(&c)
   252  		if name == "" || metrics == nil {
   253  			continue
   254  		}
   255  		result.MetricSets[name] = metrics
   256  	}
   257  
   258  	return result, nil
   259  }
   260  
   261  func (this *kubeletMetricsSource) scrapeKubelet(client *KubeletClient, host Host, start, end time.Time) ([]cadvisor.ContainerInfo, error) {
   262  	startTime := time.Now()
   263  	defer func() {
   264  		kubeletRequestLatency.WithLabelValues(this.hostname).Observe(float64(time.Since(startTime)) / float64(time.Millisecond))
   265  	}()
   266  	return client.GetAllRawContainers(host, start, end)
   267  }
   268  
   269  type kubeletProvider struct {
   270  	nodeLister    v1listers.NodeLister
   271  	reflector     *cache.Reflector
   272  	kubeletClient *KubeletClient
   273  }
   274  
   275  func (this *kubeletProvider) GetMetricsSources() []MetricsSource {
   276  	sources := []MetricsSource{}
   277  	nodes, err := this.nodeLister.List(labels.Everything())
   278  	if err != nil {
   279  		glog.Errorf("error while listing nodes: %v", err)
   280  		return sources
   281  	}
   282  	if len(nodes) == 0 {
   283  		glog.Error("No nodes received from APIserver.")
   284  		return sources
   285  	}
   286  
   287  	for _, node := range nodes {
   288  		hostname, ip, err := GetNodeHostnameAndIP(node)
   289  		if err != nil {
   290  			glog.Errorf("%v", err)
   291  			continue
   292  		}
   293  		sources = append(sources, NewKubeletMetricsSource(
   294  			Host{IP: ip, Port: this.kubeletClient.GetPort()},
   295  			this.kubeletClient,
   296  			node.Name,
   297  			hostname,
   298  			node.Spec.ExternalID,
   299  			getNodeSchedulableStatus(node),
   300  		))
   301  	}
   302  	return sources
   303  }
   304  
   305  func getNodeSchedulableStatus(node *kube_api.Node) string {
   306  	if node.Spec.Unschedulable {
   307  		return "false"
   308  	}
   309  
   310  	return "true"
   311  }
   312  
   313  func GetNodeHostnameAndIP(node *kube_api.Node) (string, net.IP, error) {
   314  	for _, c := range node.Status.Conditions {
   315  		if c.Type == kube_api.NodeReady && c.Status != kube_api.ConditionTrue {
   316  			return "", nil, fmt.Errorf("node %v is not ready", node.Name)
   317  		}
   318  	}
   319  	hostname, ip := node.Name, ""
   320  	for _, addr := range node.Status.Addresses {
   321  		if addr.Type == kube_api.NodeHostName && addr.Address != "" {
   322  			hostname = addr.Address
   323  		}
   324  		if addr.Type == kube_api.NodeInternalIP && addr.Address != "" {
   325  			if net.ParseIP(addr.Address) != nil {
   326  				ip = addr.Address
   327  			}
   328  		}
   329  		if addr.Type == kube_api.NodeExternalIP && addr.Address != "" && ip == "" {
   330  			ip = addr.Address
   331  		}
   332  	}
   333  	if parsedIP := net.ParseIP(ip); parsedIP != nil {
   334  		return hostname, parsedIP, nil
   335  	}
   336  	return "", nil, fmt.Errorf("node %v has no valid hostname and/or IP address: %v %v", node.Name, hostname, ip)
   337  }
   338  
   339  func NewKubeletProvider(uri *url.URL) (MetricsSourceProvider, error) {
   340  	// create clients
   341  	kubeConfig, kubeletConfig, err := GetKubeConfigs(uri)
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  	kubeClient := kube_client.NewForConfigOrDie(kubeConfig)
   346  	kubeletClient, err := NewKubeletClient(kubeletConfig)
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  
   351  	// Get nodes to test if the client is configured well. Watch gives less error information.
   352  	if _, err := kubeClient.CoreV1().Nodes().List(metav1.ListOptions{}); err != nil {
   353  		glog.Errorf("Failed to load nodes: %v", err)
   354  	}
   355  
   356  	// watch nodes
   357  	nodeLister, reflector, _ := util.GetNodeLister(kubeClient)
   358  
   359  	return &kubeletProvider{
   360  		nodeLister:    nodeLister,
   361  		reflector:     reflector,
   362  		kubeletClient: kubeletClient,
   363  	}, nil
   364  }