diff --git a/embed/config.go b/embed/config.go index d414789baae..090a5ad0fed 100644 --- a/embed/config.go +++ b/embed/config.go @@ -171,6 +171,8 @@ type Config struct { LogPkgLevels string `json:"log-package-levels"` EnablePprof bool `json:"enable-pprof"` Metrics string `json:"metrics"` + ListenMetricsUrls []url.URL + ListenMetricsUrlsJSON string `json:"listen-metrics-urls"` // ForceNewCluster starts a new cluster even if previously started; unsafe. ForceNewCluster bool `json:"force-new-cluster"` @@ -313,6 +315,14 @@ func (cfg *configYAML) configFromFile(path string) error { cfg.ACUrls = []url.URL(u) } + if cfg.ListenMetricsUrlsJSON != "" { + u, err := types.NewURLs(strings.Split(cfg.ListenMetricsUrlsJSON, ",")) + if err != nil { + plog.Fatalf("unexpected error setting up listen-metrics-urls: %v", err) + } + cfg.ListenMetricsUrls = []url.URL(u) + } + // If a discovery flag is set, clear default initial cluster set by InitialClusterFromName if (cfg.Durl != "" || cfg.DNSCluster != "") && cfg.InitialCluster == defaultInitialCluster { cfg.InitialCluster = "" @@ -362,6 +372,9 @@ func (cfg *Config) Validate() error { if err := checkBindURLs(cfg.LCUrls); err != nil { return err } + if err := checkBindURLs(cfg.ListenMetricsUrls); err != nil { + return err + } // Check if conflicting flags are passed. nSet := 0 diff --git a/embed/etcd.go b/embed/etcd.go index 9ae55c13467..38ffbe4eafd 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -22,6 +22,7 @@ import ( defaultLog "log" "net" "net/http" + "net/url" "sync" "time" @@ -40,6 +41,7 @@ import ( "github.com/coreos/pkg/capnslog" "google.golang.org/grpc" "google.golang.org/grpc/keepalive" + "github.com/prometheus/client_golang/prometheus" ) var plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "embed") @@ -64,6 +66,7 @@ type Etcd struct { Clients []net.Listener // a map of contexts for the servers that serves client requests. sctxs map[string]*serveCtx + metricsListeners []net.Listener Server *etcdserver.EtcdServer @@ -207,6 +210,9 @@ func (e *Etcd) Close() { e.Clients[i].Close() } } + for i := range e.metricsListeners { + e.metricsListeners[i].Close() + } // close rafthttp transports if e.Server != nil { @@ -481,6 +487,25 @@ func (e *Etcd) serveClients() (err error) { e.errHandler(s.serve(e.Server, ctlscfg, h, e.errHandler, gopts...)) }(sctx) } + + if len(e.cfg.ListenMetricsUrls) > 0 { + // TODO: maybe etcdhttp.MetricsPath or get the path from the user-provided URL + metricsMux := http.NewServeMux() + metricsMux.Handle("/metrics", prometheus.Handler()) + + for _, murl := range e.cfg.ListenMetricsUrls { + ml, err := transport.NewListener(murl.Host, murl.Scheme, &e.cfg.ClientTLSInfo) + if err != nil { + return err + } + e.metricsListeners = append(e.metricsListeners, ml) + go func(u url.URL, ln net.Listener) { + plog.Info("listening for metrics on ", u.String()) + e.errHandler(http.Serve(ln, metricsMux)) + }(murl, ml) + } + } + return nil } diff --git a/etcdmain/config.go b/etcdmain/config.go index c4a7d409dbd..2245f496208 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -20,12 +20,14 @@ import ( "flag" "fmt" "io/ioutil" + "net/url" "os" "runtime" "strings" "github.com/coreos/etcd/embed" "github.com/coreos/etcd/pkg/flags" + "github.com/coreos/etcd/pkg/types" "github.com/coreos/etcd/version" "github.com/ghodss/yaml" ) @@ -131,6 +133,7 @@ func newConfig() *config { fs.StringVar(&cfg.WalDir, "wal-dir", cfg.WalDir, "Path to the dedicated wal directory.") fs.Var(flags.NewURLsValue(embed.DefaultListenPeerURLs), "listen-peer-urls", "List of URLs to listen on for peer traffic.") fs.Var(flags.NewURLsValue(embed.DefaultListenClientURLs), "listen-client-urls", "List of URLs to listen on for client traffic.") + fs.StringVar(&cfg.ListenMetricsUrlsJSON, "listen-metrics-urls", "", "List of URLs to listen on for metrics.") fs.UintVar(&cfg.MaxSnapFiles, "max-snapshots", cfg.MaxSnapFiles, "Maximum number of snapshot files to retain (0 is unlimited).") fs.UintVar(&cfg.MaxWalFiles, "max-wals", cfg.MaxWalFiles, "Maximum number of wal files to retain (0 is unlimited).") fs.StringVar(&cfg.Name, "name", cfg.Name, "Human-readable name for this member.") @@ -264,6 +267,15 @@ func (cfg *config) configFromCmdLine() error { cfg.APUrls = flags.URLsFromFlag(cfg.FlagSet, "initial-advertise-peer-urls") cfg.LCUrls = flags.URLsFromFlag(cfg.FlagSet, "listen-client-urls") cfg.ACUrls = flags.URLsFromFlag(cfg.FlagSet, "advertise-client-urls") + + if len(cfg.ListenMetricsUrlsJSON) > 0 { + u, err := types.NewURLs(strings.Split(cfg.ListenMetricsUrlsJSON, ",")) + if err != nil { + plog.Fatalf("unexpected error setting up listen-metrics-urls: %v", err) + } + cfg.ListenMetricsUrls = []url.URL(u) + } + cfg.ClusterState = cfg.clusterState.String() cfg.Fallback = cfg.fallback.String() cfg.Proxy = cfg.proxy.String() diff --git a/etcdmain/etcd.go b/etcdmain/etcd.go index a70b2502c73..4c7975b2805 100644 --- a/etcdmain/etcd.go +++ b/etcdmain/etcd.go @@ -313,7 +313,7 @@ func startProxy(cfg *config) error { go func() { plog.Info("proxy: listening for client requests on ", host) mux := http.NewServeMux() - mux.Handle("/metrics", prometheus.Handler()) + mux.Handle("/metrics", prometheus.Handler()) // v2 proxy just uses the same port mux.Handle("/", ph) plog.Fatal(http.Serve(l, mux)) }() diff --git a/etcdmain/help.go b/etcdmain/help.go index b40231112b3..1e45a47f7c6 100644 --- a/etcdmain/help.go +++ b/etcdmain/help.go @@ -68,6 +68,8 @@ member flags: comma-separated whitelist of origins for CORS (cross-origin resource sharing). --quota-backend-bytes '0' raise alarms when backend size exceeds the given quota (0 defaults to low space quota). + --max-txn-ops '128' + maximum number of operations permitted in a transaction. --max-request-bytes '1572864' maximum client request size in bytes the server will accept. --grpc-keepalive-min-time '5s' @@ -174,7 +176,9 @@ profiling flags: --enable-pprof 'false' Enable runtime profiling data via HTTP server. Address is at client URL + "/debug/pprof/" --metrics 'basic' - Set level of detail for exported metrics, specify 'extensive' to include histogram metrics. + Set level of detail for exported metrics, specify 'extensive' to include histogram metrics. + --listen-metrics-urls '' + List of URLs to listen on for metrics. auth flags: --auth-token 'simple'