2015-06-15 03:36:32 -07:00
|
|
|
// Copyright 2015 The Prometheus 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.
|
|
|
|
|
2016-02-17 14:52:44 -08:00
|
|
|
// The main package for the Prometheus server executable.
|
2015-06-15 03:36:32 -07:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
2015-06-15 03:23:02 -07:00
|
|
|
"fmt"
|
2015-06-15 03:36:32 -07:00
|
|
|
_ "net/http/pprof" // Comment this line to disable pprof endpoint.
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
main.go: Set GOGC to 40 by default
Rationale: The default value for GOGC is 100, i.e. a garbage collected
is initialized once as many heap space has been allocated as was in
use after the last GC was done. This ratio doesn't make a lot of sense
in Prometheus, as typically about 60% of the heap is allocated for
long-lived memory chunks (most of which are around for many hours if
not days). Thus, short-lived heap objects are accumulated for quite
some time until they finally match the large amount of memory used by
bulk memory chunks and a gigantic GC cyle is invoked. With GOGC=40, we
are essentially reinstating "normal" GC behavior by acknowledging that
about 60% of the heap are used for long-term bulk storage.
The median Prometheus production server at SoundCloud runs a GC cycle
every 90 seconds. With GOGC=40, a GC cycle is run every 35 seconds
(which is still not very often). However, the effective RAM usage is
now reduced by about 30%. If settings are updated to utilize more RAM,
the time between GC cycles goes up again (as the heap size is larger
with more long-lived memory chunks, but the frequency of creating
short-lived heap objects does not change). On a quite busy large
Prometheus server, the timing changed from one GC run every 20s to one
GC run every 12s.
In the former case (just changing GOGC, leave everything else as it
is), the CPU usage increases by about 10% (on a mid-size referenc
server from 8.1 to 8.9). If settings are adjusted, the CPU
consumptions increases more drastically (from 8 cores to 13 cores on a
large reference server), despite GCs happening more rarely, presumably
because a 50% larger set of memory chunks is managed now. Having more
memory chunks is good in many regards, and most servers are running
out of memory long before they run out of CPU cycles, so the tradeoff
is overwhelmingly positive in most cases.
Power users can still set the GOGC environment variable as usual, as
the implementation in this commit honors an explicitly set variable.
2017-03-26 12:55:37 -07:00
|
|
|
"runtime/debug"
|
2015-06-15 03:36:32 -07:00
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
|
2015-06-23 09:04:04 -07:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
2016-05-05 04:46:51 -07:00
|
|
|
"github.com/prometheus/common/log"
|
|
|
|
"github.com/prometheus/common/version"
|
promql: Allow per-query contexts.
For Weaveworks' Frankenstein, we need to support multitenancy. In
Frankenstein, we initially solved this without modifying the promql
package at all: we constructed a new promql.Engine for every
query and injected a storage implementation into that engine which would
be primed to only collect data for a given user.
This is problematic to upstream, however. Prometheus assumes that there
is only one engine: the query concurrency gate is part of the engine,
and the engine contains one central cancellable context to shut down all
queries. Also, creating a new engine for every query seems like overkill.
Thus, we want to be able to pass per-query contexts into a single engine.
This change gets rid of the promql.Engine's built-in base context and
allows passing in a per-query context instead. Central cancellation of
all queries is still possible by deriving all passed-in contexts from
one central one, but this is now the responsibility of the caller. The
central query context is now created in main() and passed into the
relevant components (web handler / API, rule manager).
In a next step, the per-query context would have to be passed to the
storage implementation, so that the storage can implement multi-tenancy
or other features based on the contextual information.
2016-09-15 04:52:50 -07:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
|
2015-06-15 03:36:32 -07:00
|
|
|
"github.com/prometheus/prometheus/config"
|
2016-03-01 03:37:22 -08:00
|
|
|
"github.com/prometheus/prometheus/notifier"
|
2015-06-15 03:36:32 -07:00
|
|
|
"github.com/prometheus/prometheus/promql"
|
|
|
|
"github.com/prometheus/prometheus/retrieval"
|
|
|
|
"github.com/prometheus/prometheus/rules"
|
|
|
|
"github.com/prometheus/prometheus/storage"
|
2017-03-10 03:53:27 -08:00
|
|
|
"github.com/prometheus/prometheus/storage/fanin"
|
2015-06-15 03:36:32 -07:00
|
|
|
"github.com/prometheus/prometheus/storage/local"
|
|
|
|
"github.com/prometheus/prometheus/storage/remote"
|
|
|
|
"github.com/prometheus/prometheus/web"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
os.Exit(Main())
|
|
|
|
}
|
|
|
|
|
main.go: Set GOGC to 40 by default
Rationale: The default value for GOGC is 100, i.e. a garbage collected
is initialized once as many heap space has been allocated as was in
use after the last GC was done. This ratio doesn't make a lot of sense
in Prometheus, as typically about 60% of the heap is allocated for
long-lived memory chunks (most of which are around for many hours if
not days). Thus, short-lived heap objects are accumulated for quite
some time until they finally match the large amount of memory used by
bulk memory chunks and a gigantic GC cyle is invoked. With GOGC=40, we
are essentially reinstating "normal" GC behavior by acknowledging that
about 60% of the heap are used for long-term bulk storage.
The median Prometheus production server at SoundCloud runs a GC cycle
every 90 seconds. With GOGC=40, a GC cycle is run every 35 seconds
(which is still not very often). However, the effective RAM usage is
now reduced by about 30%. If settings are updated to utilize more RAM,
the time between GC cycles goes up again (as the heap size is larger
with more long-lived memory chunks, but the frequency of creating
short-lived heap objects does not change). On a quite busy large
Prometheus server, the timing changed from one GC run every 20s to one
GC run every 12s.
In the former case (just changing GOGC, leave everything else as it
is), the CPU usage increases by about 10% (on a mid-size referenc
server from 8.1 to 8.9). If settings are adjusted, the CPU
consumptions increases more drastically (from 8 cores to 13 cores on a
large reference server), despite GCs happening more rarely, presumably
because a 50% larger set of memory chunks is managed now. Having more
memory chunks is good in many regards, and most servers are running
out of memory long before they run out of CPU cycles, so the tradeoff
is overwhelmingly positive in most cases.
Power users can still set the GOGC environment variable as usual, as
the implementation in this commit honors an explicitly set variable.
2017-03-26 12:55:37 -07:00
|
|
|
// defaultGCPercent is the value used to to call SetGCPercent if the GOGC
|
|
|
|
// environment variable is not set or empty. The value here is intended to hit
|
|
|
|
// the sweet spot between memory utilization and GC effort. It is lower than the
|
|
|
|
// usual default of 100 as a lot of the heap in Prometheus is used to cache
|
|
|
|
// memory chunks, which have a lifetime of hours if not days or weeks.
|
|
|
|
const defaultGCPercent = 40
|
|
|
|
|
2015-09-01 10:18:39 -07:00
|
|
|
var (
|
|
|
|
configSuccess = prometheus.NewGauge(prometheus.GaugeOpts{
|
|
|
|
Namespace: "prometheus",
|
|
|
|
Name: "config_last_reload_successful",
|
|
|
|
Help: "Whether the last configuration reload attempt was successful.",
|
|
|
|
})
|
|
|
|
configSuccessTime = prometheus.NewGauge(prometheus.GaugeOpts{
|
|
|
|
Namespace: "prometheus",
|
|
|
|
Name: "config_last_reload_success_timestamp_seconds",
|
|
|
|
Help: "Timestamp of the last successful configuration reload.",
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
2016-05-05 04:46:51 -07:00
|
|
|
func init() {
|
|
|
|
prometheus.MustRegister(version.NewCollector("prometheus"))
|
|
|
|
}
|
|
|
|
|
2015-08-24 06:07:27 -07:00
|
|
|
// Main manages the startup and shutdown lifecycle of the entire Prometheus server.
|
2015-06-15 03:36:32 -07:00
|
|
|
func Main() int {
|
|
|
|
if err := parse(os.Args[1:]); err != nil {
|
2016-02-19 03:18:19 -08:00
|
|
|
log.Error(err)
|
2015-06-15 03:36:32 -07:00
|
|
|
return 2
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.printVersion {
|
2016-05-05 04:46:51 -07:00
|
|
|
fmt.Fprintln(os.Stdout, version.Print("prometheus"))
|
2015-06-15 03:36:32 -07:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
main.go: Set GOGC to 40 by default
Rationale: The default value for GOGC is 100, i.e. a garbage collected
is initialized once as many heap space has been allocated as was in
use after the last GC was done. This ratio doesn't make a lot of sense
in Prometheus, as typically about 60% of the heap is allocated for
long-lived memory chunks (most of which are around for many hours if
not days). Thus, short-lived heap objects are accumulated for quite
some time until they finally match the large amount of memory used by
bulk memory chunks and a gigantic GC cyle is invoked. With GOGC=40, we
are essentially reinstating "normal" GC behavior by acknowledging that
about 60% of the heap are used for long-term bulk storage.
The median Prometheus production server at SoundCloud runs a GC cycle
every 90 seconds. With GOGC=40, a GC cycle is run every 35 seconds
(which is still not very often). However, the effective RAM usage is
now reduced by about 30%. If settings are updated to utilize more RAM,
the time between GC cycles goes up again (as the heap size is larger
with more long-lived memory chunks, but the frequency of creating
short-lived heap objects does not change). On a quite busy large
Prometheus server, the timing changed from one GC run every 20s to one
GC run every 12s.
In the former case (just changing GOGC, leave everything else as it
is), the CPU usage increases by about 10% (on a mid-size referenc
server from 8.1 to 8.9). If settings are adjusted, the CPU
consumptions increases more drastically (from 8 cores to 13 cores on a
large reference server), despite GCs happening more rarely, presumably
because a 50% larger set of memory chunks is managed now. Having more
memory chunks is good in many regards, and most servers are running
out of memory long before they run out of CPU cycles, so the tradeoff
is overwhelmingly positive in most cases.
Power users can still set the GOGC environment variable as usual, as
the implementation in this commit honors an explicitly set variable.
2017-03-26 12:55:37 -07:00
|
|
|
if os.Getenv("GOGC") == "" {
|
|
|
|
debug.SetGCPercent(defaultGCPercent)
|
|
|
|
}
|
|
|
|
|
2016-05-05 04:46:51 -07:00
|
|
|
log.Infoln("Starting prometheus", version.Info())
|
|
|
|
log.Infoln("Build context", version.BuildContext())
|
|
|
|
|
2015-06-15 03:36:32 -07:00
|
|
|
var (
|
2016-09-09 17:28:19 -07:00
|
|
|
sampleAppender = storage.Fanout{}
|
|
|
|
reloadables []Reloadable
|
2015-06-15 03:36:32 -07:00
|
|
|
)
|
2016-08-29 09:48:20 -07:00
|
|
|
|
2016-09-09 17:28:19 -07:00
|
|
|
var localStorage local.Storage
|
|
|
|
switch cfg.localStorageEngine {
|
|
|
|
case "persisted":
|
|
|
|
localStorage = local.NewMemorySeriesStorage(&cfg.storage)
|
|
|
|
sampleAppender = storage.Fanout{localStorage}
|
|
|
|
case "none":
|
|
|
|
localStorage = &local.NoopStorage{}
|
|
|
|
default:
|
|
|
|
log.Errorf("Invalid local storage engine %q", cfg.localStorageEngine)
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2017-03-20 05:15:28 -07:00
|
|
|
remoteAppender := &remote.Writer{}
|
|
|
|
sampleAppender = append(sampleAppender, remoteAppender)
|
2017-03-10 03:53:27 -08:00
|
|
|
remoteReader := &remote.Reader{}
|
2017-03-20 05:15:28 -07:00
|
|
|
reloadables = append(reloadables, remoteAppender, remoteReader)
|
2017-03-10 03:53:27 -08:00
|
|
|
|
|
|
|
queryable := fanin.Queryable{
|
|
|
|
Local: localStorage,
|
|
|
|
Remote: remoteReader,
|
|
|
|
}
|
2016-09-19 13:47:51 -07:00
|
|
|
|
2015-06-25 16:32:44 -07:00
|
|
|
var (
|
2016-09-15 15:58:06 -07:00
|
|
|
notifier = notifier.New(&cfg.notifier)
|
|
|
|
targetManager = retrieval.NewTargetManager(sampleAppender)
|
2017-03-10 03:53:27 -08:00
|
|
|
queryEngine = promql.NewEngine(queryable, &cfg.queryEngine)
|
2016-09-15 15:58:06 -07:00
|
|
|
ctx, cancelCtx = context.WithCancel(context.Background())
|
2015-06-25 16:32:44 -07:00
|
|
|
)
|
|
|
|
|
2015-06-15 03:36:32 -07:00
|
|
|
ruleManager := rules.NewManager(&rules.ManagerOptions{
|
2016-03-01 03:37:22 -08:00
|
|
|
SampleAppender: sampleAppender,
|
|
|
|
Notifier: notifier,
|
|
|
|
QueryEngine: queryEngine,
|
2017-03-20 16:46:31 -07:00
|
|
|
Context: fanin.WithLocalOnly(ctx),
|
2016-03-01 03:37:22 -08:00
|
|
|
ExternalURL: cfg.web.ExternalURL,
|
2015-06-15 03:36:32 -07:00
|
|
|
})
|
|
|
|
|
2016-09-15 15:58:06 -07:00
|
|
|
cfg.web.Context = ctx
|
|
|
|
cfg.web.Storage = localStorage
|
|
|
|
cfg.web.QueryEngine = queryEngine
|
|
|
|
cfg.web.TargetManager = targetManager
|
|
|
|
cfg.web.RuleManager = ruleManager
|
2016-11-23 09:23:09 -08:00
|
|
|
cfg.web.Notifier = notifier
|
2015-06-15 03:36:32 -07:00
|
|
|
|
2016-09-15 15:58:06 -07:00
|
|
|
cfg.web.Version = &web.PrometheusVersion{
|
2016-05-05 04:46:51 -07:00
|
|
|
Version: version.Version,
|
|
|
|
Revision: version.Revision,
|
|
|
|
Branch: version.Branch,
|
|
|
|
BuildUser: version.BuildUser,
|
|
|
|
BuildDate: version.BuildDate,
|
|
|
|
GoVersion: version.GoVersion,
|
|
|
|
}
|
|
|
|
|
2016-09-15 15:58:06 -07:00
|
|
|
cfg.web.Flags = map[string]string{}
|
|
|
|
cfg.fs.VisitAll(func(f *flag.Flag) {
|
|
|
|
cfg.web.Flags[f.Name] = f.Value.String()
|
|
|
|
})
|
|
|
|
|
|
|
|
webHandler := web.New(&cfg.web)
|
2015-06-15 03:36:32 -07:00
|
|
|
|
2016-05-13 08:59:59 -07:00
|
|
|
reloadables = append(reloadables, targetManager, ruleManager, webHandler, notifier)
|
2015-09-01 09:47:48 -07:00
|
|
|
|
2016-07-11 07:24:54 -07:00
|
|
|
if err := reloadConfig(cfg.configFile, reloadables...); err != nil {
|
|
|
|
log.Errorf("Error loading config: %s", err)
|
2015-06-23 09:04:04 -07:00
|
|
|
return 1
|
2015-06-15 03:36:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for reload or termination signals. Start the handler for SIGHUP as
|
|
|
|
// early as possible, but ignore it until we are ready to handle reloading
|
|
|
|
// our config.
|
|
|
|
hup := make(chan os.Signal)
|
|
|
|
hupReady := make(chan bool)
|
|
|
|
signal.Notify(hup, syscall.SIGHUP)
|
|
|
|
go func() {
|
|
|
|
<-hupReady
|
2015-08-11 00:08:17 -07:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-hup:
|
2016-07-11 07:24:54 -07:00
|
|
|
if err := reloadConfig(cfg.configFile, reloadables...); err != nil {
|
|
|
|
log.Errorf("Error reloading config: %s", err)
|
|
|
|
}
|
|
|
|
case rc := <-webHandler.Reload():
|
|
|
|
if err := reloadConfig(cfg.configFile, reloadables...); err != nil {
|
|
|
|
log.Errorf("Error reloading config: %s", err)
|
|
|
|
rc <- err
|
|
|
|
} else {
|
|
|
|
rc <- nil
|
|
|
|
}
|
2015-08-11 00:08:17 -07:00
|
|
|
}
|
2015-06-15 03:36:32 -07:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2016-01-18 07:47:31 -08:00
|
|
|
// Start all components. The order is NOT arbitrary.
|
|
|
|
|
2016-09-09 17:28:19 -07:00
|
|
|
if err := localStorage.Start(); err != nil {
|
2015-06-15 03:36:32 -07:00
|
|
|
log.Errorln("Error opening memory series storage:", err)
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
defer func() {
|
2016-09-09 17:28:19 -07:00
|
|
|
if err := localStorage.Stop(); err != nil {
|
2015-06-15 03:36:32 -07:00
|
|
|
log.Errorln("Error stopping storage:", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2017-03-20 05:15:28 -07:00
|
|
|
defer remoteAppender.Stop()
|
2016-09-19 13:47:51 -07:00
|
|
|
|
2015-06-23 09:04:04 -07:00
|
|
|
// The storage has to be fully initialized before registering.
|
2016-09-09 17:28:19 -07:00
|
|
|
if instrumentedStorage, ok := localStorage.(prometheus.Collector); ok {
|
|
|
|
prometheus.MustRegister(instrumentedStorage)
|
|
|
|
}
|
2015-09-01 10:18:39 -07:00
|
|
|
prometheus.MustRegister(configSuccess)
|
|
|
|
prometheus.MustRegister(configSuccessTime)
|
2015-06-15 03:36:32 -07:00
|
|
|
|
2016-09-21 13:59:25 -07:00
|
|
|
// The notifier is a dependency of the rule manager. It has to be
|
2016-01-18 07:47:31 -08:00
|
|
|
// started before and torn down afterwards.
|
2016-03-01 03:37:22 -08:00
|
|
|
go notifier.Run()
|
|
|
|
defer notifier.Stop()
|
2015-06-15 03:36:32 -07:00
|
|
|
|
2016-01-18 07:47:31 -08:00
|
|
|
go ruleManager.Run()
|
|
|
|
defer ruleManager.Stop()
|
|
|
|
|
2015-06-15 03:36:32 -07:00
|
|
|
go targetManager.Run()
|
|
|
|
defer targetManager.Stop()
|
|
|
|
|
2016-01-18 07:47:31 -08:00
|
|
|
// Shutting down the query engine before the rule manager will cause pending queries
|
|
|
|
// to be canceled and ensures a quick shutdown of the rule manager.
|
2016-09-15 15:58:06 -07:00
|
|
|
defer cancelCtx()
|
2015-06-15 03:36:32 -07:00
|
|
|
|
|
|
|
go webHandler.Run()
|
|
|
|
|
|
|
|
// Wait for reload or termination signals.
|
|
|
|
close(hupReady) // Unblock SIGHUP handler.
|
|
|
|
|
|
|
|
term := make(chan os.Signal)
|
|
|
|
signal.Notify(term, os.Interrupt, syscall.SIGTERM)
|
|
|
|
select {
|
|
|
|
case <-term:
|
|
|
|
log.Warn("Received SIGTERM, exiting gracefully...")
|
|
|
|
case <-webHandler.Quit():
|
|
|
|
log.Warn("Received termination request via web service, exiting gracefully...")
|
2015-08-20 09:23:57 -07:00
|
|
|
case err := <-webHandler.ListenError():
|
|
|
|
log.Errorln("Error starting web server, exiting gracefully:", err)
|
2015-06-15 03:36:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Info("See you next time!")
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reloadable things can change their internal state to match a new config
|
|
|
|
// and handle failure gracefully.
|
|
|
|
type Reloadable interface {
|
2016-07-11 07:24:54 -07:00
|
|
|
ApplyConfig(*config.Config) error
|
2015-06-15 03:36:32 -07:00
|
|
|
}
|
|
|
|
|
2016-07-11 07:24:54 -07:00
|
|
|
func reloadConfig(filename string, rls ...Reloadable) (err error) {
|
2015-06-15 03:36:32 -07:00
|
|
|
log.Infof("Loading configuration file %s", filename)
|
2015-09-01 10:18:39 -07:00
|
|
|
defer func() {
|
2016-07-11 07:24:54 -07:00
|
|
|
if err == nil {
|
2015-09-01 10:18:39 -07:00
|
|
|
configSuccess.Set(1)
|
|
|
|
configSuccessTime.Set(float64(time.Now().Unix()))
|
|
|
|
} else {
|
|
|
|
configSuccess.Set(0)
|
|
|
|
}
|
|
|
|
}()
|
2015-06-15 03:36:32 -07:00
|
|
|
|
2015-08-05 09:30:37 -07:00
|
|
|
conf, err := config.LoadFile(filename)
|
2015-06-15 03:36:32 -07:00
|
|
|
if err != nil {
|
2016-07-11 07:24:54 -07:00
|
|
|
return fmt.Errorf("couldn't load configuration (-config.file=%s): %v", filename, err)
|
2015-06-15 03:36:32 -07:00
|
|
|
}
|
|
|
|
|
2016-11-23 08:03:22 -08:00
|
|
|
// Add AlertmanagerConfigs for legacy Alertmanager URL flags.
|
|
|
|
for us := range cfg.alertmanagerURLs {
|
2017-01-16 08:39:20 -08:00
|
|
|
acfg, err := parseAlertmanagerURLToConfig(us)
|
2016-11-23 08:03:22 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-11-24 06:17:50 -08:00
|
|
|
conf.AlertingConfig.AlertmanagerConfigs = append(conf.AlertingConfig.AlertmanagerConfigs, acfg)
|
2016-11-23 08:03:22 -08:00
|
|
|
}
|
|
|
|
|
2016-08-11 18:23:18 -07:00
|
|
|
failed := false
|
2015-06-15 03:36:32 -07:00
|
|
|
for _, rl := range rls {
|
2016-08-11 18:23:18 -07:00
|
|
|
if err := rl.ApplyConfig(conf); err != nil {
|
|
|
|
log.Error("Failed to apply configuration: ", err)
|
|
|
|
failed = true
|
2016-07-11 07:24:54 -07:00
|
|
|
}
|
2015-06-15 03:36:32 -07:00
|
|
|
}
|
2016-08-11 18:23:18 -07:00
|
|
|
if failed {
|
2016-09-14 20:23:28 -07:00
|
|
|
return fmt.Errorf("one or more errors occurred while applying the new configuration (-config.file=%s)", filename)
|
2016-08-11 18:23:18 -07:00
|
|
|
}
|
|
|
|
return nil
|
2015-06-15 03:36:32 -07:00
|
|
|
}
|