Merge pull request #31064 from soundcloud/grobie/filter-internal-labels

Automatic merge from submit-queue

Filter internal Kubernetes labels from Prometheus metrics

**What this PR does / why we need it**:

Kubernetes uses Docker labels as storage for some internal labels. The
majority of these labels are not meaningful metric labels and a few of
them are even harmful as they're not static and cause wrong aggregation
results.

This change provides a custom labels func to only attach meaningful
labels to cAdvisor exported metrics.

**Which issue this PR fixes**

google/cadvisor#1312

**Special notes for your reviewer**:

Depends on google/cadvisor#1429. Once that is merged, I'll update the vendor update commit.

**Release note**:

```release-note
Remove environment variables and internal Kubernetes Docker labels from cAdvisor Prometheus metric labels.

Old behavior:

- environment variables explicitly whitelisted via --docker-env-metadata-whitelist were exported as `container_env_*=*`. Default is zero so by default non were exported
- all docker labels were exported as `container_label_*=*`

New behavior:

- Only `container_name`, `pod_name`, `namespace`, `id`, `image`, and `name` labels are exposed
- no environment variables will be exposed ever via /metrics, even if whitelisted
```

---

Given that we have full control over the exported label set, I shortened the pod_name, pod_namespace and container_name label names. Below an example of the change (reformatted for readability).

```
# BEFORE
container_cpu_cfs_periods_total{
  container_label_io_kubernetes_container_hash="5af8c3b4",
  container_label_io_kubernetes_container_name="sync",
  container_label_io_kubernetes_container_restartCount="1",
  container_label_io_kubernetes_container_terminationMessagePath="/dev/termination-log",
  container_label_io_kubernetes_pod_name="popularsearches-web-3165456836-2bfey",
  container_label_io_kubernetes_pod_namespace="popularsearches",
  container_label_io_kubernetes_pod_terminationGracePeriod="30",
  container_label_io_kubernetes_pod_uid="6a291e48-47c4-11e6-84a4-c81f66bdf8bd",
  id="/docker/68e1f15353921f4d6d4d998fa7293306c4ac828d04d1284e410ddaa75cf8cf25",
  image="redacted.com/popularsearches:42-16-ba6bd88",
  name="k8s_sync.5af8c3b4_popularsearches-web-3165456836-2bfey_popularsearches_6a291e48-47c4-11e6-84a4-c81f66bdf8bd_c02d3775"
} 72819

# AFTER
container_cpu_cfs_periods_total{
  container_name="sync",
  pod_name="popularsearches-web-3165456836-2bfey",
  namespace="popularsearches",
  id="/docker/68e1f15353921f4d6d4d998fa7293306c4ac828d04d1284e410ddaa75cf8cf25",
  image="redacted.com/popularsearches:42-16-ba6bd88",
  name="k8s_sync.5af8c3b4_popularsearches-web-3165456836-2bfey_popularsearches_6a291e48-47c4-11e6-84a4-c81f66bdf8bd_c02d3775"
} 72819
```

Feedback requested on:
* Label names. Other suggestions? Should we keep these very long ones?
* Do we need to export io.kubernetes.pod.uid? It makes working with the metrics a bit more complicated and the pod name is already unique at any time (but not over time). The UID is aslo part of `name`.

As discussed with @timstclair, this should be added to v1.4 as the current labels are harmful.

PTAL @jimmidyson @fabxc @vishh
pull/6/head
Kubernetes Submit Queue 2016-08-25 21:11:10 -07:00 committed by GitHub
commit feb4d2004a
5 changed files with 248 additions and 145 deletions

164
Godeps/Godeps.json generated
View File

