From bf0bc41b36876ef4bf9967da992b634860cbf81d Mon Sep 17 00:00:00 2001 From: Roland Schilter Date: Thu, 10 Aug 2017 18:46:07 +0100 Subject: [PATCH] Use percentage and MB for CPU/Memory urls And more DRY. --- render/detailed/links.go | 85 +++++++++++++++++++---------------- render/detailed/links_test.go | 17 +++---- 2 files changed, 55 insertions(+), 47 deletions(-) diff --git a/render/detailed/links.go b/render/detailed/links.go index 6277aa50c..ca02ed9fd 100644 --- a/render/detailed/links.go +++ b/render/detailed/links.go @@ -2,6 +2,7 @@ package detailed import ( "bytes" + "fmt" "net/url" "strings" @@ -11,8 +12,13 @@ import ( "github.com/ugorji/go/codec" ) -// Replacement variable name for the query in the metrics graph url -const urlQueryVarName = ":query" +const ( + // Replacement variable name for the query in the metrics graph url + urlQueryVarName = ":query" + + idReceiveBytes = "receive_bytes" + idTransmitBytes = "transmit_bytes" +) var ( // As configured by the user @@ -32,54 +38,32 @@ var ( Label: "Memory", }, { - ID: "receive_bytes", + ID: idReceiveBytes, Label: "Rx/s", }, { - ID: "transmit_bytes", + ID: idTransmitBytes, Label: "Tx/s", }, } // Prometheus queries for topologies - // - // Metrics - // - `container_cpu_usage_seconds_total` --> cAdvisor in Kubelets. - // - `container_memory_usage_bytes` --> cAdvisor in Kubelets. topologyQueries = map[string]map[string]string{ // Containers - report.Container: { - docker.MemoryUsage: `sum(container_memory_usage_bytes{container_name="{{label}}"})`, - docker.CPUTotalUsage: `sum(rate(container_cpu_usage_seconds_total{container_name="{{label}}"}[1m]))`, - }, - report.ContainerImage: { - docker.MemoryUsage: `sum(container_memory_usage_bytes{image="{{label}}"})`, - docker.CPUTotalUsage: `sum(rate(container_cpu_usage_seconds_total{image="{{label}}"}[1m]))`, - }, - "group:container:docker_container_hostname": { - docker.MemoryUsage: `sum(container_memory_usage_bytes{pod_name="{{label}}"})`, - docker.CPUTotalUsage: `sum(rate(container_cpu_usage_seconds_total{pod_name="{{label}}"}[1m]))`, - }, + report.Container: formatMetricQueries(`container_name="{{label}}"`, []string{docker.MemoryUsage, docker.CPUTotalUsage}), + report.ContainerImage: formatMetricQueries(`image="{{label}}"`, []string{docker.MemoryUsage, docker.CPUTotalUsage}), + "group:container:docker_container_hostname": formatMetricQueries(`pod_name="{{label}}"`, []string{docker.MemoryUsage, docker.CPUTotalUsage}), // Kubernetes topologies - report.Pod: { - docker.MemoryUsage: `sum(container_memory_usage_bytes{pod_name="{{label}}"})`, - docker.CPUTotalUsage: `sum(rate(container_cpu_usage_seconds_total{pod_name="{{label}}"}[1m]))`, - "receive_bytes": `sum(rate(container_network_receive_bytes_total{pod_name="{{label}}"}[5m]))`, - "transmit_bytes": `sum(rate(container_network_transmit_bytes_total{pod_name="{{label}}"}[5m]))`, - }, - // Pod naming: - // https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#pod-template-hash-label - "__k8s_controllers": { - docker.MemoryUsage: `sum(container_memory_usage_bytes{pod_name=~"^{{label}}-[^-]+-[^-]+$"})`, - docker.CPUTotalUsage: `sum(rate(container_cpu_usage_seconds_total{pod_name=~"^{{label}}-[^-]+-[^-]+$"}[1m]))`, - }, - report.DaemonSet: { - docker.MemoryUsage: `sum(container_memory_usage_bytes{pod_name=~"^{{label}}-[^-]+$"})`, - docker.CPUTotalUsage: `sum(rate(container_cpu_usage_seconds_total{pod_name=~"^{{label}}-[^-]+$"}[1m]))`, - }, + report.Pod: formatMetricQueries( + `pod_name="{{label}}"`, + []string{docker.MemoryUsage, docker.CPUTotalUsage, idReceiveBytes, idTransmitBytes}, + ), + // Pod naming: // https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#pod-template-hash-label + "__k8s_controllers": formatMetricQueries(`pod_name=~"^{{label}}-[^-]+-[^-]+$"}`, []string{docker.MemoryUsage, docker.CPUTotalUsage}), + report.DaemonSet: formatMetricQueries(`pod_name=~"^{{label}}-[^-]+$"}`, []string{docker.MemoryUsage, docker.CPUTotalUsage}), report.Service: { // These recording rules must be defined in the prometheus config. // NB: Pods need to be labeled and selected by their respective Service name, meaning: @@ -90,12 +74,35 @@ var ( }, } k8sControllers = map[string]struct{}{ - report.Deployment: {}, - "stateful_set": {}, - "cron_job": {}, + report.Deployment: {}, + report.StatefulSet: {}, + report.CronJob: {}, } ) +func formatMetricQueries(filter string, ids []string) map[string]string { + queries := make(map[string]string) + for _, id := range ids { + // All `container_*`metrics are provided by cAdvisor in Kubelets + switch id { + case docker.MemoryUsage: + queries[id] = fmt.Sprintf("sum(container_memory_usage_bytes{%s})/1024/1024", filter) + case docker.CPUTotalUsage: + queries[id] = fmt.Sprintf( + "sum(rate(container_cpu_usage_seconds_total{%s}[1m]))/count(container_cpu_usage_seconds_total{%s})*100", + filter, + filter, + ) + case idReceiveBytes: + queries[id] = fmt.Sprintf(`sum(rate(container_network_receive_bytes_total{%s}[5m]))`, filter) + case idTransmitBytes: + queries[id] = fmt.Sprintf(`sum(rate(container_network_transmit_bytes_total{%s}[5m]))`, filter) + } + } + + return queries +} + // SetMetricsGraphURL sets the URL we deduce our eventual metric link from. // Supports placeholders such as `:orgID` and `:query`. An empty url disables // this feature. If the `:query` part is missing, a JSON version will be diff --git a/render/detailed/links_test.go b/render/detailed/links_test.go index 0a107d6c3..b1b87889d 100644 --- a/render/detailed/links_test.go +++ b/render/detailed/links_test.go @@ -1,6 +1,7 @@ package detailed_test import ( + "strings" "testing" "github.com/weaveworks/scope/probe/docker" @@ -47,10 +48,10 @@ func TestRenderMetricURLs(t *testing.T) { s := detailed.NodeSummary{Label: "foo", Metrics: sampleMetrics} result := detailed.RenderMetricURLs(s, samplePodNode) - u := "/prom/:orgID/notebook/new/%7B%22cells%22%3A%5B%7B%22queries%22%3A%5B%22sum%28container_memory_usage_bytes%7Bpod_name%3D%5C%22foo%5C%22%7D%29%22%5D%7D%5D%7D" - assert.Equal(t, u, result.Metrics[0].URL) - u = "/prom/:orgID/notebook/new/%7B%22cells%22%3A%5B%7B%22queries%22%3A%5B%22sum%28rate%28container_cpu_usage_seconds_total%7Bpod_name%3D%5C%22foo%5C%22%7D%5B1m%5D%29%29%22%5D%7D%5D%7D" - assert.Equal(t, u, result.Metrics[1].URL) + assert.Equal(t, 0, strings.Index(result.Metrics[0].URL, sampleMetricsGraphURL)) + assert.Contains(t, result.Metrics[0].URL, "container_memory_usage_bytes%7Bpod_name%3D%5C%22foo%5C%22%7D") + assert.Equal(t, 0, strings.Index(result.Metrics[1].URL, sampleMetricsGraphURL)) + assert.Contains(t, result.Metrics[1].URL, "container_cpu_usage_seconds_total%7Bpod_name%3D%5C%22foo%5C%22%7D") } func TestRenderMetricURLs_EmptyMetrics(t *testing.T) { @@ -94,8 +95,8 @@ func TestRenderMetricURLs_QueryReplacement(t *testing.T) { s := detailed.NodeSummary{Label: "foo", Metrics: sampleMetrics} result := detailed.RenderMetricURLs(s, samplePodNode) - u := "http://example.test/?q=sum%28container_memory_usage_bytes%7Bpod_name%3D%22foo%22%7D%29" - assert.Equal(t, u, result.Metrics[0].URL) - u = "http://example.test/?q=sum%28rate%28container_cpu_usage_seconds_total%7Bpod_name%3D%22foo%22%7D%5B1m%5D%29%29" - assert.Equal(t, u, result.Metrics[1].URL) + assert.Contains(t, result.Metrics[0].URL, "http://example.test/?q=") + assert.Contains(t, result.Metrics[0].URL, "container_memory_usage_bytes%7Bpod_name%3D%22foo%22%7D") + assert.Contains(t, result.Metrics[1].URL, "http://example.test/?q=") + assert.Contains(t, result.Metrics[1].URL, "container_cpu_usage_seconds_total%7Bpod_name%3D%22foo%22%7D") }