github.com/aclisp/heapster@v0.19.2-0.20160613100040-51756f899a96/metrics/heapster.go (about)

     1  // Copyright 2014 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:generate ./hooks/run_extpoints.sh
    16  
    17  package main
    18  
    19  import (
    20  	"crypto/tls"
    21  	"flag"
    22  	"fmt"
    23  	"net/http"
    24  	"net/url"
    25  	"os"
    26  	"runtime"
    27  	"strings"
    28  	"time"
    29  
    30  	"github.com/golang/glog"
    31  	"github.com/prometheus/client_golang/prometheus"
    32  	"k8s.io/heapster/common/flags"
    33  	kube_config "k8s.io/heapster/common/kubernetes"
    34  	"k8s.io/heapster/metrics/core"
    35  	"k8s.io/heapster/metrics/manager"
    36  	"k8s.io/heapster/metrics/processors"
    37  	"k8s.io/heapster/metrics/sinks"
    38  	"k8s.io/heapster/metrics/sources"
    39  	"k8s.io/heapster/version"
    40  	kube_api "k8s.io/kubernetes/pkg/api"
    41  	"k8s.io/kubernetes/pkg/client/cache"
    42  	kube_client "k8s.io/kubernetes/pkg/client/unversioned"
    43  	"k8s.io/kubernetes/pkg/fields"
    44  )
    45  
    46  var (
    47  	argMetricResolution = flag.Duration("metric_resolution", 60*time.Second, "The resolution at which heapster will retain metrics.")
    48  	argPort             = flag.Int("port", 8082, "port to listen to")
    49  	argIp               = flag.String("listen_ip", "", "IP to listen on, defaults to all IPs")
    50  	argMaxProcs         = flag.Int("max_procs", 0, "max number of CPUs that can be used simultaneously. Less than 1 for default (number of cores)")
    51  	argTLSCertFile      = flag.String("tls_cert", "", "file containing TLS certificate")
    52  	argTLSKeyFile       = flag.String("tls_key", "", "file containing TLS key")
    53  	argTLSClientCAFile  = flag.String("tls_client_ca", "", "file containing TLS client CA for client cert validation")
    54  	argAllowedUsers     = flag.String("allowed_users", "", "comma-separated list of allowed users")
    55  	argSources          flags.Uris
    56  	argSinks            flags.Uris
    57  )
    58  
    59  func main() {
    60  	defer glog.Flush()
    61  	flag.Var(&argSources, "source", "source(s) to watch")
    62  	flag.Var(&argSinks, "sink", "external sink(s) that receive data")
    63  	flag.Parse()
    64  	setMaxProcs()
    65  	glog.Infof(strings.Join(os.Args, " "))
    66  	glog.Infof("Heapster version %v", version.HeapsterVersion)
    67  	if err := validateFlags(); err != nil {
    68  		glog.Fatal(err)
    69  	}
    70  
    71  	// sources
    72  	if len(argSources) != 1 {
    73  		glog.Fatal("Wrong number of sources specified")
    74  	}
    75  	sourceFactory := sources.NewSourceFactory()
    76  	sourceProvider, err := sourceFactory.BuildAll(argSources)
    77  	if err != nil {
    78  		glog.Fatalf("Failed to create source provide: %v", err)
    79  	}
    80  	sourceManager, err := sources.NewSourceManager(sourceProvider, sources.DefaultMetricsScrapeTimeout)
    81  	if err != nil {
    82  		glog.Fatalf("Failed to create source manager: %v", err)
    83  	}
    84  
    85  	// sinks
    86  	sinksFactory := sinks.NewSinkFactory()
    87  	metricSink, sinkList := sinksFactory.BuildAll(argSinks)
    88  	if metricSink == nil {
    89  		glog.Fatal("Failed to create metric sink")
    90  	}
    91  	for _, sink := range sinkList {
    92  		glog.Infof("Starting with %s", sink.Name())
    93  	}
    94  	sinkManager, err := sinks.NewDataSinkManager(sinkList, sinks.DefaultSinkExportDataTimeout, sinks.DefaultSinkStopTimeout)
    95  	if err != nil {
    96  		glog.Fatalf("Failed to created sink manager: %v", err)
    97  	}
    98  
    99  	// data processors
   100  	metricsToAggregate := []string{
   101  		core.MetricCpuUsageRate.Name,
   102  		core.MetricMemoryUsage.Name,
   103  		core.MetricCpuRequest.Name,
   104  		core.MetricCpuLimit.Name,
   105  		core.MetricMemoryRequest.Name,
   106  		core.MetricMemoryLimit.Name,
   107  	}
   108  
   109  	metricsToAggregateForNode := []string{
   110  		core.MetricCpuRequest.Name,
   111  		core.MetricCpuLimit.Name,
   112  		core.MetricMemoryRequest.Name,
   113  		core.MetricMemoryLimit.Name,
   114  	}
   115  
   116  	dataProcessors := []core.DataProcessor{
   117  		// Convert cumulaties to rate
   118  		processors.NewRateCalculator(core.RateMetricsMapping),
   119  	}
   120  
   121  	kubernetesUrl, err := getKubernetesAddress(argSources)
   122  	if err != nil {
   123  		glog.Fatalf("Failed to get kubernetes address: %v", err)
   124  	}
   125  	podLister, err := getPodLister(kubernetesUrl)
   126  	if err != nil {
   127  		glog.Fatalf("Failed to create podLister: %v", err)
   128  	}
   129  
   130  	podBasedEnricher, err := processors.NewPodBasedEnricher(podLister)
   131  	if err != nil {
   132  		glog.Fatalf("Failed to create PodBasedEnricher: %v", err)
   133  	}
   134  	dataProcessors = append(dataProcessors, podBasedEnricher)
   135  
   136  	namespaceBasedEnricher, err := processors.NewNamespaceBasedEnricher(kubernetesUrl)
   137  	if err != nil {
   138  		glog.Fatalf("Failed to create NamespaceBasedEnricher: %v", err)
   139  	}
   140  	dataProcessors = append(dataProcessors, namespaceBasedEnricher)
   141  
   142  	// then aggregators
   143  	dataProcessors = append(dataProcessors,
   144  		processors.NewPodAggregator(),
   145  		&processors.NamespaceAggregator{
   146  			MetricsToAggregate: metricsToAggregate,
   147  		},
   148  		&processors.NodeAggregator{
   149  			MetricsToAggregate: metricsToAggregateForNode,
   150  		},
   151  		&processors.ClusterAggregator{
   152  			MetricsToAggregate: metricsToAggregate,
   153  		})
   154  
   155  	nodeAutoscalingEnricher, err := processors.NewNodeAutoscalingEnricher(kubernetesUrl)
   156  	if err != nil {
   157  		glog.Fatalf("Failed to create NodeAutoscalingEnricher: %v", err)
   158  	}
   159  	dataProcessors = append(dataProcessors, nodeAutoscalingEnricher)
   160  
   161  	// main manager
   162  	manager, err := manager.NewManager(sourceManager, dataProcessors, sinkManager, *argMetricResolution,
   163  		manager.DefaultScrapeOffset, manager.DefaultMaxParallelism)
   164  	if err != nil {
   165  		glog.Fatalf("Failed to create main manager: %v", err)
   166  	}
   167  	manager.Start()
   168  
   169  	handler := setupHandlers(metricSink, podLister)
   170  	addr := fmt.Sprintf("%s:%d", *argIp, *argPort)
   171  	glog.Infof("Starting heapster on port %d", *argPort)
   172  
   173  	mux := http.NewServeMux()
   174  	promHandler := prometheus.Handler()
   175  	if len(*argTLSCertFile) > 0 && len(*argTLSKeyFile) > 0 {
   176  		if len(*argTLSClientCAFile) > 0 {
   177  			authPprofHandler, err := newAuthHandler(handler)
   178  			if err != nil {
   179  				glog.Fatalf("Failed to create authorized pprof handler: %v", err)
   180  			}
   181  			handler = authPprofHandler
   182  
   183  			authPromHandler, err := newAuthHandler(promHandler)
   184  			if err != nil {
   185  				glog.Fatalf("Failed to create authorized prometheus handler: %v", err)
   186  			}
   187  			promHandler = authPromHandler
   188  		}
   189  		mux.Handle("/", handler)
   190  		mux.Handle("/metrics", promHandler)
   191  
   192  		// If allowed users is set, then we need to enable Client Authentication
   193  		if len(*argAllowedUsers) > 0 {
   194  			server := &http.Server{
   195  				Addr:      addr,
   196  				Handler:   mux,
   197  				TLSConfig: &tls.Config{ClientAuth: tls.RequestClientCert},
   198  			}
   199  			glog.Fatal(server.ListenAndServeTLS(*argTLSCertFile, *argTLSKeyFile))
   200  		} else {
   201  			glog.Fatal(http.ListenAndServeTLS(addr, *argTLSCertFile, *argTLSKeyFile, mux))
   202  		}
   203  
   204  	} else {
   205  		mux.Handle("/", handler)
   206  		mux.Handle("/metrics", promHandler)
   207  		glog.Fatal(http.ListenAndServe(addr, mux))
   208  	}
   209  }
   210  
   211  // Gets the address of the kubernetes source from the list of source URIs.
   212  // Possible kubernetes sources are: 'kubernetes' and 'kubernetes.summary_api'
   213  func getKubernetesAddress(args flags.Uris) (*url.URL, error) {
   214  	for _, uri := range args {
   215  		if strings.SplitN(uri.Key, ".", 2)[0] == "kubernetes" {
   216  			return &uri.Val, nil
   217  		}
   218  	}
   219  	return nil, fmt.Errorf("No kubernetes source found.")
   220  }
   221  
   222  func getPodLister(url *url.URL) (*cache.StoreToPodLister, error) {
   223  	kubeConfig, err := kube_config.GetKubeClientConfig(url)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	kubeClient := kube_client.NewOrDie(kubeConfig)
   228  
   229  	lw := cache.NewListWatchFromClient(kubeClient, "pods", kube_api.NamespaceAll, fields.Everything())
   230  	store := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
   231  	podLister := &cache.StoreToPodLister{Indexer: store}
   232  	reflector := cache.NewReflector(lw, &kube_api.Pod{}, store, time.Hour)
   233  	reflector.Run()
   234  
   235  	return podLister, nil
   236  }
   237  
   238  func validateFlags() error {
   239  	if *argMetricResolution < 5*time.Second {
   240  		return fmt.Errorf("metric resolution needs to be greater than 5 seconds - %d", *argMetricResolution)
   241  	}
   242  	if (len(*argTLSCertFile) > 0 && len(*argTLSKeyFile) == 0) || (len(*argTLSCertFile) == 0 && len(*argTLSKeyFile) > 0) {
   243  		return fmt.Errorf("both TLS certificate & key are required to enable TLS serving")
   244  	}
   245  	if len(*argTLSClientCAFile) > 0 && len(*argTLSCertFile) == 0 {
   246  		return fmt.Errorf("client cert authentication requires TLS certificate & key")
   247  	}
   248  	return nil
   249  }
   250  
   251  func setMaxProcs() {
   252  	// Allow as many threads as we have cores unless the user specified a value.
   253  	var numProcs int
   254  	if *argMaxProcs < 1 {
   255  		numProcs = runtime.NumCPU()
   256  	} else {
   257  		numProcs = *argMaxProcs
   258  	}
   259  	runtime.GOMAXPROCS(numProcs)
   260  
   261  	// Check if the setting was successful.
   262  	actualNumProcs := runtime.GOMAXPROCS(0)
   263  	if actualNumProcs != numProcs {
   264  		glog.Warningf("Specified max procs of %d but using %d", numProcs, actualNumProcs)
   265  	}
   266  }