diff --git a/cmd/kube-scheduler/app/BUILD b/cmd/kube-scheduler/app/BUILD index a98dc30914..d6de2aaef8 100644 --- a/cmd/kube-scheduler/app/BUILD +++ b/cmd/kube-scheduler/app/BUILD @@ -21,7 +21,6 @@ go_library( "//pkg/scheduler/api:go_default_library", "//pkg/scheduler/api/latest:go_default_library", "//pkg/scheduler/factory:go_default_library", - "//pkg/scheduler/metrics:go_default_library", "//pkg/util/configz:go_default_library", "//pkg/util/flag:go_default_library", "//pkg/version:go_default_library", diff --git a/cmd/kube-scheduler/app/server.go b/cmd/kube-scheduler/app/server.go index ee776c89b6..11ace1a983 100644 --- a/cmd/kube-scheduler/app/server.go +++ b/cmd/kube-scheduler/app/server.go @@ -19,7 +19,6 @@ package app import ( "fmt" - "io" "io/ioutil" "net/http" "os" @@ -52,7 +51,6 @@ import ( schedulerapi "k8s.io/kubernetes/pkg/scheduler/api" latestschedulerapi "k8s.io/kubernetes/pkg/scheduler/api/latest" "k8s.io/kubernetes/pkg/scheduler/factory" - "k8s.io/kubernetes/pkg/scheduler/metrics" "k8s.io/kubernetes/pkg/util/configz" utilflag "k8s.io/kubernetes/pkg/util/flag" "k8s.io/kubernetes/pkg/version" @@ -223,23 +221,11 @@ func buildHandlerChain(handler http.Handler, authn authenticator.Request, authz return handler } -func installMetricHandler(pathRecorderMux *mux.PathRecorderMux) { - configz.InstallHandler(pathRecorderMux) - defaultMetricsHandler := prometheus.Handler().ServeHTTP - pathRecorderMux.HandleFunc("/metrics", func(w http.ResponseWriter, req *http.Request) { - if req.Method == "DELETE" { - metrics.Reset() - io.WriteString(w, "metrics reset\n") - return - } - defaultMetricsHandler(w, req) - }) -} - // newMetricsHandler builds a metrics server from the config. func newMetricsHandler(config *componentconfig.KubeSchedulerConfiguration) http.Handler { pathRecorderMux := mux.NewPathRecorderMux("kube-scheduler") - installMetricHandler(pathRecorderMux) + configz.InstallHandler(pathRecorderMux) + pathRecorderMux.Handle("/metrics", prometheus.Handler()) if config.EnableProfiling { routes.Profiling{}.Install(pathRecorderMux) if config.EnableContentionProfiling { @@ -256,7 +242,8 @@ func newHealthzHandler(config *componentconfig.KubeSchedulerConfiguration, separ pathRecorderMux := mux.NewPathRecorderMux("kube-scheduler") healthz.InstallHandler(pathRecorderMux) if !separateMetrics { - installMetricHandler(pathRecorderMux) + configz.InstallHandler(pathRecorderMux) + pathRecorderMux.Handle("/metrics", prometheus.Handler()) } if config.EnableProfiling { routes.Profiling{}.Install(pathRecorderMux) diff --git a/pkg/scheduler/metrics/metrics.go b/pkg/scheduler/metrics/metrics.go index 0250ddb76d..4836ac4b93 100644 --- a/pkg/scheduler/metrics/metrics.go +++ b/pkg/scheduler/metrics/metrics.go @@ -23,38 +23,29 @@ import ( "github.com/prometheus/client_golang/prometheus" ) -const ( - // SchedulerSubsystem - subsystem name used by scheduler - SchedulerSubsystem = "scheduler" - // SchedulingLatencyName - scheduler latency metric name - SchedulingLatencyName = "scheduling_latencies_summary" - - // OperationLabel - operation label name - OperationLabel = "operation" - // Binding - binding operation label value - Binding = "binding" - // SchedulingAlgorithm - scheduling algorithm operation label value - SchedulingAlgorithm = "scheduling_algorithm" - // E2eScheduling - e2e scheduling operation label value - E2eScheduling = "e2e_scheduling" -) +const schedulerSubsystem = "scheduler" // All the histogram based metrics have 1ms as size for the smallest bucket. var ( - SchedulingLatency = prometheus.NewSummaryVec( - prometheus.SummaryOpts{ - Subsystem: SchedulerSubsystem, - Name: SchedulingLatencyName, - Help: "Scheduling latency in microseconds split by sub-parts of the scheduling operation", - // Make the sliding window of 5h. - // TODO: The value for this should be based on some SLI definition (long term). - MaxAge: 5 * time.Hour, + E2eSchedulingLatency = prometheus.NewHistogram( + prometheus.HistogramOpts{ + Subsystem: schedulerSubsystem, + Name: "e2e_scheduling_latency_microseconds", + Help: "E2e scheduling latency (scheduling algorithm + binding)", + Buckets: prometheus.ExponentialBuckets(1000, 2, 15), + }, + ) + SchedulingAlgorithmLatency = prometheus.NewHistogram( + prometheus.HistogramOpts{ + Subsystem: schedulerSubsystem, + Name: "scheduling_algorithm_latency_microseconds", + Help: "Scheduling algorithm latency", + Buckets: prometheus.ExponentialBuckets(1000, 2, 15), }, - []string{OperationLabel}, ) SchedulingAlgorithmPredicateEvaluationDuration = prometheus.NewHistogram( prometheus.HistogramOpts{ - Subsystem: SchedulerSubsystem, + Subsystem: schedulerSubsystem, Name: "scheduling_algorithm_predicate_evaluation", Help: "Scheduling algorithm predicate evaluation duration", Buckets: prometheus.ExponentialBuckets(1000, 2, 15), @@ -62,7 +53,7 @@ var ( ) SchedulingAlgorithmPriorityEvaluationDuration = prometheus.NewHistogram( prometheus.HistogramOpts{ - Subsystem: SchedulerSubsystem, + Subsystem: schedulerSubsystem, Name: "scheduling_algorithm_priority_evaluation", Help: "Scheduling algorithm priority evaluation duration", Buckets: prometheus.ExponentialBuckets(1000, 2, 15), @@ -70,48 +61,50 @@ var ( ) SchedulingAlgorithmPremptionEvaluationDuration = prometheus.NewHistogram( prometheus.HistogramOpts{ - Subsystem: SchedulerSubsystem, + Subsystem: schedulerSubsystem, Name: "scheduling_algorithm_preemption_evaluation", Help: "Scheduling algorithm preemption evaluation duration", Buckets: prometheus.ExponentialBuckets(1000, 2, 15), }, ) + BindingLatency = prometheus.NewHistogram( + prometheus.HistogramOpts{ + Subsystem: schedulerSubsystem, + Name: "binding_latency_microseconds", + Help: "Binding latency", + Buckets: prometheus.ExponentialBuckets(1000, 2, 15), + }, + ) PreemptionVictims = prometheus.NewGauge( prometheus.GaugeOpts{ - Subsystem: SchedulerSubsystem, + Subsystem: schedulerSubsystem, Name: "pod_preemption_victims", Help: "Number of selected preemption victims", }) PreemptionAttempts = prometheus.NewCounter( prometheus.CounterOpts{ - Subsystem: SchedulerSubsystem, + Subsystem: schedulerSubsystem, Name: "total_preemption_attempts", Help: "Total preemption attempts in the cluster till now", }) - metricsList = []prometheus.Collector{ - SchedulingLatency, - SchedulingAlgorithmPredicateEvaluationDuration, - SchedulingAlgorithmPriorityEvaluationDuration, - SchedulingAlgorithmPremptionEvaluationDuration, - PreemptionVictims, - PreemptionAttempts, - } ) var registerMetrics sync.Once // Register all metrics. func Register() { + // Register the metrics. registerMetrics.Do(func() { - for _, metric := range metricsList { - prometheus.MustRegister(metric) - } - }) -} + prometheus.MustRegister(E2eSchedulingLatency) + prometheus.MustRegister(SchedulingAlgorithmLatency) + prometheus.MustRegister(BindingLatency) -// Reset resets metrics -func Reset() { - SchedulingLatency.Reset() + prometheus.MustRegister(SchedulingAlgorithmPredicateEvaluationDuration) + prometheus.MustRegister(SchedulingAlgorithmPriorityEvaluationDuration) + prometheus.MustRegister(SchedulingAlgorithmPremptionEvaluationDuration) + prometheus.MustRegister(PreemptionVictims) + prometheus.MustRegister(PreemptionAttempts) + }) } // SinceInMicroseconds gets the time since the specified start in microseconds. diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go index b4667d4ecb..eb8d948af1 100644 --- a/pkg/scheduler/scheduler.go +++ b/pkg/scheduler/scheduler.go @@ -429,7 +429,7 @@ func (sched *Scheduler) bind(assumed *v1.Pod, b *v1.Binding) error { return err } - metrics.SchedulingLatency.WithLabelValues(metrics.Binding).Observe(metrics.SinceInMicroseconds(bindingStart)) + metrics.BindingLatency.Observe(metrics.SinceInMicroseconds(bindingStart)) sched.config.Recorder.Eventf(assumed, v1.EventTypeNormal, "Scheduled", "Successfully assigned %v/%v to %v", assumed.Namespace, assumed.Name, b.Target.Name) return nil } @@ -461,7 +461,7 @@ func (sched *Scheduler) scheduleOne() { } return } - metrics.SchedulingLatency.WithLabelValues(metrics.SchedulingAlgorithm).Observe(metrics.SinceInMicroseconds(start)) + metrics.SchedulingAlgorithmLatency.Observe(metrics.SinceInMicroseconds(start)) // Tell the cache to assume that a pod now is running on a given node, even though it hasn't been bound yet. // This allows us to keep scheduling without waiting on binding to occur. assumedPod := pod.DeepCopy() @@ -496,7 +496,7 @@ func (sched *Scheduler) scheduleOne() { Name: suggestedHost, }, }) - metrics.SchedulingLatency.WithLabelValues(metrics.E2eScheduling).Observe(metrics.SinceInMicroseconds(start)) + metrics.E2eSchedulingLatency.Observe(metrics.SinceInMicroseconds(start)) if err != nil { glog.Errorf("Internal error binding pod: (%v)", err) } diff --git a/test/e2e/framework/BUILD b/test/e2e/framework/BUILD index fc650ed9fc..022339ae39 100644 --- a/test/e2e/framework/BUILD +++ b/test/e2e/framework/BUILD @@ -72,7 +72,6 @@ go_library( "//pkg/master/ports:go_default_library", "//pkg/scheduler/algorithm/predicates:go_default_library", "//pkg/scheduler/cache:go_default_library", - "//pkg/scheduler/metrics:go_default_library", "//pkg/security/podsecuritypolicy/seccomp:go_default_library", "//pkg/ssh:go_default_library", "//pkg/util/file:go_default_library", diff --git a/test/e2e/framework/metrics_util.go b/test/e2e/framework/metrics_util.go index 9b7b5b2aa8..e8ae03d375 100644 --- a/test/e2e/framework/metrics_util.go +++ b/test/e2e/framework/metrics_util.go @@ -32,7 +32,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" clientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/pkg/master/ports" - schedulermetric "k8s.io/kubernetes/pkg/scheduler/metrics" "k8s.io/kubernetes/pkg/util/system" "k8s.io/kubernetes/test/e2e/framework/metrics" @@ -131,8 +130,6 @@ func (m *MetricsForE2E) SummaryKind() string { return "MetricsForE2E" } -var SchedulingLatencyMetricName = model.LabelValue(schedulermetric.SchedulerSubsystem + "_" + schedulermetric.SchedulingLatencyName) - var InterestingApiServerMetrics = []string{ "apiserver_request_count", "apiserver_request_latencies_summary", @@ -445,29 +442,27 @@ func getMetrics(c clientset.Interface) (string, error) { return string(body), nil } -// Sends REST request to kube scheduler metrics -func sendRestRequestToScheduler(c clientset.Interface, op string) (string, error) { - opUpper := strings.ToUpper(op) - if opUpper != "GET" && opUpper != "DELETE" { - return "", fmt.Errorf("Unknown REST request") - } +// Retrieves scheduler latency metrics. +func getSchedulingLatency(c clientset.Interface) (*SchedulingMetrics, error) { + result := SchedulingMetrics{} + // Check if master Node is registered nodes, err := c.CoreV1().Nodes().List(metav1.ListOptions{}) ExpectNoError(err) + var data string var masterRegistered = false for _, node := range nodes.Items { if system.IsMasterNode(node.Name) { masterRegistered = true } } - - var responseText string if masterRegistered { ctx, cancel := context.WithTimeout(context.Background(), SingleCallTimeout) defer cancel() - body, err := c.CoreV1().RESTClient().Verb(opUpper). + var rawData []byte + rawData, err = c.CoreV1().RESTClient().Get(). Context(ctx). Namespace(metav1.NamespaceSystem). Resource("pods"). @@ -477,46 +472,33 @@ func sendRestRequestToScheduler(c clientset.Interface, op string) (string, error Do().Raw() ExpectNoError(err) - responseText = string(body) + data = string(rawData) } else { // If master is not registered fall back to old method of using SSH. if TestContext.Provider == "gke" { Logf("Not grabbing scheduler metrics through master SSH: unsupported for gke") - return "", nil + return nil, nil } - - cmd := "curl -X " + opUpper + " http://localhost:10251/metrics" + cmd := "curl http://localhost:10251/metrics" sshResult, err := SSH(cmd, GetMasterHost()+":22", TestContext.Provider) if err != nil || sshResult.Code != 0 { - return "", fmt.Errorf("unexpected error (code: %d) in ssh connection to master: %#v", sshResult.Code, err) + return &result, fmt.Errorf("unexpected error (code: %d) in ssh connection to master: %#v", sshResult.Code, err) } - responseText = sshResult.Stdout + data = sshResult.Stdout } - return responseText, nil -} - -// Retrieves scheduler latency metrics. -func getSchedulingLatency(c clientset.Interface) (*SchedulingMetrics, error) { - result := SchedulingMetrics{} - data, err := sendRestRequestToScheduler(c, "GET") - samples, err := extractMetricSamples(data) if err != nil { return nil, err } for _, sample := range samples { - if sample.Metric[model.MetricNameLabel] != SchedulingLatencyMetricName { - continue - } - var metric *LatencyMetric = nil - switch sample.Metric[schedulermetric.OperationLabel] { - case schedulermetric.SchedulingAlgorithm: + switch sample.Metric[model.MetricNameLabel] { + case "scheduler_scheduling_algorithm_latency_microseconds": metric = &result.SchedulingLatency - case schedulermetric.Binding: + case "scheduler_binding_latency_microseconds": metric = &result.BindingLatency - case schedulermetric.E2eScheduling: + case "scheduler_e2e_scheduling_latency_microseconds": metric = &result.E2ELatency } if metric == nil { @@ -528,7 +510,7 @@ func getSchedulingLatency(c clientset.Interface) (*SchedulingMetrics, error) { if err != nil { return nil, err } - setQuantile(metric, quantile, time.Duration(int64(latency))) + setQuantile(metric, quantile, time.Duration(int64(latency))*time.Microsecond) } return &result, nil } @@ -542,14 +524,6 @@ func VerifySchedulerLatency(c clientset.Interface) (*SchedulingMetrics, error) { return latency, nil } -func ResetSchedulerMetrics(c clientset.Interface) error { - responseText, err := sendRestRequestToScheduler(c, "DELETE") - if err != nil || responseText != "metrics reset\n" { - return fmt.Errorf("Unexpected response: %q", responseText) - } - return nil -} - func PrettyPrintJSON(metrics interface{}) string { output := &bytes.Buffer{} if err := json.NewEncoder(output).Encode(metrics); err != nil { diff --git a/test/e2e/scalability/density.go b/test/e2e/scalability/density.go index 30df63e7af..92d4f8b991 100644 --- a/test/e2e/scalability/density.go +++ b/test/e2e/scalability/density.go @@ -413,6 +413,8 @@ var _ = SIGDescribe("Density", func() { } // Verify scheduler metrics. + // TODO: Reset metrics at the beginning of the test. + // We should do something similar to how we do it for APIserver. latency, err := framework.VerifySchedulerLatency(c) framework.ExpectNoError(err) if err == nil { @@ -467,7 +469,6 @@ var _ = SIGDescribe("Density", func() { uuid = string(utiluuid.NewUUID()) - framework.ExpectNoError(framework.ResetSchedulerMetrics(c)) framework.ExpectNoError(framework.ResetMetrics(c)) framework.ExpectNoError(os.Mkdir(fmt.Sprintf(framework.TestContext.OutputDir+"/%s", uuid), 0777))