mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 02:08:13 +00:00 
			
		
		
		
	Add a tool for grabbing and parsing prometheus metrics
This commit is contained in:
		
							
								
								
									
										77
									
								
								pkg/metrics/api_server_metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								pkg/metrics/api_server_metrics.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| /* | ||||
| Copyright 2015 The Kubernetes Authors All rights reserved. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package metrics | ||||
|  | ||||
| import ( | ||||
| 	"k8s.io/kubernetes/pkg/util/sets" | ||||
|  | ||||
| 	"github.com/prometheus/common/model" | ||||
| ) | ||||
|  | ||||
| var KnownApiServerMetrics = map[string][]string{ | ||||
| 	"apiserver_request_count":                        {"verb", "resource", "client", "code"}, | ||||
| 	"apiserver_request_latencies_bucket":             {"verb", "resource", "le"}, | ||||
| 	"apiserver_request_latencies_count":              {"verb", "resource"}, | ||||
| 	"apiserver_request_latencies_sum":                {"verb", "resource"}, | ||||
| 	"apiserver_request_latencies_summary":            {"verb", "resource", "quantile"}, | ||||
| 	"apiserver_request_latencies_summary_count":      {"verb", "resource"}, | ||||
| 	"apiserver_request_latencies_summary_sum":        {"verb", "resource"}, | ||||
| 	"etcd_helper_cache_entry_count":                  {}, | ||||
| 	"etcd_helper_cache_hit_count":                    {}, | ||||
| 	"etcd_helper_cache_miss_count":                   {}, | ||||
| 	"etcd_request_cache_add_latencies_summary":       {"quantile"}, | ||||
| 	"etcd_request_cache_add_latencies_summary_count": {}, | ||||
| 	"etcd_request_cache_add_latencies_summary_sum":   {}, | ||||
| 	"etcd_request_cache_get_latencies_summary":       {"quantile"}, | ||||
| 	"etcd_request_cache_get_latencies_summary_count": {}, | ||||
| 	"etcd_request_cache_get_latencies_summary_sum":   {}, | ||||
| 	"etcd_request_latencies_summary":                 {"operation", "type", "quantile"}, | ||||
| 	"etcd_request_latencies_summary_count":           {"operation", "type"}, | ||||
| 	"etcd_request_latencies_summary_sum":             {"operation", "type"}, | ||||
| 	"get_token_count":                                {}, | ||||
| 	"get_token_fail_count":                           {}, | ||||
| 	"rest_client_request_latency_microseconds":       {"url", "verb", "quantile"}, | ||||
| 	"rest_client_request_latency_microseconds_count": {"url", "verb"}, | ||||
| 	"rest_client_request_latency_microseconds_sum":   {"url", "verb"}, | ||||
| 	"rest_client_request_status_codes":               {"code", "host", "method"}, | ||||
| } | ||||
|  | ||||
| type ApiServerMetrics Metrics | ||||
|  | ||||
| func NewApiServerMetrics() ApiServerMetrics { | ||||
| 	result := NewMetrics() | ||||
| 	for metric := range KnownApiServerMetrics { | ||||
| 		result[metric] = make(model.Samples, 0) | ||||
| 	} | ||||
| 	return ApiServerMetrics(result) | ||||
| } | ||||
|  | ||||
| func parseApiServerMetrics(data string, unknownMetrics sets.String) (ApiServerMetrics, error) { | ||||
| 	result := NewApiServerMetrics() | ||||
| 	if err := parseMetrics(data, KnownApiServerMetrics, (*Metrics)(&result), unknownMetrics); err != nil { | ||||
| 		return ApiServerMetrics{}, err | ||||
| 	} | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
| func (g *MetricsGrabber) getMetricsFromApiServer() (string, error) { | ||||
| 	rawOutput, err := g.client.Get().RequestURI("/metrics").Do().Raw() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return string(rawOutput), nil | ||||
| } | ||||
							
								
								
									
										59
									
								
								pkg/metrics/controller_manager_metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								pkg/metrics/controller_manager_metrics.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| /* | ||||
| Copyright 2015 The Kubernetes Authors All rights reserved. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package metrics | ||||
|  | ||||
| import ( | ||||
| 	"k8s.io/kubernetes/pkg/util/sets" | ||||
|  | ||||
| 	"github.com/prometheus/common/model" | ||||
| ) | ||||
|  | ||||
| var KnownControllerManagerMetrics = map[string][]string{ | ||||
| 	"etcd_helper_cache_entry_count":                  {}, | ||||
| 	"etcd_helper_cache_hit_count":                    {}, | ||||
| 	"etcd_helper_cache_miss_count":                   {}, | ||||
| 	"etcd_request_cache_add_latencies_summary":       {"quantile"}, | ||||
| 	"etcd_request_cache_add_latencies_summary_count": {}, | ||||
| 	"etcd_request_cache_add_latencies_summary_sum":   {}, | ||||
| 	"etcd_request_cache_get_latencies_summary":       {"quantile"}, | ||||
| 	"etcd_request_cache_get_latencies_summary_count": {}, | ||||
| 	"etcd_request_cache_get_latencies_summary_sum":   {}, | ||||
| 	"get_token_count":                                {}, | ||||
| 	"get_token_fail_count":                           {}, | ||||
| 	"rest_client_request_latency_microseconds":       {"url", "verb", "quantile"}, | ||||
| 	"rest_client_request_latency_microseconds_count": {"url", "verb"}, | ||||
| 	"rest_client_request_latency_microseconds_sum":   {"url", "verb"}, | ||||
| 	"rest_client_request_status_codes":               {"method", "code", "host"}, | ||||
| } | ||||
|  | ||||
| type ControllerManagerMetrics Metrics | ||||
|  | ||||
| func NewControllerManagerMetrics() ControllerManagerMetrics { | ||||
| 	result := NewMetrics() | ||||
| 	for metric := range KnownControllerManagerMetrics { | ||||
| 		result[metric] = make(model.Samples, 0) | ||||
| 	} | ||||
| 	return ControllerManagerMetrics(result) | ||||
| } | ||||
|  | ||||
| func parseControllerManagerMetrics(data string, unknownMetrics sets.String) (ControllerManagerMetrics, error) { | ||||
| 	result := NewControllerManagerMetrics() | ||||
| 	if err := parseMetrics(data, KnownControllerManagerMetrics, (*Metrics)(&result), unknownMetrics); err != nil { | ||||
| 		return ControllerManagerMetrics{}, err | ||||
| 	} | ||||
| 	return result, nil | ||||
| } | ||||
							
								
								
									
										119
									
								
								pkg/metrics/generic_metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								pkg/metrics/generic_metrics.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| /* | ||||
| Copyright 2015 The Kubernetes Authors All rights reserved. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package metrics | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strings" | ||||
|  | ||||
| 	"k8s.io/kubernetes/pkg/util/sets" | ||||
|  | ||||
| 	"github.com/golang/glog" | ||||
| 	"github.com/prometheus/common/expfmt" | ||||
| 	"github.com/prometheus/common/model" | ||||
| ) | ||||
|  | ||||
| var CommonMetrics = map[string][]string{ | ||||
| 	"process_start_time_seconds":    {}, | ||||
| 	"process_resident_memory_bytes": {}, | ||||
| 	"process_virtual_memory_bytes":  {}, | ||||
| 	"process_cpu_seconds_total":     {}, | ||||
| 	"process_max_fds":               {}, | ||||
| 	"process_open_fds":              {}, | ||||
|  | ||||
| 	"http_request_size_bytes":                  {"handler", "quantile"}, | ||||
| 	"http_request_size_bytes_count":            {"handler"}, | ||||
| 	"http_request_size_bytes_sum":              {"handler"}, | ||||
| 	"http_request_duration_microseconds":       {"handler", "quantile"}, | ||||
| 	"http_request_duration_microseconds_count": {"handler"}, | ||||
| 	"http_request_duration_microseconds_sum":   {"handler"}, | ||||
| 	"http_requests_total":                      {"handler", "method", "code"}, | ||||
|  | ||||
| 	"http_response_size_bytes":       {"handler", "quantile"}, | ||||
| 	"http_response_size_bytes_count": {"handler"}, | ||||
| 	"http_response_size_bytes_sum":   {"handler"}, | ||||
|  | ||||
| 	"ssh_tunnel_open_fail_count": {}, | ||||
| 	"ssh_tunnel_open_count":      {}, | ||||
|  | ||||
| 	"go_gc_duration_seconds":       {"quantile"}, | ||||
| 	"go_gc_duration_seconds_count": {}, | ||||
| 	"go_gc_duration_seconds_sum":   {}, | ||||
| 	"go_goroutines":                {}, | ||||
|  | ||||
| 	"kubernetes_build_info": {"major", "minor", "gitCommit", "gitTreeState", "gitVersion"}, | ||||
| } | ||||
|  | ||||
| type Metrics map[string]model.Samples | ||||
|  | ||||
| func NewMetrics() Metrics { | ||||
| 	result := make(Metrics) | ||||
| 	for metric := range CommonMetrics { | ||||
| 		result[metric] = make(model.Samples, 0) | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| func parseMetrics(data string, knownMetrics map[string][]string, output *Metrics, unknownMetrics sets.String) error { | ||||
| 	dec, err := expfmt.NewDecoder(strings.NewReader(data), expfmt.FmtText) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	decoder := expfmt.SampleDecoder{ | ||||
| 		Dec:  dec, | ||||
| 		Opts: &expfmt.DecodeOptions{}, | ||||
| 	} | ||||
|  | ||||
| 	for { | ||||
| 		var v model.Vector | ||||
| 		if err = decoder.Decode(&v); err != nil { | ||||
| 			if err == io.EOF { | ||||
| 				// Expected loop termination condition. | ||||
| 				return nil | ||||
| 			} | ||||
| 			glog.Warningf("Invalid Decode. Skipping.") | ||||
| 			continue | ||||
| 		} | ||||
| 		for _, metric := range v { | ||||
| 			name := string(metric.Metric[model.MetricNameLabel]) | ||||
| 			_, isCommonMetric := CommonMetrics[name] | ||||
| 			_, isKnownMetric := knownMetrics[name] | ||||
| 			if isKnownMetric || isCommonMetric { | ||||
| 				(*output)[name] = append((*output)[name], metric) | ||||
| 			} else { | ||||
| 				glog.Warning("Unknown metric %v", metric) | ||||
| 				unknownMetrics.Insert(name) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (g *MetricsGrabber) getMetricsFromPod(podName string, namespace string, port int) (string, error) { | ||||
| 	rawOutput, err := g.client.Get(). | ||||
| 		Prefix("proxy"). | ||||
| 		Namespace(namespace). | ||||
| 		Resource("pods"). | ||||
| 		Name(fmt.Sprintf("%v:%v", podName, port)). | ||||
| 		Suffix("metrics"). | ||||
| 		Do().Raw() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return string(rawOutput), nil | ||||
| } | ||||
							
								
								
									
										130
									
								
								pkg/metrics/kubelet_metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								pkg/metrics/kubelet_metrics.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| /* | ||||
| Copyright 2015 The Kubernetes Authors All rights reserved. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package metrics | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"k8s.io/kubernetes/pkg/util/sets" | ||||
|  | ||||
| 	"github.com/prometheus/common/model" | ||||
| ) | ||||
|  | ||||
| var KnownKubeletMetrics = map[string][]string{ | ||||
| 	"cadvisor_version_info":                                  {"cadvisorRevision", "cadvisorVersion", "dockerVersion", "kernelVersion", "osVersion"}, | ||||
| 	"container_cpu_system_seconds_total":                     {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_cpu_usage_seconds_total":                      {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name", "cpu"}, | ||||
| 	"container_cpu_user_seconds_total":                       {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_io_current":                                {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_io_time_seconds_total":                     {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_io_time_weighted_seconds_total":            {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_limit_bytes":                               {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_read_seconds_total":                        {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_reads_merged_total":                        {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_reads_total":                               {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_sector_reads_total":                        {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_sector_writes_total":                       {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_usage_bytes":                               {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_write_seconds_total":                       {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_writes_merged_total":                       {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_fs_writes_total":                              {"device", "id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_last_seen":                                    {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_memory_failcnt":                               {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_memory_failures_total":                        {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name", "scope", "type"}, | ||||
| 	"container_memory_usage_bytes":                           {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_memory_working_set_bytes":                     {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_network_receive_bytes_total":                  {"id", "interface", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_network_receive_errors_total":                 {"id", "image", "interface", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_network_receive_packets_dropped_total":        {"id", "image", "interface", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_network_receive_packets_total":                {"id", "image", "interface", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_network_transmit_bytes_total":                 {"id", "interface", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_network_transmit_errors_total":                {"id", "interface", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_network_transmit_packets_dropped_total":       {"id", "interface", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_network_transmit_packets_total":               {"id", "interface", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_scrape_error":                                 {}, | ||||
| 	"container_spec_cpu_shares":                              {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_spec_memory_limit_bytes":                      {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_spec_memory_swap_limit_bytes":                 {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_start_time_seconds":                           {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name"}, | ||||
| 	"container_tasks_state":                                  {"id", "image", "kubernetes_container_name", "kubernetes_namespace", "kubernetes_pod_name", "name", "state"}, | ||||
| 	"get_token_count":                                        {}, | ||||
| 	"get_token_fail_count":                                   {}, | ||||
| 	"kubelet_container_manager_latency_microseconds":         {"operation_type", "quantile"}, | ||||
| 	"kubelet_container_manager_latency_microseconds_count":   {"operation_type"}, | ||||
| 	"kubelet_container_manager_latency_microseconds_sum":     {"operation_type"}, | ||||
| 	"kubelet_containers_per_pod_count":                       {"quantile"}, | ||||
| 	"kubelet_containers_per_pod_count_count":                 {}, | ||||
| 	"kubelet_containers_per_pod_count_sum":                   {}, | ||||
| 	"kubelet_docker_errors":                                  {"operation_type"}, | ||||
| 	"kubelet_docker_operations_latency_microseconds":         {"operation_type", "quantile"}, | ||||
| 	"kubelet_docker_operations_latency_microseconds_count":   {"operation_type"}, | ||||
| 	"kubelet_docker_operations_latency_microseconds_sum":     {"operation_type"}, | ||||
| 	"kubelet_generate_pod_status_latency_microseconds":       {"quantile"}, | ||||
| 	"kubelet_generate_pod_status_latency_microseconds_count": {}, | ||||
| 	"kubelet_generate_pod_status_latency_microseconds_sum":   {}, | ||||
| 	"kubelet_pod_start_latency_microseconds":                 {"quantile"}, | ||||
| 	"kubelet_pod_start_latency_microseconds_count":           {}, | ||||
| 	"kubelet_pod_start_latency_microseconds_sum":             {}, | ||||
| 	"kubelet_pod_worker_latency_microseconds":                {"operation_type", "quantile"}, | ||||
| 	"kubelet_pod_worker_latency_microseconds_count":          {"operation_type"}, | ||||
| 	"kubelet_pod_worker_latency_microseconds_sum":            {"operation_type"}, | ||||
| 	"kubelet_pod_worker_start_latency_microseconds":          {"quantile"}, | ||||
| 	"kubelet_pod_worker_start_latency_microseconds_count":    {}, | ||||
| 	"kubelet_pod_worker_start_latency_microseconds_sum":      {}, | ||||
| 	"kubelet_running_container_count":                        {}, | ||||
| 	"kubelet_running_pod_count":                              {}, | ||||
| 	"kubelet_sync_pods_latency_microseconds":                 {"quantile"}, | ||||
| 	"kubelet_sync_pods_latency_microseconds_count":           {}, | ||||
| 	"kubelet_sync_pods_latency_microseconds_sum":             {}, | ||||
| 	"machine_cpu_cores":                                      {}, | ||||
| 	"machine_memory_bytes":                                   {}, | ||||
| 	"rest_client_request_latency_microseconds":               {"quantile", "url", "verb"}, | ||||
| 	"rest_client_request_latency_microseconds_count":         {"url", "verb"}, | ||||
| 	"rest_client_request_latency_microseconds_sum":           {"url", "verb"}, | ||||
| 	"rest_client_request_status_codes":                       {"code", "host", "method"}, | ||||
| } | ||||
|  | ||||
| type KubeletMetrics Metrics | ||||
|  | ||||
| func NewKubeletMetrics() KubeletMetrics { | ||||
| 	result := NewMetrics() | ||||
| 	for metric := range KnownKubeletMetrics { | ||||
| 		result[metric] = make(model.Samples, 0) | ||||
| 	} | ||||
| 	return KubeletMetrics(result) | ||||
| } | ||||
|  | ||||
| func parseKubeletMetrics(data string, unknownMetrics sets.String) (KubeletMetrics, error) { | ||||
| 	result := NewKubeletMetrics() | ||||
| 	if err := parseMetrics(data, KnownKubeletMetrics, (*Metrics)(&result), unknownMetrics); err != nil { | ||||
| 		return KubeletMetrics{}, err | ||||
| 	} | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
| func (g *MetricsGrabber) getMetricsFromNode(nodeName string) (string, error) { | ||||
| 	rawOutput, err := g.client.Get(). | ||||
| 		Prefix("proxy"). | ||||
| 		Resource("nodes"). | ||||
| 		Name(fmt.Sprintf("%v:%v", nodeName, g.kubeletPort)). | ||||
| 		Suffix("metrics"). | ||||
| 		Do().Raw() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return string(rawOutput), nil | ||||
| } | ||||
							
								
								
									
										136
									
								
								pkg/metrics/metrics_grabber.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								pkg/metrics/metrics_grabber.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| /* | ||||
| Copyright 2015 The Kubernetes Authors All rights reserved. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package metrics | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"k8s.io/kubernetes/pkg/api" | ||||
| 	client "k8s.io/kubernetes/pkg/client/unversioned" | ||||
| 	"k8s.io/kubernetes/pkg/master/ports" | ||||
| 	"k8s.io/kubernetes/pkg/util/sets" | ||||
|  | ||||
| 	"github.com/golang/glog" | ||||
| ) | ||||
|  | ||||
| type MetricsCollection struct { | ||||
| 	ApiServerMetrics         ApiServerMetrics | ||||
| 	ControllerManagerMetrics ControllerManagerMetrics | ||||
| 	KubeletMetrics           map[string]KubeletMetrics | ||||
| 	SchedulerMetrics         SchedulerMetrics | ||||
| } | ||||
|  | ||||
| type MetricsGrabber struct { | ||||
| 	client                    *client.Client | ||||
| 	grabFromApiServer         bool | ||||
| 	grabFromControllerManager bool | ||||
| 	grabFromKubelets          bool | ||||
| 	grabFromScheduler         bool | ||||
| 	kubeletPort               int | ||||
| 	masterName                string | ||||
| 	registeredMaster          bool | ||||
| } | ||||
|  | ||||
| // TODO: find a better way of figuring out if given node is a registered master. | ||||
| func isMasterNode(node *api.Node) bool { | ||||
| 	return strings.HasSuffix(node.Name, "master") | ||||
| } | ||||
|  | ||||
| func NewMetricsGrabber(c *client.Client, kubelets bool, scheduler bool, controllers bool, apiServer bool) (*MetricsGrabber, error) { | ||||
| 	kubeletPort := 0 | ||||
| 	registeredMaster := false | ||||
| 	masterName := "" | ||||
| 	nodeList, err := c.Nodes().List(api.ListOptions{}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(nodeList.Items) < 1 { | ||||
| 		glog.Warning("Can't find any Nodes in the API server to grab metrics from") | ||||
| 	} | ||||
| 	for _, node := range nodeList.Items { | ||||
| 		if isMasterNode(&node) { | ||||
| 			registeredMaster = true | ||||
| 			masterName = node.Name | ||||
| 		} else { | ||||
| 			// We assume that all Kubelets run on the same port, except possibly Master's Kubelet. | ||||
| 			// This will need to get change if assumption is prover wrong. | ||||
| 			kubeletPort = node.Status.DaemonEndpoints.KubeletEndpoint.Port | ||||
| 		} | ||||
| 	} | ||||
| 	if !registeredMaster { | ||||
| 		scheduler = false | ||||
| 		controllers = false | ||||
| 		glog.Warningf("Master node is not registered. Grabbing metrics from Scheduler and ControllerManager is disabled.") | ||||
| 	} | ||||
|  | ||||
| 	return &MetricsGrabber{ | ||||
| 		client:                    c, | ||||
| 		grabFromApiServer:         apiServer, | ||||
| 		grabFromControllerManager: controllers, | ||||
| 		grabFromKubelets:          kubelets, | ||||
| 		grabFromScheduler:         scheduler, | ||||
| 		kubeletPort:               kubeletPort, | ||||
| 		masterName:                masterName, | ||||
| 		registeredMaster:          registeredMaster, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (g *MetricsGrabber) GrabFromKubelet(nodeName string, unknownMetrics sets.String) (KubeletMetrics, error) { | ||||
| 	if g.kubeletPort == 0 { | ||||
| 		return KubeletMetrics{}, fmt.Errorf("MetricsGrabber wasn't able to find Kubelet port during startup. Skipping Kubelet's metrics gathering.") | ||||
| 	} | ||||
| 	output, err := g.getMetricsFromNode(nodeName) | ||||
| 	if err != nil { | ||||
| 		return KubeletMetrics{}, err | ||||
| 	} | ||||
| 	return parseKubeletMetrics(output, unknownMetrics) | ||||
| } | ||||
|  | ||||
| func (g *MetricsGrabber) GrabFromScheduler(unknownMetrics sets.String) (SchedulerMetrics, error) { | ||||
| 	if !g.registeredMaster { | ||||
| 		return SchedulerMetrics{}, fmt.Errorf("Master's Kubelet is not registered. Skipping Scheduler's metrics gathering.") | ||||
| 	} | ||||
| 	output, err := g.getMetricsFromPod(fmt.Sprintf("%v-%v", "kube-scheduler", g.masterName), api.NamespaceSystem, ports.SchedulerPort) | ||||
| 	if err != nil { | ||||
| 		return SchedulerMetrics{}, err | ||||
| 	} | ||||
| 	return parseSchedulerMetrics(output, unknownMetrics) | ||||
| } | ||||
|  | ||||
| func (g *MetricsGrabber) GrabFromControllerManager(unknownMetrics sets.String) (ControllerManagerMetrics, error) { | ||||
| 	if !g.registeredMaster { | ||||
| 		return ControllerManagerMetrics{}, fmt.Errorf("Master's Kubelet is not registered. Skipping ControllerManager's metrics gathering.") | ||||
| 	} | ||||
| 	output, err := g.getMetricsFromPod(fmt.Sprintf("%v-%v", "kube-controller-manager", g.masterName), api.NamespaceSystem, ports.ControllerManagerPort) | ||||
| 	if err != nil { | ||||
| 		return ControllerManagerMetrics{}, err | ||||
| 	} | ||||
| 	return parseControllerManagerMetrics(output, unknownMetrics) | ||||
| } | ||||
|  | ||||
| func (g *MetricsGrabber) GrabFromApiServer(unknownMetrics sets.String) (ApiServerMetrics, error) { | ||||
| 	output, err := g.getMetricsFromApiServer() | ||||
| 	if err != nil { | ||||
| 		return ApiServerMetrics{}, nil | ||||
| 	} | ||||
| 	return parseApiServerMetrics(output, unknownMetrics) | ||||
| } | ||||
|  | ||||
| func (g *MetricsGrabber) Grab(unknownMetrics sets.String) (MetricsCollection, error) { | ||||
| 	return MetricsCollection{}, nil | ||||
| } | ||||
							
								
								
									
										58
									
								
								pkg/metrics/scheduler_metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								pkg/metrics/scheduler_metrics.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| /* | ||||
| Copyright 2015 The Kubernetes Authors All rights reserved. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package metrics | ||||
|  | ||||
| import ( | ||||
| 	"k8s.io/kubernetes/pkg/util/sets" | ||||
|  | ||||
| 	"github.com/prometheus/common/model" | ||||
| ) | ||||
|  | ||||
| var KnownSchedulerMetrics = map[string][]string{ | ||||
| 	"rest_client_request_latency_microseconds":                  {"url", "verb", "quantile"}, | ||||
| 	"rest_client_request_latency_microseconds_count":            {"url", "verb"}, | ||||
| 	"rest_client_request_latency_microseconds_sum":              {"url", "verb"}, | ||||
| 	"rest_client_request_status_codes":                          {"code", "host", "method"}, | ||||
| 	"scheduler_binding_latency_microseconds":                    {"quantile"}, | ||||
| 	"scheduler_binding_latency_microseconds_count":              {}, | ||||
| 	"scheduler_binding_latency_microseconds_sum":                {}, | ||||
| 	"scheduler_binding_ratelimiter_saturation":                  {}, | ||||
| 	"scheduler_e2e_scheduling_latency_microseconds":             {"quantile"}, | ||||
| 	"scheduler_e2e_scheduling_latency_microseconds_count":       {}, | ||||
| 	"scheduler_e2e_scheduling_latency_microseconds_sum":         {}, | ||||
| 	"scheduler_scheduling_algorithm_latency_microseconds":       {"quantile"}, | ||||
| 	"scheduler_scheduling_algorithm_latency_microseconds_count": {}, | ||||
| 	"scheduler_scheduling_algorithm_latency_microseconds_sum":   {}, | ||||
| } | ||||
|  | ||||
| type SchedulerMetrics Metrics | ||||
|  | ||||
| func NewSchedulerMetrics() SchedulerMetrics { | ||||
| 	result := NewMetrics() | ||||
| 	for metric := range KnownSchedulerMetrics { | ||||
| 		result[metric] = make(model.Samples, 0) | ||||
| 	} | ||||
| 	return SchedulerMetrics(result) | ||||
| } | ||||
|  | ||||
| func parseSchedulerMetrics(data string, unknownMetrics sets.String) (SchedulerMetrics, error) { | ||||
| 	result := NewSchedulerMetrics() | ||||
| 	if err := parseMetrics(data, KnownSchedulerMetrics, (*Metrics)(&result), unknownMetrics); err != nil { | ||||
| 		return SchedulerMetrics{}, err | ||||
| 	} | ||||
| 	return result, nil | ||||
| } | ||||
							
								
								
									
										153
									
								
								test/e2e/metrics_grabber_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								test/e2e/metrics_grabber_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| /* | ||||
| Copyright 2015 The Kubernetes Authors All rights reserved. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package e2e | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"k8s.io/kubernetes/pkg/api" | ||||
| 	client "k8s.io/kubernetes/pkg/client/unversioned" | ||||
| 	"k8s.io/kubernetes/pkg/metrics" | ||||
| 	"k8s.io/kubernetes/pkg/util/sets" | ||||
|  | ||||
| 	. "github.com/onsi/ginkgo" | ||||
| 	. "github.com/onsi/gomega" | ||||
| ) | ||||
|  | ||||
| // Missing = Assumed minus Observed, Invalid = Observed minus Assumed | ||||
| func validateLabelSet(labelSet map[string][]string, data metrics.Metrics, invalidLabels map[string]sets.String, missingLabels map[string]sets.String) { | ||||
| 	for metric, labels := range labelSet { | ||||
| 		vector, found := data[metric] | ||||
| 		Expect(found).To(Equal(true)) | ||||
| 		if found && len(vector) > 0 { | ||||
| 			for _, observation := range vector { | ||||
| 				for label := range observation.Metric { | ||||
| 					// We need to check if it's a known label for this metric. | ||||
| 					// Omit Prometheus internal metrics. | ||||
| 					if strings.HasPrefix(string(label), "__") { | ||||
| 						continue | ||||
| 					} | ||||
| 					invalidLabel := true | ||||
| 					for _, knownLabel := range labels { | ||||
| 						if string(label) == knownLabel { | ||||
| 							invalidLabel = false | ||||
| 						} | ||||
| 					} | ||||
| 					if invalidLabel { | ||||
| 						if _, ok := invalidLabels[metric]; !ok { | ||||
| 							invalidLabels[metric] = sets.NewString() | ||||
| 						} | ||||
| 						invalidLabels[metric].Insert(string(label)) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func checkMetrics(response metrics.Metrics, assumedMetrics map[string][]string) { | ||||
| 	invalidLabels := make(map[string]sets.String) | ||||
| 	unknownLabels := make(map[string]sets.String) | ||||
| 	validateLabelSet(metrics.CommonMetrics, response, invalidLabels, unknownLabels) | ||||
| 	validateLabelSet(assumedMetrics, response, invalidLabels, unknownLabels) | ||||
|  | ||||
| 	Expect(unknownLabels).To(BeEmpty()) | ||||
| 	Expect(invalidLabels).To(BeEmpty()) | ||||
| } | ||||
|  | ||||
| var _ = Describe("MetricsGrabber", func() { | ||||
| 	framework := NewFramework("metrics-grabber") | ||||
| 	var c *client.Client | ||||
| 	var grabber *metrics.MetricsGrabber | ||||
| 	BeforeEach(func() { | ||||
| 		var err error | ||||
| 		c = framework.Client | ||||
| 		expectNoError(err) | ||||
| 		grabber, err = metrics.NewMetricsGrabber(c, true, true, true, true) | ||||
| 		expectNoError(err) | ||||
| 	}) | ||||
|  | ||||
| 	It("should grab all metrics from API server.", func() { | ||||
| 		By("Connecting to /metrics endpoint") | ||||
| 		unknownMetrics := sets.NewString() | ||||
| 		response, err := grabber.GrabFromApiServer(unknownMetrics) | ||||
| 		expectNoError(err) | ||||
| 		Expect(unknownMetrics).To(BeEmpty()) | ||||
|  | ||||
| 		checkMetrics(metrics.Metrics(response), metrics.KnownApiServerMetrics) | ||||
| 	}) | ||||
|  | ||||
| 	It("should grab all metrics from a Kubelet.", func() { | ||||
| 		By("Connecting proxying to Node through the API server") | ||||
| 		nodes := ListSchedulableNodesOrDie(c) | ||||
| 		Expect(nodes.Items).NotTo(BeEmpty()) | ||||
| 		unknownMetrics := sets.NewString() | ||||
| 		response, err := grabber.GrabFromKubelet(nodes.Items[0].Name, unknownMetrics) | ||||
| 		expectNoError(err) | ||||
| 		Expect(unknownMetrics).To(BeEmpty()) | ||||
|  | ||||
| 		checkMetrics(metrics.Metrics(response), metrics.KnownKubeletMetrics) | ||||
| 	}) | ||||
|  | ||||
| 	It("should grab all metrics from a Scheduler.", func() { | ||||
| 		By("Connecting proxying to Pod through the API server") | ||||
| 		// Check if master Node is registered | ||||
| 		nodes, err := c.Nodes().List(api.ListOptions{}) | ||||
| 		expectNoError(err) | ||||
|  | ||||
| 		var masterRegistered = false | ||||
| 		for _, node := range nodes.Items { | ||||
| 			if strings.HasSuffix(node.Name, "master") { | ||||
| 				masterRegistered = true | ||||
| 			} | ||||
| 		} | ||||
| 		if !masterRegistered { | ||||
| 			Logf("Master is node registered. Skipping testing Scheduler metrics.") | ||||
| 			return | ||||
| 		} | ||||
| 		unknownMetrics := sets.NewString() | ||||
| 		response, err := grabber.GrabFromScheduler(unknownMetrics) | ||||
| 		expectNoError(err) | ||||
| 		Expect(unknownMetrics).To(BeEmpty()) | ||||
|  | ||||
| 		checkMetrics(metrics.Metrics(response), metrics.KnownSchedulerMetrics) | ||||
| 	}) | ||||
|  | ||||
| 	It("should grab all metrics from a ControllerManager.", func() { | ||||
| 		By("Connecting proxying to Pod through the API server") | ||||
| 		// Check if master Node is registered | ||||
| 		nodes, err := c.Nodes().List(api.ListOptions{}) | ||||
| 		expectNoError(err) | ||||
|  | ||||
| 		var masterRegistered = false | ||||
| 		for _, node := range nodes.Items { | ||||
| 			if strings.HasSuffix(node.Name, "master") { | ||||
| 				masterRegistered = true | ||||
| 			} | ||||
| 		} | ||||
| 		if !masterRegistered { | ||||
| 			Logf("Master is node registered. Skipping testing ControllerManager metrics.") | ||||
| 			return | ||||
| 		} | ||||
| 		unknownMetrics := sets.NewString() | ||||
| 		response, err := grabber.GrabFromControllerManager(unknownMetrics) | ||||
| 		expectNoError(err) | ||||
| 		Expect(unknownMetrics).To(BeEmpty()) | ||||
|  | ||||
| 		checkMetrics(metrics.Metrics(response), metrics.KnownControllerManagerMetrics) | ||||
| 	}) | ||||
| }) | ||||
		Reference in New Issue
	
	Block a user
	 gmarek
					gmarek