2018-02-05 16:05:59 -05:00
|
|
|
package daemon // import "github.com/docker/docker/daemon"
|
2016-07-20 19:11:28 -04:00
|
|
|
|
2017-02-09 21:57:35 -05:00
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
|
2018-05-30 15:00:42 -04:00
|
|
|
"github.com/docker/docker/errdefs"
|
2017-04-13 21:56:50 -04:00
|
|
|
"github.com/docker/docker/pkg/plugingetter"
|
2018-04-24 21:45:00 -04:00
|
|
|
"github.com/docker/docker/pkg/plugins"
|
2018-05-19 07:38:54 -04:00
|
|
|
"github.com/docker/go-metrics"
|
2017-04-13 21:56:50 -04:00
|
|
|
"github.com/pkg/errors"
|
2017-02-09 21:57:35 -05:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
2017-07-26 17:42:13 -04:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-02-09 21:57:35 -05:00
|
|
|
)
|
2016-07-20 19:11:28 -04:00
|
|
|
|
2017-04-13 21:56:50 -04:00
|
|
|
const metricsPluginType = "MetricsCollector"
|
|
|
|
|
2016-07-20 19:11:28 -04:00
|
|
|
var (
|
|
|
|
containerActions metrics.LabeledTimer
|
|
|
|
networkActions metrics.LabeledTimer
|
2017-04-24 07:32:01 -04:00
|
|
|
engineInfo metrics.LabeledGauge
|
2016-07-20 19:11:28 -04:00
|
|
|
engineCpus metrics.Gauge
|
|
|
|
engineMemory metrics.Gauge
|
|
|
|
healthChecksCounter metrics.Counter
|
|
|
|
healthChecksFailedCounter metrics.Counter
|
2017-02-09 21:57:35 -05:00
|
|
|
|
|
|
|
stateCtr *stateCounter
|
2016-07-20 19:11:28 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
2017-02-09 21:57:35 -05:00
|
|
|
|
2016-07-20 19:11:28 -04:00
|
|
|
networkActions = ns.NewLabeledTimer("network_actions", "The number of seconds it takes to process each network action", "action")
|
2017-04-24 07:32:01 -04:00
|
|
|
engineInfo = ns.NewLabeledGauge("engine", "The information related to the engine and the OS it is running on", metrics.Unit("info"),
|
2016-07-20 19:11:28 -04:00
|
|
|
"version",
|
|
|
|
"commit",
|
|
|
|
"architecture",
|
2017-04-24 07:32:01 -04:00
|
|
|
"graphdriver",
|
|
|
|
"kernel", "os",
|
|
|
|
"os_type",
|
|
|
|
"daemon_id", // ID is a randomly generated unique identifier (e.g. UUID4)
|
2016-07-20 19:11:28 -04:00
|
|
|
)
|
|
|
|
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")
|
2017-02-09 21:57:35 -05:00
|
|
|
|
|
|
|
stateCtr = newStateCounter(ns.NewDesc("container_states", "The count of containers in various states", metrics.Unit("containers"), "state"))
|
|
|
|
ns.Add(stateCtr)
|
|
|
|
|
2016-07-20 19:11:28 -04:00
|
|
|
metrics.Register(ns)
|
|
|
|
}
|
2017-02-09 21:57:35 -05:00
|
|
|
|
|
|
|
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")
|
|
|
|
}
|
2017-04-13 21:56:50 -04:00
|
|
|
|
|
|
|
func (d *Daemon) cleanupMetricsPlugins() {
|
|
|
|
ls := d.PluginStore.GetAllManagedPluginsByCap(metricsPluginType)
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(len(ls))
|
|
|
|
|
2018-01-16 17:51:36 -05:00
|
|
|
for _, plugin := range ls {
|
|
|
|
p := plugin
|
2017-04-13 21:56:50 -04:00
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
2018-04-24 21:45:00 -04:00
|
|
|
|
|
|
|
adapter, err := makePluginAdapter(p)
|
|
|
|
if err != nil {
|
|
|
|
logrus.WithError(err).WithField("plugin", p.Name()).Error("Error creating metrics plugin adapater")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := adapter.StopMetrics(); err != nil {
|
|
|
|
logrus.WithError(err).WithField("plugin", p.Name()).Error("Error stopping plugin metrics collection")
|
|
|
|
}
|
2017-04-13 21:56:50 -04:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
if d.metricsPluginListener != nil {
|
|
|
|
d.metricsPluginListener.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-24 21:45:00 -04:00
|
|
|
type metricsPlugin interface {
|
|
|
|
StartMetrics() error
|
|
|
|
StopMetrics() error
|
|
|
|
}
|
|
|
|
|
2018-05-30 15:00:42 -04:00
|
|
|
func makePluginAdapter(p plugingetter.CompatPlugin) (metricsPlugin, error) { // nolint: interfacer
|
|
|
|
if pc, ok := p.(plugingetter.PluginWithV1Client); ok {
|
|
|
|
return &metricsPluginAdapter{pc.Client(), p.Name()}, nil
|
|
|
|
}
|
|
|
|
|
2018-04-24 21:45:00 -04:00
|
|
|
pa, ok := p.(plugingetter.PluginAddr)
|
|
|
|
if !ok {
|
2018-05-30 15:00:42 -04:00
|
|
|
return nil, errdefs.System(errors.Errorf("got unknown plugin type %T", p))
|
2018-04-24 21:45:00 -04:00
|
|
|
}
|
2018-05-30 15:00:42 -04:00
|
|
|
|
2018-04-24 21:45:00 -04:00
|
|
|
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 {
|
2017-04-13 21:56:50 -04:00
|
|
|
type metricsPluginResponse struct {
|
|
|
|
Err string
|
|
|
|
}
|
|
|
|
var res metricsPluginResponse
|
2018-04-24 21:45:00 -04:00
|
|
|
if err := a.c.Call(metricsPluginType+".StartMetrics", nil, &res); err != nil {
|
2017-04-13 21:56:50 -04:00
|
|
|
return errors.Wrap(err, "could not start metrics plugin")
|
|
|
|
}
|
|
|
|
if res.Err != "" {
|
|
|
|
return errors.New(res.Err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-24 21:45:00 -04:00
|
|
|
func (a *metricsPluginAdapter) StopMetrics() error {
|
|
|
|
if err := a.c.Call(metricsPluginType+".StopMetrics", nil, nil); err != nil {
|
|
|
|
return errors.Wrap(err, "error stopping metrics collector")
|
2017-04-13 21:56:50 -04:00
|
|
|
}
|
2018-04-24 21:45:00 -04:00
|
|
|
return nil
|
2017-04-13 21:56:50 -04:00
|
|
|
}
|