223 lines
7.0 KiB
Go
223 lines
7.0 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"
|
|
)
|
|
|
|
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 (
|
|
// Metrics to be ignored.
|
|
// Tcp metrics are ignored by default.
|
|
ignoreMetrics metricSetValue = metricSetValue{container.MetricSet{
|
|
container.NetworkTcpUsageMetrics: struct{}{},
|
|
container.NetworkUdpUsageMetrics: struct{}{},
|
|
}}
|
|
|
|
// List of metrics that can be ignored.
|
|
ignoreWhitelist = container.MetricSet{
|
|
container.DiskUsageMetrics: struct{}{},
|
|
container.NetworkUsageMetrics: struct{}{},
|
|
container.NetworkTcpUsageMetrics: struct{}{},
|
|
container.NetworkUdpUsageMetrics: 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'. Note: tcp and udp are disabled by default due to high CPU usage.")
|
|
}
|
|
|
|
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)
|
|
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)
|
|
}
|
|
|
|
cadvisorhttp.RegisterPrometheusHandler(mux, containerManager, *prometheusEndpoint, nil)
|
|
|
|
// Start the manager.
|
|
if err := containerManager.Start(); err != nil {
|
|
glog.Fatalf("Failed to start container manager: %v", err)
|
|
}
|
|
|
|
// Install signal handler.
|
|
installSignalHandler(containerManager)
|
|
|
|
glog.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}
|
|
}
|