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 }