4bafaa00aa
The containerd client is very chatty at the best of times. Because the libcontained API is stateless and references containers and processes by string ID for every method call, the implementation is essentially forced to use the containerd client in a way which amplifies the number of redundant RPCs invoked to perform any operation. The libcontainerd remote implementation has to reload the containerd container, task and/or process metadata for nearly every operation. This in turn amplifies the number of context switches between dockerd and containerd to perform any container operation or handle a containerd event, increasing the load on the system which could otherwise be allocated to workloads. Overhaul the libcontainerd interface to reduce the impedance mismatch with the containerd client so that the containerd client can be used more efficiently. Split the API out into container, task and process interfaces which the consumer is expected to retain so that libcontainerd can retain state---especially the analogous containerd client objects---without having to manage any state-store inside the libcontainerd client. Signed-off-by: Cory Snider <csnider@mirantis.com>
71 lines
2.5 KiB
Go
71 lines
2.5 KiB
Go
package daemon // import "github.com/docker/docker/daemon"
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
containertypes "github.com/docker/docker/api/types/container"
|
|
libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
|
|
units "github.com/docker/go-units"
|
|
)
|
|
|
|
// ContainerTop handles `docker top` client requests.
|
|
//
|
|
// Future considerations:
|
|
// - Windows users are far more familiar with CPU% total.
|
|
// Further, users on Windows rarely see user/kernel CPU stats split.
|
|
// The kernel returns everything in terms of 100ns. To obtain
|
|
// CPU%, we could do something like docker stats does which takes two
|
|
// samples, subtract the difference and do the maths. Unfortunately this
|
|
// would slow the stat call down and require two kernel calls. So instead,
|
|
// we do something similar to linux and display the CPU as combined HH:MM:SS.mmm.
|
|
// - Perhaps we could add an argument to display "raw" stats
|
|
// - "Memory" is an extremely overloaded term in Windows. Hence we do what
|
|
// task manager does and use the private working set as the memory counter.
|
|
// We could return more info for those who really understand how memory
|
|
// management works in Windows if we introduced a "raw" stats (above).
|
|
func (daemon *Daemon) ContainerTop(name string, psArgs string) (*containertypes.ContainerTopOKBody, error) {
|
|
// It's not at all an equivalent to linux 'ps' on Windows
|
|
if psArgs != "" {
|
|
return nil, errors.New("Windows does not support arguments to top")
|
|
}
|
|
|
|
container, err := daemon.GetContainer(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
task, err := func() (libcontainerdtypes.Task, error) {
|
|
container.Lock()
|
|
defer container.Unlock()
|
|
|
|
task, err := container.GetRunningTask()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if container.Restarting {
|
|
return nil, errContainerIsRestarting(container.ID)
|
|
}
|
|
return task, nil
|
|
}()
|
|
|
|
s, err := task.Summary(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
procList := &containertypes.ContainerTopOKBody{}
|
|
procList.Titles = []string{"Name", "PID", "CPU", "Private Working Set"}
|
|
|
|
for _, j := range s {
|
|
d := time.Duration((j.KernelTime_100Ns + j.UserTime_100Ns) * 100) // Combined time in nanoseconds
|
|
procList.Processes = append(procList.Processes, []string{
|
|
j.ImageName,
|
|
fmt.Sprint(j.ProcessID),
|
|
fmt.Sprintf("%02d:%02d:%02d.%03d", int(d.Hours()), int(d.Minutes())%60, int(d.Seconds())%60, int(d.Nanoseconds()/1000000)%1000),
|
|
units.HumanSize(float64(j.MemoryWorkingSetPrivateBytes))})
|
|
}
|
|
|
|
return procList, nil
|
|
}
|