@ -1095,208 +1095,208 @@
},
{
"ImportPath": "github.com/google/cadvisor/api",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/cache/memory",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/client/v2",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/collector",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/container",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/container/common",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/container/docker",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/container/libcontainer",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/container/raw",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/container/rkt",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/container/systemd",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/devicemapper",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/events",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/fs",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/healthz",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/http",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/http/mux",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/info/v1",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/info/v1/test",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/info/v2",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/machine",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/manager",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/manager/watcher",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/manager/watcher/raw",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/manager/watcher/rkt",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/metrics",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/pages",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/pages/static",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/storage",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/summary",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/utils",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/utils/cloudinfo",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/utils/cpuload",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/utils/cpuload/netlink",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/utils/docker",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/utils/oomparser",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/utils/sysfs",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/utils/sysinfo",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/utils/tail",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/validate",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/cadvisor/version",
"Comment": "v0.23.2-89-g2ed7198",
"Rev": "2ed7198f77395ee9a172878a0a7ab92ab59a2cfd"
"Comment": "v0.24.0-alpha1-1-gd84e075",
"Rev": "d84e0758ab16ee68598702793119c9a7370c1522"
},
{
"ImportPath": "github.com/google/certificate-transparency/go",

View File

@ -33,7 +33,9 @@ import (
cadvisorapi "github.com/google/cadvisor/info/v1"
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
"github.com/google/cadvisor/manager"
"github.com/google/cadvisor/metrics"
"github.com/google/cadvisor/utils/sysfs"
"k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/util/runtime"
)
@ -70,7 +72,27 @@ func init() {
}
}
// Creates a cAdvisor and exports its API on the specified port if port > 0.
func containerLabels(c *cadvisorapi.ContainerInfo) map[string]string {
set := map[string]string{metrics.LabelID: c.Name}
if len(c.Aliases) > 0 {
set[metrics.LabelName] = c.Aliases[0]
}
if image := c.Spec.Image; len(image) > 0 {
set[metrics.LabelImage] = image
}
if v, ok := c.Spec.Labels[types.KubernetesPodNameLabel]; ok {
set["pod_name"] = v
}
if v, ok := c.Spec.Labels[types.KubernetesPodNamespaceLabel]; ok {
set["namespace"] = v
}
if v, ok := c.Spec.Labels[types.KubernetesContainerNameLabel]; ok {
set["container_name"] = v
}
return set
}
// New creates a cAdvisor and exports its API on the specified port if port > 0.
func New(port uint, runtime string) (Interface, error) {
sysFs, err := sysfs.NewRealSysFs()
if err != nil {
@ -108,7 +130,7 @@ func (cc *cadvisorClient) exportHTTP(port uint) error {
return err
}
cadvisorhttp.RegisterPrometheusHandler(mux, cc, "/metrics", nil)
cadvisorhttp.RegisterPrometheusHandler(mux, cc, "/metrics", containerLabels)
// Only start the http server if port > 0
if port > 0 {

View File

@ -0,0 +1,65 @@
// +build cgo,linux
/*
Copyright 2016 The Kubernetes Authors.
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 cadvisor
import (
"reflect"
"testing"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/metrics"
"k8s.io/kubernetes/pkg/kubelet/types"
)
func TestContainerLabels(t *testing.T) {
container := &info.ContainerInfo{
ContainerReference: info.ContainerReference{
Name: "/docker/f81ad5335d390944e454ea19ab0924037d57337c19731524ad96eb26e74b6c6d",
Aliases: []string{"k8s_POD.639b2af2_foo-web-315473031-e40e2_foobar_a369ace2-5fa9-11e6-b10f-c81f66e5e84d_851a97fd"},
},
Spec: info.ContainerSpec{
Image: "qux/foo:latest",
Labels: map[string]string{
"io.kubernetes.container.hash": "639b2af2",
types.KubernetesContainerNameLabel: "POD",
"io.kubernetes.container.restartCount": "0",
"io.kubernetes.container.terminationMessagePath": "",
types.KubernetesPodNameLabel: "foo-web-315473031-e40e2",
types.KubernetesPodNamespaceLabel: "foobar",
"io.kubernetes.pod.terminationGracePeriod": "30",
types.KubernetesPodUIDLabel: "a369ace2-5fa9-11e6-b10f-c81f66e5e84d",
},
Envs: map[string]string{
"foo+env": "prod",
},
},
}
want := map[string]string{
metrics.LabelID: "/docker/f81ad5335d390944e454ea19ab0924037d57337c19731524ad96eb26e74b6c6d",
metrics.LabelName: "k8s_POD.639b2af2_foo-web-315473031-e40e2_foobar_a369ace2-5fa9-11e6-b10f-c81f66e5e84d_851a97fd",
metrics.LabelImage: "qux/foo:latest",
"namespace": "foobar",
"container_name": "POD",
"pod_name": "foo-web-315473031-e40e2",
}
if have := containerLabels(container); !reflect.DeepEqual(want, have) {
t.Errorf("want %v, have %v", want, have)
}
}

View File

@ -89,8 +89,11 @@ func RegisterHandlers(mux httpmux.Mux, containerManager manager.Manager, httpAut
return nil
}
func RegisterPrometheusHandler(mux httpmux.Mux, containerManager manager.Manager, prometheusEndpoint string, containerNameToLabelsFunc metrics.ContainerNameToLabelsFunc) {
collector := metrics.NewPrometheusCollector(containerManager, containerNameToLabelsFunc)
// RegisterPrometheusHandler creates a new PrometheusCollector, registers it
// on the global registry and configures the provided HTTP mux to handle the
// given Prometheus endpoint.
func RegisterPrometheusHandler(mux httpmux.Mux, containerManager manager.Manager, prometheusEndpoint string, f metrics.ContainerLabelsFunc) {
collector := metrics.NewPrometheusCollector(containerManager, f)
prometheus.MustRegister(collector)
mux.Handle(prometheusEndpoint, prometheus.Handler())
}

View File

@ -25,13 +25,14 @@ import (
"github.com/prometheus/client_golang/prometheus"
)
// This will usually be manager.Manager, but can be swapped out for testing.
// infoProvider will usually be manager.Manager, but can be swapped out for testing.
type infoProvider interface {
// Get information about all subcontainers of the specified container (includes self).
// SubcontainersInfo provides information about all subcontainers of the
// specified container including itself.
SubcontainersInfo(containerName string, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error)
// Get information about the version.
// GetVersionInfo provides information about the version.
GetVersionInfo() (*info.VersionInfo, error)
// Get information about the machine.
// GetMachineInfo provides information about the machine.
GetMachineInfo() (*info.MachineInfo, error)
}
@ -56,8 +57,8 @@ func fsValues(fsStats []info.FsStats, valueFn func(*info.FsStats) float64) metri
return values
}
// A containerMetric describes a multi-dimensional metric used for exposing
// a certain type of container statistic.
// containerMetric describes a multi-dimensional metric used for exposing a
// certain type of container statistic.
type containerMetric struct {
name string
help string
@ -71,21 +72,29 @@ func (cm *containerMetric) desc(baseLabels []string) *prometheus.Desc {
return prometheus.NewDesc(cm.name, cm.help, append(baseLabels, cm.extraLabels...), nil)
}
type ContainerNameToLabelsFunc func(containerName string) map[string]string
// ContainerLabelsFunc defines all base labels and their values attached to
// each metric exported by cAdvisor.
type ContainerLabelsFunc func(*info.ContainerInfo) map[string]string
// PrometheusCollector implements prometheus.Collector.
type PrometheusCollector struct {
infoProvider infoProvider
errors prometheus.Gauge
containerMetrics []containerMetric
containerNameToLabels ContainerNameToLabelsFunc
infoProvider infoProvider
errors prometheus.Gauge
containerMetrics []containerMetric
containerLabelsFunc ContainerLabelsFunc
}
// NewPrometheusCollector returns a new PrometheusCollector.
func NewPrometheusCollector(infoProvider infoProvider, f ContainerNameToLabelsFunc) *PrometheusCollector {
// NewPrometheusCollector returns a new PrometheusCollector. The passed
// ContainerLabelsFunc specifies which base labels will be attached to all
// exported metrics. If left to nil, the DefaultContainerLabels function
// will be used instead.
func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc) *PrometheusCollector {
if f == nil {
f = DefaultContainerLabels
}
c := &PrometheusCollector{
infoProvider: infoProvider,
containerNameToLabels: f,
infoProvider: i,
containerLabelsFunc: f,
errors: prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "container",
Name: "scrape_error",
@ -533,10 +542,38 @@ func (c *PrometheusCollector) Collect(ch chan<- prometheus.Metric) {
}
const (
containerLabelPrefix = "container_label_"
containerEnvPrefix = "container_env_"
// ContainerLabelPrefix is the prefix added to all container labels.
ContainerLabelPrefix = "container_label_"
// ContainerEnvPrefix is the prefix added to all env variable labels.
ContainerEnvPrefix = "container_env_"
// LabelID is the name of the id label.
LabelID = "id"
// LabelName is the name of the name label.
LabelName = "name"
// LabelImage is the name of the image label.
LabelImage = "image"
)
// DefaultContainerLabels implements ContainerLabelsFunc. It exports the
// container name, first alias, image name as well as all its env and label
// values.
func DefaultContainerLabels(container *info.ContainerInfo) map[string]string {
set := map[string]string{LabelID: container.Name}
if len(container.Aliases) > 0 {
set[LabelName] = container.Aliases[0]
}
if image := container.Spec.Image; len(image) > 0 {
set[LabelImage] = image
}
for k, v := range container.Spec.Labels {
set[ContainerLabelPrefix+k] = v
}
for k, v := range container.Spec.Envs {
set[ContainerEnvPrefix+k] = v
}
return set
}
func (c *PrometheusCollector) collectContainersInfo(ch chan<- prometheus.Metric) {
containers, err := c.infoProvider.SubcontainersInfo("/", &info.ContainerInfoRequest{NumStats: 1})
if err != nil {
@ -545,56 +582,32 @@ func (c *PrometheusCollector) collectContainersInfo(ch chan<- prometheus.Metric)
return
}
for _, container := range containers {
baseLabels := []string{"id"}
id := container.Name
name := id
if len(container.Aliases) > 0 {
name = container.Aliases[0]
baseLabels = append(baseLabels, "name")
}
image := container.Spec.Image
if len(image) > 0 {
baseLabels = append(baseLabels, "image")
}
baseLabelValues := []string{id, name, image}[:len(baseLabels)]
if c.containerNameToLabels != nil {
newLabels := c.containerNameToLabels(name)
for k, v := range newLabels {
baseLabels = append(baseLabels, sanitizeLabelName(k))
baseLabelValues = append(baseLabelValues, v)
}
}
for k, v := range container.Spec.Labels {
baseLabels = append(baseLabels, sanitizeLabelName(containerLabelPrefix+k))
baseLabelValues = append(baseLabelValues, v)
}
for k, v := range container.Spec.Envs {
baseLabels = append(baseLabels, sanitizeLabelName(containerEnvPrefix+k))
baseLabelValues = append(baseLabelValues, v)
labels, values := []string{}, []string{}
for l, v := range c.containerLabelsFunc(container) {
labels = append(labels, sanitizeLabelName(l))
values = append(values, v)
}
// Container spec
desc := prometheus.NewDesc("container_start_time_seconds", "Start time of the container since unix epoch in seconds.", baseLabels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(container.Spec.CreationTime.Unix()), baseLabelValues...)
desc := prometheus.NewDesc("container_start_time_seconds", "Start time of the container since unix epoch in seconds.", labels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(container.Spec.CreationTime.Unix()), values...)
if container.Spec.HasCpu {
desc = prometheus.NewDesc("container_spec_cpu_period", "CPU period of the container.", baseLabels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(container.Spec.Cpu.Period), baseLabelValues...)
desc = prometheus.NewDesc("container_spec_cpu_period", "CPU period of the container.", labels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(container.Spec.Cpu.Period), values...)
if container.Spec.Cpu.Quota != 0 {
desc = prometheus.NewDesc("container_spec_cpu_quota", "CPU quota of the container.", baseLabels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(container.Spec.Cpu.Quota), baseLabelValues...)
desc = prometheus.NewDesc("container_spec_cpu_quota", "CPU quota of the container.", labels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(container.Spec.Cpu.Quota), values...)
}
desc := prometheus.NewDesc("container_spec_cpu_shares", "CPU share of the container.", baseLabels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(container.Spec.Cpu.Limit), baseLabelValues...)
desc := prometheus.NewDesc("container_spec_cpu_shares", "CPU share of the container.", labels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(container.Spec.Cpu.Limit), values...)
}
if container.Spec.HasMemory {
desc := prometheus.NewDesc("container_spec_memory_limit_bytes", "Memory limit for the container.", baseLabels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, specMemoryValue(container.Spec.Memory.Limit), baseLabelValues...)
desc = prometheus.NewDesc("container_spec_memory_swap_limit_bytes", "Memory swap limit for the container.", baseLabels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, specMemoryValue(container.Spec.Memory.SwapLimit), baseLabelValues...)
desc := prometheus.NewDesc("container_spec_memory_limit_bytes", "Memory limit for the container.", labels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, specMemoryValue(container.Spec.Memory.Limit), values...)
desc = prometheus.NewDesc("container_spec_memory_swap_limit_bytes", "Memory swap limit for the container.", labels, nil)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, specMemoryValue(container.Spec.Memory.SwapLimit), values...)
}
// Now for the actual metrics
@ -603,9 +616,9 @@ func (c *PrometheusCollector) collectContainersInfo(ch chan<- prometheus.Metric)
if cm.condition != nil && !cm.condition(container.Spec) {
continue
}
desc := cm.desc(baseLabels)
desc := cm.desc(labels)
for _, metricValue := range cm.getValues(stats) {
ch <- prometheus.MustNewConstMetric(desc, cm.valueType, float64(metricValue.value), append(baseLabelValues, metricValue.labels...)...)
ch <- prometheus.MustNewConstMetric(desc, cm.valueType, float64(metricValue.value), append(values, metricValue.labels...)...)
}
}
}