daemon/metrics.go
4f0d95fa
 package daemon // import "github.com/docker/docker/daemon"
3343d234
 
e4c03623
 import (
 	"sync"
 
7c77df8a
 	"github.com/docker/docker/errdefs"
0e8e8f0f
 	"github.com/docker/docker/pkg/plugingetter"
f51a96c0
 	"github.com/docker/docker/pkg/plugins"
f23c00d8
 	"github.com/docker/go-metrics"
0e8e8f0f
 	"github.com/pkg/errors"
e4c03623
 	"github.com/prometheus/client_golang/prometheus"
1009e6a4
 	"github.com/sirupsen/logrus"
e4c03623
 )
3343d234
 
0e8e8f0f
 const metricsPluginType = "MetricsCollector"
 
3343d234
 var (
 	containerActions          metrics.LabeledTimer
 	networkActions            metrics.LabeledTimer
a28b173a
 	engineInfo                metrics.LabeledGauge
3343d234
 	engineCpus                metrics.Gauge
 	engineMemory              metrics.Gauge
 	healthChecksCounter       metrics.Counter
 	healthChecksFailedCounter metrics.Counter
e4c03623
 
 	stateCtr *stateCounter
3343d234
 )
 
 func init() {
 	ns := metrics.NewNamespace("engine", "daemon", nil)
 	containerActions = ns.NewLabeledTimer("container_actions", "The number of seconds it takes to process each container action", "action")
 	for _, a := range []string{
 		"start",
 		"changes",
 		"commit",
 		"create",
 		"delete",
 	} {
 		containerActions.WithValues(a).Update(0)
 	}
e4c03623
 
3343d234
 	networkActions = ns.NewLabeledTimer("network_actions", "The number of seconds it takes to process each network action", "action")
a28b173a
 	engineInfo = ns.NewLabeledGauge("engine", "The information related to the engine and the OS it is running on", metrics.Unit("info"),
3343d234
 		"version",
 		"commit",
 		"architecture",
a28b173a
 		"graphdriver",
 		"kernel", "os",
 		"os_type",
 		"daemon_id", // ID is a randomly generated unique identifier (e.g. UUID4)
3343d234
 	)
 	engineCpus = ns.NewGauge("engine_cpus", "The number of cpus that the host system of the engine has", metrics.Unit("cpus"))
 	engineMemory = ns.NewGauge("engine_memory", "The number of bytes of memory that the host system of the engine has", metrics.Bytes)
 	healthChecksCounter = ns.NewCounter("health_checks", "The total number of health checks")
 	healthChecksFailedCounter = ns.NewCounter("health_checks_failed", "The total number of failed health checks")
e4c03623
 
 	stateCtr = newStateCounter(ns.NewDesc("container_states", "The count of containers in various states", metrics.Unit("containers"), "state"))
 	ns.Add(stateCtr)
 
3343d234
 	metrics.Register(ns)
 }
e4c03623
 
 type stateCounter struct {
 	mu     sync.Mutex
 	states map[string]string
 	desc   *prometheus.Desc
 }
 
 func newStateCounter(desc *prometheus.Desc) *stateCounter {
 	return &stateCounter{
 		states: make(map[string]string),
 		desc:   desc,
 	}
 }
 
 func (ctr *stateCounter) get() (running int, paused int, stopped int) {
 	ctr.mu.Lock()
 	defer ctr.mu.Unlock()
 
 	states := map[string]int{
 		"running": 0,
 		"paused":  0,
 		"stopped": 0,
 	}
 	for _, state := range ctr.states {
 		states[state]++
 	}
 	return states["running"], states["paused"], states["stopped"]
 }
 
 func (ctr *stateCounter) set(id, label string) {
 	ctr.mu.Lock()
 	ctr.states[id] = label
 	ctr.mu.Unlock()
 }
 
 func (ctr *stateCounter) del(id string) {
 	ctr.mu.Lock()
 	delete(ctr.states, id)
 	ctr.mu.Unlock()
 }
 
 func (ctr *stateCounter) Describe(ch chan<- *prometheus.Desc) {
 	ch <- ctr.desc
 }
 
 func (ctr *stateCounter) Collect(ch chan<- prometheus.Metric) {
 	running, paused, stopped := ctr.get()
 	ch <- prometheus.MustNewConstMetric(ctr.desc, prometheus.GaugeValue, float64(running), "running")
 	ch <- prometheus.MustNewConstMetric(ctr.desc, prometheus.GaugeValue, float64(paused), "paused")
 	ch <- prometheus.MustNewConstMetric(ctr.desc, prometheus.GaugeValue, float64(stopped), "stopped")
 }
0e8e8f0f
 
 func (d *Daemon) cleanupMetricsPlugins() {
 	ls := d.PluginStore.GetAllManagedPluginsByCap(metricsPluginType)
 	var wg sync.WaitGroup
 	wg.Add(len(ls))
 
9db2c624
 	for _, plugin := range ls {
 		p := plugin
0e8e8f0f
 		go func() {
 			defer wg.Done()
f51a96c0
 
 			adapter, err := makePluginAdapter(p)
 			if err != nil {
d3e155d9
 				logrus.WithError(err).WithField("plugin", p.Name()).Error("Error creating metrics plugin adapter")
f51a96c0
 				return
 			}
 			if err := adapter.StopMetrics(); err != nil {
 				logrus.WithError(err).WithField("plugin", p.Name()).Error("Error stopping plugin metrics collection")
 			}
0e8e8f0f
 		}()
 	}
 	wg.Wait()
 
 	if d.metricsPluginListener != nil {
 		d.metricsPluginListener.Close()
 	}
 }
 
f51a96c0
 type metricsPlugin interface {
 	StartMetrics() error
 	StopMetrics() error
 }
 
d283c7fa
 func makePluginAdapter(p plugingetter.CompatPlugin) (metricsPlugin, error) {
7c77df8a
 	if pc, ok := p.(plugingetter.PluginWithV1Client); ok {
 		return &metricsPluginAdapter{pc.Client(), p.Name()}, nil
 	}
 
f51a96c0
 	pa, ok := p.(plugingetter.PluginAddr)
 	if !ok {
7c77df8a
 		return nil, errdefs.System(errors.Errorf("got unknown plugin type %T", p))
f51a96c0
 	}
7c77df8a
 
f51a96c0
 	if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 {
 		return nil, errors.Errorf("plugin protocol not supported: %s", pa.Protocol())
 	}
 
 	addr := pa.Addr()
 	client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout())
 	if err != nil {
 		return nil, errors.Wrap(err, "error creating metrics plugin client")
 	}
 	return &metricsPluginAdapter{client, p.Name()}, nil
 }
 
 type metricsPluginAdapter struct {
 	c    *plugins.Client
 	name string
 }
 
 func (a *metricsPluginAdapter) StartMetrics() error {
0e8e8f0f
 	type metricsPluginResponse struct {
 		Err string
 	}
 	var res metricsPluginResponse
f51a96c0
 	if err := a.c.Call(metricsPluginType+".StartMetrics", nil, &res); err != nil {
0e8e8f0f
 		return errors.Wrap(err, "could not start metrics plugin")
 	}
 	if res.Err != "" {
 		return errors.New(res.Err)
 	}
 	return nil
 }
 
f51a96c0
 func (a *metricsPluginAdapter) StopMetrics() error {
 	if err := a.c.Call(metricsPluginType+".StopMetrics", nil, nil); err != nil {
 		return errors.Wrap(err, "error stopping metrics collector")
0e8e8f0f
 	}
f51a96c0
 	return nil
0e8e8f0f
 }