Use runc events --stats for collecting stats

runc `events --stats` now has stable output so we don't need to bind to
libcontainer directly to get stats output for the containers.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2016-04-26 14:57:30 -07:00
parent c2df6e08cf
commit 9ed8422e8c
4 changed files with 121 additions and 63 deletions

View file

@ -17,8 +17,6 @@ import (
"github.com/docker/containerd/runtime" "github.com/docker/containerd/runtime"
"github.com/docker/containerd/specs" "github.com/docker/containerd/specs"
"github.com/docker/containerd/supervisor" "github.com/docker/containerd/supervisor"
"github.com/opencontainers/runc/libcontainer"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runc/libcontainer/system"
ocs "github.com/opencontainers/runtime-spec/specs-go" ocs "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -368,75 +366,68 @@ func convertToPb(st *runtime.Stat) *types.StatsResponse {
Timestamp: uint64(st.Timestamp.Unix()), Timestamp: uint64(st.Timestamp.Unix()),
CgroupStats: &types.CgroupStats{}, CgroupStats: &types.CgroupStats{},
} }
lcSt, ok := st.Data.(*libcontainer.Stats)
if !ok {
return pbSt
}
cpuSt := lcSt.CgroupStats.CpuStats
systemUsage, _ := getSystemCPUUsage() systemUsage, _ := getSystemCPUUsage()
pbSt.CgroupStats.CpuStats = &types.CpuStats{ pbSt.CgroupStats.CpuStats = &types.CpuStats{
CpuUsage: &types.CpuUsage{ CpuUsage: &types.CpuUsage{
TotalUsage: cpuSt.CpuUsage.TotalUsage, TotalUsage: st.Cpu.Usage.Total,
PercpuUsage: cpuSt.CpuUsage.PercpuUsage, PercpuUsage: st.Cpu.Usage.Percpu,
UsageInKernelmode: cpuSt.CpuUsage.UsageInKernelmode, UsageInKernelmode: st.Cpu.Usage.Kernel,
UsageInUsermode: cpuSt.CpuUsage.UsageInUsermode, UsageInUsermode: st.Cpu.Usage.User,
}, },
ThrottlingData: &types.ThrottlingData{ ThrottlingData: &types.ThrottlingData{
Periods: cpuSt.ThrottlingData.Periods, Periods: st.Cpu.Throttling.Periods,
ThrottledPeriods: cpuSt.ThrottlingData.ThrottledPeriods, ThrottledPeriods: st.Cpu.Throttling.ThrottledPeriods,
ThrottledTime: cpuSt.ThrottlingData.ThrottledTime, ThrottledTime: st.Cpu.Throttling.ThrottledTime,
}, },
SystemUsage: systemUsage, SystemUsage: systemUsage,
} }
memSt := lcSt.CgroupStats.MemoryStats
pbSt.CgroupStats.MemoryStats = &types.MemoryStats{ pbSt.CgroupStats.MemoryStats = &types.MemoryStats{
Cache: memSt.Cache, Cache: st.Memory.Cache,
Usage: &types.MemoryData{ Usage: &types.MemoryData{
Usage: memSt.Usage.Usage, Usage: st.Memory.Usage.Usage,
MaxUsage: memSt.Usage.MaxUsage, MaxUsage: st.Memory.Usage.Max,
Failcnt: memSt.Usage.Failcnt, Failcnt: st.Memory.Usage.Failcnt,
Limit: memSt.Usage.Limit, Limit: st.Memory.Usage.Limit,
}, },
SwapUsage: &types.MemoryData{ SwapUsage: &types.MemoryData{
Usage: memSt.SwapUsage.Usage, Usage: st.Memory.Swap.Usage,
MaxUsage: memSt.SwapUsage.MaxUsage, MaxUsage: st.Memory.Swap.Max,
Failcnt: memSt.SwapUsage.Failcnt, Failcnt: st.Memory.Swap.Failcnt,
Limit: memSt.SwapUsage.Limit, Limit: st.Memory.Swap.Limit,
}, },
KernelUsage: &types.MemoryData{ KernelUsage: &types.MemoryData{
Usage: memSt.KernelUsage.Usage, Usage: st.Memory.Kernel.Usage,
MaxUsage: memSt.KernelUsage.MaxUsage, MaxUsage: st.Memory.Kernel.Max,
Failcnt: memSt.KernelUsage.Failcnt, Failcnt: st.Memory.Kernel.Failcnt,
Limit: memSt.KernelUsage.Limit, Limit: st.Memory.Kernel.Limit,
}, },
} }
blkSt := lcSt.CgroupStats.BlkioStats
pbSt.CgroupStats.BlkioStats = &types.BlkioStats{ pbSt.CgroupStats.BlkioStats = &types.BlkioStats{
IoServiceBytesRecursive: convertBlkioEntryToPb(blkSt.IoServiceBytesRecursive), IoServiceBytesRecursive: convertBlkioEntryToPb(st.Blkio.IoServiceBytesRecursive),
IoServicedRecursive: convertBlkioEntryToPb(blkSt.IoServicedRecursive), IoServicedRecursive: convertBlkioEntryToPb(st.Blkio.IoServicedRecursive),
IoQueuedRecursive: convertBlkioEntryToPb(blkSt.IoQueuedRecursive), IoQueuedRecursive: convertBlkioEntryToPb(st.Blkio.IoQueuedRecursive),
IoServiceTimeRecursive: convertBlkioEntryToPb(blkSt.IoServiceTimeRecursive), IoServiceTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoServiceTimeRecursive),
IoWaitTimeRecursive: convertBlkioEntryToPb(blkSt.IoWaitTimeRecursive), IoWaitTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoWaitTimeRecursive),
IoMergedRecursive: convertBlkioEntryToPb(blkSt.IoMergedRecursive), IoMergedRecursive: convertBlkioEntryToPb(st.Blkio.IoMergedRecursive),
IoTimeRecursive: convertBlkioEntryToPb(blkSt.IoTimeRecursive), IoTimeRecursive: convertBlkioEntryToPb(st.Blkio.IoTimeRecursive),
SectorsRecursive: convertBlkioEntryToPb(blkSt.SectorsRecursive), SectorsRecursive: convertBlkioEntryToPb(st.Blkio.SectorsRecursive),
} }
pbSt.CgroupStats.HugetlbStats = make(map[string]*types.HugetlbStats) pbSt.CgroupStats.HugetlbStats = make(map[string]*types.HugetlbStats)
for k, st := range lcSt.CgroupStats.HugetlbStats { for k, st := range st.Hugetlb {
pbSt.CgroupStats.HugetlbStats[k] = &types.HugetlbStats{ pbSt.CgroupStats.HugetlbStats[k] = &types.HugetlbStats{
Usage: st.Usage, Usage: st.Usage,
MaxUsage: st.MaxUsage, MaxUsage: st.Max,
Failcnt: st.Failcnt, Failcnt: st.Failcnt,
} }
} }
pbSt.CgroupStats.PidsStats = &types.PidsStats{ pbSt.CgroupStats.PidsStats = &types.PidsStats{
Current: lcSt.CgroupStats.PidsStats.Current, Current: st.Pids.Current,
Limit: lcSt.CgroupStats.PidsStats.Limit, Limit: st.Pids.Limit,
} }
return pbSt return pbSt
} }
func convertBlkioEntryToPb(b []cgroups.BlkioStatEntry) []*types.BlkioStatsEntry { func convertBlkioEntryToPb(b []runtime.BlkioEntry) []*types.BlkioStatsEntry {
var pbEs []*types.BlkioStatsEntry var pbEs []*types.BlkioStatsEntry
for _, e := range b { for _, e := range b {
pbEs = append(pbEs, &types.BlkioStatsEntry{ pbEs = append(pbEs, &types.BlkioStatsEntry{

View file

@ -523,7 +523,7 @@ func hostIDFromMap(id uint32, mp []ocs.IDMapping) int {
func (c *container) Pids() ([]int, error) { func (c *container) Pids() ([]int, error) {
out, err := exec.Command(c.runtime, "ps", "--format=json", c.id).CombinedOutput() out, err := exec.Command(c.runtime, "ps", "--format=json", c.id).CombinedOutput()
if err != nil { if err != nil {
return nil, fmt.Errorf(string(out)) return nil, fmt.Errorf("%s", out)
} }
var pids []int var pids []int
if err := json.Unmarshal(out, &pids); err != nil { if err := json.Unmarshal(out, &pids); err != nil {
@ -533,19 +533,19 @@ func (c *container) Pids() ([]int, error) {
} }
func (c *container) Stats() (*Stat, error) { func (c *container) Stats() (*Stat, error) {
container, err := c.getLibctContainer()
if err != nil {
return nil, err
}
now := time.Now() now := time.Now()
stats, err := container.Stats() out, err := exec.Command(c.runtime, "events", "--stats", c.id).CombinedOutput()
if err != nil { if err != nil {
return nil, fmt.Errorf("%s", out)
}
s := struct {
Data *Stat `json:"data"`
}{}
if err := json.Unmarshal(out, &s); err != nil {
return nil, err return nil, err
} }
return &Stat{ s.Data.Timestamp = now
Timestamp: now, return s.Data, nil
Data: stats,
}, nil
} }
func (c *container) OOM() (OOM, error) { func (c *container) OOM() (OOM, error) {
@ -575,7 +575,7 @@ func (c *container) Status() (State, error) {
out, err := exec.Command(c.runtime, args...).CombinedOutput() out, err := exec.Command(c.runtime, args...).CombinedOutput()
if err != nil { if err != nil {
return "", fmt.Errorf(string(out)) return "", fmt.Errorf("%s", out)
} }
// We only require the runtime json output to have a top level Status field. // We only require the runtime json output to have a top level Status field.

View file

@ -97,13 +97,3 @@ type ProcessState struct {
PlatformProcessState PlatformProcessState
} }
type Stat struct {
// Timestamp is the time that the statistics where collected
Timestamp time.Time
// Data is the raw stats
// TODO: it is currently an interface because we don't know what type of exec drivers
// we will have or what the structure should look like at the moment os the containers
// can return what they want and we could marshal to json or whatever.
Data interface{}
}

77
runtime/stats.go Normal file
View file

@ -0,0 +1,77 @@
package runtime
import "time"
type Stat struct {
// Timestamp is the time that the statistics where collected
Timestamp time.Time
Cpu Cpu `json:"cpu"`
Memory Memory `json:"memory"`
Pids Pids `json:"pids"`
Blkio Blkio `json:"blkio"`
Hugetlb map[string]Hugetlb `json:"hugetlb"`
}
type Hugetlb struct {
Usage uint64 `json:"usage,omitempty"`
Max uint64 `json:"max,omitempty"`
Failcnt uint64 `json:"failcnt"`
}
type BlkioEntry struct {
Major uint64 `json:"major,omitempty"`
Minor uint64 `json:"minor,omitempty"`
Op string `json:"op,omitempty"`
Value uint64 `json:"value,omitempty"`
}
type Blkio struct {
IoServiceBytesRecursive []BlkioEntry `json:"ioServiceBytesRecursive,omitempty"`
IoServicedRecursive []BlkioEntry `json:"ioServicedRecursive,omitempty"`
IoQueuedRecursive []BlkioEntry `json:"ioQueueRecursive,omitempty"`
IoServiceTimeRecursive []BlkioEntry `json:"ioServiceTimeRecursive,omitempty"`
IoWaitTimeRecursive []BlkioEntry `json:"ioWaitTimeRecursive,omitempty"`
IoMergedRecursive []BlkioEntry `json:"ioMergedRecursive,omitempty"`
IoTimeRecursive []BlkioEntry `json:"ioTimeRecursive,omitempty"`
SectorsRecursive []BlkioEntry `json:"sectorsRecursive,omitempty"`
}
type Pids struct {
Current uint64 `json:"current,omitempty"`
Limit uint64 `json:"limit,omitempty"`
}
type Throttling struct {
Periods uint64 `json:"periods,omitempty"`
ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"`
ThrottledTime uint64 `json:"throttledTime,omitempty"`
}
type CpuUsage struct {
// Units: nanoseconds.
Total uint64 `json:"total,omitempty"`
Percpu []uint64 `json:"percpu,omitempty"`
Kernel uint64 `json:"kernel"`
User uint64 `json:"user"`
}
type Cpu struct {
Usage CpuUsage `json:"usage,omitempty"`
Throttling Throttling `json:"throttling,omitempty"`
}
type MemoryEntry struct {
Limit uint64 `json:"limit"`
Usage uint64 `json:"usage,omitempty"`
Max uint64 `json:"max,omitempty"`
Failcnt uint64 `json:"failcnt"`
}
type Memory struct {
Cache uint64 `json:"cache,omitempty"`
Usage MemoryEntry `json:"usage,omitempty"`
Swap MemoryEntry `json:"swap,omitempty"`
Kernel MemoryEntry `json:"kernel,omitempty"`
KernelTCP MemoryEntry `json:"kernelTCP,omitempty"`
Raw map[string]uint64 `json:"raw,omitempty"`
}