Implement client side display for stats
Signed-off-by: Michael Crosby <crosbymichael@gmail.com> Upstream-commit: 2640a10bca29c4a4199c906a26f658aac8a68dc2 Component: engine
This commit is contained in:
@@ -106,8 +106,10 @@ type Resources struct {
|
||||
|
||||
type ResourceStats struct {
|
||||
*libcontainer.ContainerStats
|
||||
Read time.Time `json:"read"`
|
||||
ClockTicks int `json:"clock_ticks"`
|
||||
Read time.Time `json:"read"`
|
||||
ClockTicks int `json:"clock_ticks"`
|
||||
MemoryLimit int64 `json:"memory_limit"`
|
||||
SystemUsage uint64 `json:"system_usage"`
|
||||
}
|
||||
|
||||
type Mount struct {
|
||||
|
||||
@@ -2,14 +2,21 @@ package execdrivers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/daemon/execdriver/lxc"
|
||||
"github.com/docker/docker/daemon/execdriver/native"
|
||||
"github.com/docker/docker/pkg/sysinfo"
|
||||
"path"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
)
|
||||
|
||||
func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
|
||||
meminfo, err := system.ReadMemInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "lxc":
|
||||
// we want to give the lxc driver the full docker root because it needs
|
||||
@@ -17,7 +24,7 @@ func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdrive
|
||||
// to be backwards compatible
|
||||
return lxc.NewDriver(root, initPath, sysInfo.AppArmor)
|
||||
case "native":
|
||||
return native.NewDriver(path.Join(root, "execdriver", "native"), initPath)
|
||||
return native.NewDriver(path.Join(root, "execdriver", "native"), initPath, meminfo.MemTotal/1000)
|
||||
}
|
||||
return nil, fmt.Errorf("unknown exec driver %s", name)
|
||||
}
|
||||
|
||||
@@ -42,23 +42,23 @@ type driver struct {
|
||||
root string
|
||||
initPath string
|
||||
activeContainers map[string]*activeContainer
|
||||
machineMemory int64
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewDriver(root, initPath string) (*driver, error) {
|
||||
func NewDriver(root, initPath string, machineMemory int64) (*driver, error) {
|
||||
if err := os.MkdirAll(root, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// native driver root is at docker_root/execdriver/native. Put apparmor at docker_root
|
||||
if err := apparmor.InstallDefaultProfile(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &driver{
|
||||
root: root,
|
||||
initPath: initPath,
|
||||
activeContainers: make(map[string]*activeContainer),
|
||||
machineMemory: machineMemory,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -281,6 +281,7 @@ func (d *driver) Clean(id string) error {
|
||||
}
|
||||
|
||||
func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) {
|
||||
c := d.activeContainers[id]
|
||||
state, err := libcontainer.GetState(filepath.Join(d.root, id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -290,10 +291,15 @@ func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
memoryLimit := c.container.Cgroups.Memory
|
||||
if memoryLimit == 0 {
|
||||
memoryLimit = d.machineMemory
|
||||
}
|
||||
return &execdriver.ResourceStats{
|
||||
ContainerStats: stats,
|
||||
ClockTicks: system.GetClockTicks(),
|
||||
Read: now,
|
||||
MemoryLimit: memoryLimit,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/engine"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/stats"
|
||||
)
|
||||
|
||||
func (daemon *Daemon) ContainerStart(job *engine.Job) engine.Status {
|
||||
@@ -80,15 +81,24 @@ func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.
|
||||
}
|
||||
|
||||
func (daemon *Daemon) ContainerStats(job *engine.Job) engine.Status {
|
||||
stats, err := daemon.SubscribeToContainerStats(job.Args[0])
|
||||
s, err := daemon.SubscribeToContainerStats(job.Args[0])
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
enc := json.NewEncoder(job.Stdout)
|
||||
for update := range stats {
|
||||
if err := enc.Encode(update); err != nil {
|
||||
for update := range s {
|
||||
ss := stats.ToStats(update.ContainerStats)
|
||||
ss.MemoryStats.Limit = uint64(update.MemoryLimit)
|
||||
ss.Read = update.Read
|
||||
ss.ClockTicks = update.ClockTicks
|
||||
ss.CpuStats.SystemUsage = update.SystemUsage
|
||||
if err := enc.Encode(ss); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
}
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
||||
func mapToAPIStats() {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -55,12 +60,18 @@ func (s *statsCollector) start() {
|
||||
log.Debugf("starting collection of container stats")
|
||||
s.m.Lock()
|
||||
for id, d := range s.containers {
|
||||
systemUsage, err := getSystemCpuUsage()
|
||||
if err != nil {
|
||||
log.Errorf("collecting system cpu usage for %s: %v", id, err)
|
||||
continue
|
||||
}
|
||||
stats, err := d.c.Stats()
|
||||
if err != nil {
|
||||
// TODO: @crosbymichael evict container depending on error
|
||||
log.Errorf("collecting stats for %s: %v", id, err)
|
||||
continue
|
||||
}
|
||||
stats.SystemUsage = systemUsage
|
||||
for _, sub := range s.containers[id].subs {
|
||||
sub <- stats
|
||||
}
|
||||
@@ -69,3 +80,36 @@ func (s *statsCollector) start() {
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// returns value in nanoseconds
|
||||
func getSystemCpuUsage() (uint64, error) {
|
||||
f, err := os.Open("/proc/stat")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
parts := strings.Fields(sc.Text())
|
||||
switch parts[0] {
|
||||
case "cpu":
|
||||
if len(parts) < 8 {
|
||||
return 0, fmt.Errorf("invalid number of cpu fields")
|
||||
}
|
||||
|
||||
var total uint64
|
||||
for _, i := range parts[1:8] {
|
||||
v, err := strconv.ParseUint(i, 10, 64)
|
||||
if err != nil {
|
||||
return 0.0, fmt.Errorf("Unable to convert value %s to int: %s", i, err)
|
||||
}
|
||||
total += v
|
||||
}
|
||||
return total * 1000000000, nil
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("invalid stat format")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user