cadvisor/cadvisor.go
Joseph Lorenzini 4bd335b8fd Control whether container labels are exported as prometheus metrics.
when cadvisor exports metrics for docker containers, there is a root cgroup (/) and cgroup for a docker container (/docker/uuid).
If docker container has a label on it, then this label is applied to all containers including the root container.
Because some containers don't have that label, the label will have an empty value. The reason for this is that Prometheus
does not allow sending a metric with the same name, but different labels, so cadvisor uses empty label values based on
the set of all labels for a given metric. This can result in many docker containers getting a large number of empty labels
because another container has that label.

If large number of docker labels vary a lot across images, then the set of labels will be enormous, where most of the labels
will be empty and have no value as prometheus metrics. To avoid this problem, a flag is provided that allows a user to
disable exporting docker labels as metrics.
2018-07-04 10:53:08 -05:00

236 lines
7.7 KiB
Go

// Copyright 2014 Google Inc. 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 main
import (
"flag"
"fmt"
"net/http"
"net/http/pprof"
"os"
"os/signal"
"runtime"
"strings"
"syscall"
"time"
"github.com/google/cadvisor/container"
cadvisorhttp "github.com/google/cadvisor/http"
"github.com/google/cadvisor/manager"
"github.com/google/cadvisor/utils/sysfs"
"github.com/google/cadvisor/version"
"crypto/tls"
"github.com/golang/glog"
"github.com/google/cadvisor/metrics"
)
var argIp = flag.String("listen_ip", "", "IP to listen on, defaults to all IPs")
var argPort = flag.Int("port", 8080, "port to listen")
var maxProcs = flag.Int("max_procs", 0, "max number of CPUs that can be used simultaneously. Less than 1 for default (number of cores).")
var versionFlag = flag.Bool("version", false, "print cAdvisor version and exit")
var httpAuthFile = flag.String("http_auth_file", "", "HTTP auth file for the web UI")
var httpAuthRealm = flag.String("http_auth_realm", "localhost", "HTTP auth realm for the web UI")
var httpDigestFile = flag.String("http_digest_file", "", "HTTP digest file for the web UI")
var httpDigestRealm = flag.String("http_digest_realm", "localhost", "HTTP digest file for the web UI")
var prometheusEndpoint = flag.String("prometheus_endpoint", "/metrics", "Endpoint to expose Prometheus metrics on")
var maxHousekeepingInterval = flag.Duration("max_housekeeping_interval", 60*time.Second, "Largest interval to allow between container housekeepings")
var allowDynamicHousekeeping = flag.Bool("allow_dynamic_housekeeping", true, "Whether to allow the housekeeping interval to be dynamic")
var enableProfiling = flag.Bool("profiling", false, "Enable profiling via web interface host:port/debug/pprof/")
var collectorCert = flag.String("collector_cert", "", "Collector's certificate, exposed to endpoints for certificate based authentication.")
var collectorKey = flag.String("collector_key", "", "Key for the collector's certificate")
var storeContainerLabels = flag.Bool("store_container_labels", true, "convert container labels and environment variables into labels on prometheus metrics for each container. If flag set to false, then only metrics exported are container name, first alias, and image name")
var (
// Metrics to be ignored.
// Tcp metrics are ignored by default.
ignoreMetrics metricSetValue = metricSetValue{container.MetricSet{
container.NetworkTcpUsageMetrics: struct{}{},
container.NetworkUdpUsageMetrics: struct{}{},
container.ProcessSchedulerMetrics: struct{}{},
}}
// List of metrics that can be ignored.
ignoreWhitelist = container.MetricSet{
container.DiskUsageMetrics: struct{}{},
container.NetworkUsageMetrics: struct{}{},
container.NetworkTcpUsageMetrics: struct{}{},
container.NetworkUdpUsageMetrics: struct{}{},
container.PerCpuUsageMetrics: struct{}{},
container.ProcessSchedulerMetrics: struct{}{},
}
)
type metricSetValue struct {
container.MetricSet
}
func (ml *metricSetValue) String() string {
var values []string
for metric, _ := range ml.MetricSet {
values = append(values, string(metric))
}
return strings.Join(values, ",")
}
func (ml *metricSetValue) Set(value string) error {
ml.MetricSet = container.MetricSet{}
if value == "" {
return nil
}
for _, metric := range strings.Split(value, ",") {
if ignoreWhitelist.Has(container.MetricKind(metric)) {
(*ml).Add(container.MetricKind(metric))
} else {
return fmt.Errorf("unsupported metric %q specified in disable_metrics", metric)
}
}
return nil
}
func init() {
flag.Var(&ignoreMetrics, "disable_metrics", "comma-separated list of `metrics` to be disabled. Options are 'disk', 'network', 'tcp', 'udp', 'percpu'. Note: tcp and udp are disabled by default due to high CPU usage.")
// Default logging verbosity to V(2)
flag.Set("v", "2")
}
func main() {
defer glog.Flush()
flag.Parse()
if *versionFlag {
fmt.Printf("cAdvisor version %s (%s)\n", version.Info["version"], version.Info["revision"])
os.Exit(0)
}
setMaxProcs()
memoryStorage, err := NewMemoryStorage()
if err != nil {
glog.Fatalf("Failed to initialize storage driver: %s", err)
}
sysFs := sysfs.NewRealSysFs()
collectorHttpClient := createCollectorHttpClient(*collectorCert, *collectorKey)
containerManager, err := manager.New(memoryStorage, sysFs, *maxHousekeepingInterval, *allowDynamicHousekeeping, ignoreMetrics.MetricSet, &collectorHttpClient, []string{"/"})
if err != nil {
glog.Fatalf("Failed to create a Container Manager: %s", err)
}
mux := http.NewServeMux()
if *enableProfiling {
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
}
// Register all HTTP handlers.
err = cadvisorhttp.RegisterHandlers(mux, containerManager, *httpAuthFile, *httpAuthRealm, *httpDigestFile, *httpDigestRealm)
if err != nil {
glog.Fatalf("Failed to register HTTP handlers: %v", err)
}
containerLabelFunc := metrics.DefaultContainerLabels
if !*storeContainerLabels {
containerLabelFunc = metrics.BaseContainerLabels
}
cadvisorhttp.RegisterPrometheusHandler(mux, containerManager, *prometheusEndpoint, containerLabelFunc)
// Start the manager.
if err := containerManager.Start(); err != nil {
glog.Fatalf("Failed to start container manager: %v", err)
}
// Install signal handler.
installSignalHandler(containerManager)
glog.V(1).Infof("Starting cAdvisor version: %s-%s on port %d", version.Info["version"], version.Info["revision"], *argPort)
addr := fmt.Sprintf("%s:%d", *argIp, *argPort)
glog.Fatal(http.ListenAndServe(addr, mux))
}
func setMaxProcs() {
// TODO(vmarmol): Consider limiting if we have a CPU mask in effect.
// Allow as many threads as we have cores unless the user specified a value.
var numProcs int
if *maxProcs < 1 {
numProcs = runtime.NumCPU()
} else {
numProcs = *maxProcs
}
runtime.GOMAXPROCS(numProcs)
// Check if the setting was successful.
actualNumProcs := runtime.GOMAXPROCS(0)
if actualNumProcs != numProcs {
glog.Warningf("Specified max procs of %v but using %v", numProcs, actualNumProcs)
}
}
func installSignalHandler(containerManager manager.Manager) {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
// Block until a signal is received.
go func() {
sig := <-c
if err := containerManager.Stop(); err != nil {
glog.Errorf("Failed to stop container manager: %v", err)
}
glog.Infof("Exiting given signal: %v", sig)
os.Exit(0)
}()
}
func createCollectorHttpClient(collectorCert, collectorKey string) http.Client {
//Enable accessing insecure endpoints. We should be able to access metrics from any endpoint
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
}
if collectorCert != "" {
if collectorKey == "" {
glog.Fatal("The collector_key value must be specified if the collector_cert value is set.")
}
cert, err := tls.LoadX509KeyPair(collectorCert, collectorKey)
if err != nil {
glog.Fatalf("Failed to use the collector certificate and key: %s", err)
}
tlsConfig.Certificates = []tls.Certificate{cert}
tlsConfig.BuildNameToCertificate()
}
transport := &http.Transport{
TLSClientConfig: tlsConfig,
}
return http.Client{Transport: transport}
}