1. Update filesystem stats APIs.
2. Add BaseUsage to containers which includes only the rootfs usage, if available. Signed-off-by: Vishnu kannan <vishnuk@google.com>
This commit is contained in:
parent
c285b726ef
commit
6dfdd8eca1
@ -31,6 +31,7 @@ const (
|
|||||||
containersApi = "containers"
|
containersApi = "containers"
|
||||||
subcontainersApi = "subcontainers"
|
subcontainersApi = "subcontainers"
|
||||||
machineApi = "machine"
|
machineApi = "machine"
|
||||||
|
machineStatsApi = "machinestats"
|
||||||
dockerApi = "docker"
|
dockerApi = "docker"
|
||||||
summaryApi = "summary"
|
summaryApi = "summary"
|
||||||
statsApi = "stats"
|
statsApi = "stats"
|
||||||
@ -62,6 +63,7 @@ func getApiVersions() []ApiVersion {
|
|||||||
v1_2 := newVersion1_2(v1_1)
|
v1_2 := newVersion1_2(v1_1)
|
||||||
v1_3 := newVersion1_3(v1_2)
|
v1_3 := newVersion1_3(v1_2)
|
||||||
v2_0 := newVersion2_0()
|
v2_0 := newVersion2_0()
|
||||||
|
v2_1 := newVersion2_1()
|
||||||
|
|
||||||
return []ApiVersion{v1_0, v1_1, v1_2, v1_3, v2_0}
|
return []ApiVersion{v1_0, v1_1, v1_2, v1_3, v2_0}
|
||||||
|
|
||||||
@ -449,6 +451,56 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type version2_1 struct {
|
||||||
|
baseVersion *version2_0
|
||||||
|
}
|
||||||
|
|
||||||
|
func newVersion2_1(v *version2_0) *version2_1 {
|
||||||
|
return &version2_1{
|
||||||
|
baseVersion: v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *version2_1) Version() string {
|
||||||
|
return "v2.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *version2_1) SupportedRequestTypes() []string {
|
||||||
|
return self.baseVersion.SupportedRequestTypes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *version2_1) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
|
||||||
|
// Get the query request.
|
||||||
|
opt, err := getContainerInfoRequest(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch requestType {
|
||||||
|
case machineStatsApi:
|
||||||
|
glog.V(4).Infof("Api - MachineStats(%v)", request)
|
||||||
|
cont, err := m.GetRequestedContainersInfo("/", opt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeResult(convertMachineStats(cont), w)
|
||||||
|
case statsApi:
|
||||||
|
name := getContainerName(request)
|
||||||
|
glog.V(4).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt)
|
||||||
|
conts, err := m.GetRequestedContainersInfo(name, opt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
contStats := make(map[string][]v2.ContainerStats, 0)
|
||||||
|
for name, cont := range conts {
|
||||||
|
contStats[name] = convertStats(cont)
|
||||||
|
}
|
||||||
|
return writeResult(contStats, w)
|
||||||
|
default:
|
||||||
|
return self.baseVersion.HandleRequest(requestType, request, m, w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getRequestOptions(r *http.Request) (v2.RequestOptions, error) {
|
func getRequestOptions(r *http.Request) (v2.RequestOptions, error) {
|
||||||
supportedTypes := map[string]bool{
|
supportedTypes := map[string]bool{
|
||||||
v2.TypeName: true,
|
v2.TypeName: true,
|
||||||
|
@ -34,8 +34,10 @@ type realFsHandler struct {
|
|||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
lastUpdate time.Time
|
lastUpdate time.Time
|
||||||
usageBytes uint64
|
usageBytes uint64
|
||||||
|
baseUsageBytes uint64
|
||||||
period time.Duration
|
period time.Duration
|
||||||
storageDirs []string
|
writable string
|
||||||
|
other string
|
||||||
fsInfo fs.FsInfo
|
fsInfo fs.FsInfo
|
||||||
// Tells the container to stop.
|
// Tells the container to stop.
|
||||||
stopChan chan struct{}
|
stopChan chan struct{}
|
||||||
@ -45,12 +47,14 @@ const longDu = time.Second
|
|||||||
|
|
||||||
var _ fsHandler = &realFsHandler{}
|
var _ fsHandler = &realFsHandler{}
|
||||||
|
|
||||||
func newFsHandler(period time.Duration, storageDirs []string, fsInfo fs.FsInfo) fsHandler {
|
func newFsHandler(period time.Duration, rootfs, other string, fsInfo fs.FsInfo) fsHandler {
|
||||||
return &realFsHandler{
|
return &realFsHandler{
|
||||||
lastUpdate: time.Time{},
|
lastUpdate: time.Time{},
|
||||||
usageBytes: 0,
|
usageBytes: 0,
|
||||||
|
baseUsageBytes: 0,
|
||||||
period: period,
|
period: period,
|
||||||
storageDirs: storageDirs,
|
rootfs: rootfs,
|
||||||
|
other: other,
|
||||||
fsInfo: fsInfo,
|
fsInfo: fsInfo,
|
||||||
stopChan: make(chan struct{}, 1),
|
stopChan: make(chan struct{}, 1),
|
||||||
}
|
}
|
||||||
@ -61,19 +65,22 @@ func (fh *realFsHandler) needsUpdate() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fh *realFsHandler) update() error {
|
func (fh *realFsHandler) update() error {
|
||||||
var usage uint64
|
// TODO(vishh): Add support for external mounts.
|
||||||
for _, dir := range fh.storageDirs {
|
baseUsage, err := fh.fsInfo.GetDirUsage(fh.rootfs)
|
||||||
// TODO(Vishh): Add support for external mounts.
|
|
||||||
dirUsage, err := fh.fsInfo.GetDirUsage(dir)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
usage += dirUsage
|
|
||||||
|
otherUsage, error := fh.fsInfo.GetDirUsage(fh.other)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fh.Lock()
|
fh.Lock()
|
||||||
defer fh.Unlock()
|
defer fh.Unlock()
|
||||||
fh.lastUpdate = time.Now()
|
fh.lastUpdate = time.Now()
|
||||||
fh.usageBytes = usage
|
fh.usageBytes = baseUsage + otherUsage
|
||||||
|
fh.baseUsage = baseUsage
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,8 +111,8 @@ func (fh *realFsHandler) stop() {
|
|||||||
close(fh.stopChan)
|
close(fh.stopChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fh *realFsHandler) usage() uint64 {
|
func (fh *realFsHandler) usage() (baseUsageBytes, totalUsageBytes uint64) {
|
||||||
fh.RLock()
|
fh.RLock()
|
||||||
defer fh.RUnlock()
|
defer fh.RUnlock()
|
||||||
return fh.usageBytes
|
return fh.baseUsageBytes, fh.usageBytes
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ type dockerContainerHandler struct {
|
|||||||
|
|
||||||
storageDriver storageDriver
|
storageDriver storageDriver
|
||||||
fsInfo fs.FsInfo
|
fsInfo fs.FsInfo
|
||||||
storageDirs []string
|
rootfsStorageDir string
|
||||||
|
|
||||||
// Time at which this container was created.
|
// Time at which this container was created.
|
||||||
creationTime time.Time
|
creationTime time.Time
|
||||||
@ -118,14 +118,13 @@ func newDockerContainerHandler(
|
|||||||
id := ContainerNameToDockerId(name)
|
id := ContainerNameToDockerId(name)
|
||||||
|
|
||||||
// Add the Containers dir where the log files are stored.
|
// Add the Containers dir where the log files are stored.
|
||||||
storageDirs := []string{path.Join(*dockerRootDir, pathToContainersDir, id)}
|
otherStorageDir := path.Join(*dockerRootDir, pathToContainersDir, id)
|
||||||
|
var rootfsStorageDir string
|
||||||
switch storageDriver {
|
switch storageDriver {
|
||||||
case aufsStorageDriver:
|
case aufsStorageDriver:
|
||||||
// Add writable layer for aufs.
|
rootfsStorageDir = path.Join(*dockerRootDir, pathToAufsDir, id)
|
||||||
storageDirs = append(storageDirs, path.Join(*dockerRootDir, pathToAufsDir, id))
|
|
||||||
case overlayStorageDriver:
|
case overlayStorageDriver:
|
||||||
storageDirs = append(storageDirs, path.Join(*dockerRootDir, pathToOverlayDir, id))
|
rootfsStorageDir = path.Join(*dockerRootDir, pathToOverlayDir, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := &dockerContainerHandler{
|
handler := &dockerContainerHandler{
|
||||||
@ -138,8 +137,8 @@ func newDockerContainerHandler(
|
|||||||
storageDriver: storageDriver,
|
storageDriver: storageDriver,
|
||||||
fsInfo: fsInfo,
|
fsInfo: fsInfo,
|
||||||
rootFs: rootFs,
|
rootFs: rootFs,
|
||||||
storageDirs: storageDirs,
|
rootfsStorageDir: rootfsStorageDir,
|
||||||
fsHandler: newFsHandler(time.Minute, storageDirs, fsInfo),
|
fsHandler: newFsHandler(time.Minute, rootfsStorageDir, otherStorageDir, fsInfo),
|
||||||
}
|
}
|
||||||
|
|
||||||
// We assume that if Inspect fails then the container is not known to docker.
|
// We assume that if Inspect fails then the container is not known to docker.
|
||||||
@ -274,9 +273,7 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// As of now we assume that all the storage dirs are on the same device.
|
deviceInfo, err := self.fsInfo.GetDirFsDevice(self.rootfsStorageDir)
|
||||||
// The first storage dir will be that of the image layers.
|
|
||||||
deviceInfo, err := self.fsInfo.GetDirFsDevice(self.storageDirs[0])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -296,7 +293,7 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
|
|||||||
|
|
||||||
fsStat := info.FsStats{Device: deviceInfo.Device, Limit: limit}
|
fsStat := info.FsStats{Device: deviceInfo.Device, Limit: limit}
|
||||||
|
|
||||||
fsStat.Usage = self.fsHandler.usage()
|
fs.Stat.BaseUsage, fsStat.Usage = self.fsHandler.usage()
|
||||||
stats.Filesystem = append(stats.Filesystem, fsStat)
|
stats.Filesystem = append(stats.Filesystem, fsStat)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -398,6 +398,10 @@ type FsStats struct {
|
|||||||
// Number of bytes that is consumed by the container on this filesystem.
|
// Number of bytes that is consumed by the container on this filesystem.
|
||||||
Usage uint64 `json:"usage"`
|
Usage uint64 `json:"usage"`
|
||||||
|
|
||||||
|
// Base Usage that is consumed by the container's writable layer.
|
||||||
|
// This field is only applicable for docker container's as of now.
|
||||||
|
BaseUsage uint64 `json:"base_usage"`
|
||||||
|
|
||||||
// Number of bytes available for non-root user.
|
// Number of bytes available for non-root user.
|
||||||
Available uint64 `json:"available"`
|
Available uint64 `json:"available"`
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ type ContainerSpec struct {
|
|||||||
Image string `json:"image,omitempty"`
|
Image string `json:"image,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerStats struct {
|
type DeprecatedContainerStats struct {
|
||||||
// The time of this stat point.
|
// The time of this stat point.
|
||||||
Timestamp time.Time `json:"timestamp"`
|
Timestamp time.Time `json:"timestamp"`
|
||||||
// CPU statistics
|
// CPU statistics
|
||||||
@ -124,6 +124,28 @@ type ContainerStats struct {
|
|||||||
CustomMetrics map[string][]v1.MetricVal `json:"custom_metrics,omitempty"`
|
CustomMetrics map[string][]v1.MetricVal `json:"custom_metrics,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ContainerStats struct {
|
||||||
|
// The time of this stat point.
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
// CPU statistics
|
||||||
|
// In nanoseconds (aggregated)
|
||||||
|
Cpu *v1.CpuStats `json:"cpu,omitempty"`
|
||||||
|
// In nanocores per second (instantaneous)
|
||||||
|
CpuInst *CpuInstStats `json:"cpu_inst,omitempty"`
|
||||||
|
// Disk IO statistics
|
||||||
|
DiskIo *v1.DiskIoStats `json:"diskio,omitempty"`
|
||||||
|
// Memory statistics
|
||||||
|
Memory *v1.MemoryStats `json:"memory,omitempty"`
|
||||||
|
// Network statistics
|
||||||
|
Network *NetworkStats `json:"network,omitempty"`
|
||||||
|
// Filesystem statistics
|
||||||
|
Filesystem *FilesystemStats `json:"filesystem,omitempty"`
|
||||||
|
// Task load statistics
|
||||||
|
Load *v1.LoadStats `json:"load_stats,omitempty"`
|
||||||
|
// Custom Metrics
|
||||||
|
CustomMetrics map[string][]v1.MetricVal `json:"custom_metrics,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type Percentiles struct {
|
type Percentiles struct {
|
||||||
// Indicates whether the stats are present or not.
|
// Indicates whether the stats are present or not.
|
||||||
// If true, values below do not have any data.
|
// If true, values below do not have any data.
|
||||||
@ -262,3 +284,11 @@ type CpuInstUsage struct {
|
|||||||
// Unit: nanocores per second
|
// Unit: nanocores per second
|
||||||
System uint64 `json:"system"`
|
System uint64 `json:"system"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filesystem usage statistics.
|
||||||
|
type FilesystemStats struct {
|
||||||
|
// Total Number of bytes consumed by container.
|
||||||
|
TotalUsageBytes *uint64 `json:"totalUsageBytes,omitempty"`
|
||||||
|
// Number of bytes consumed by a container through its root filesystem.
|
||||||
|
BaseUsageBytes *uint64 `json:"baseUsageBytes,omitempty"`
|
||||||
|
}
|
||||||
|
@ -16,6 +16,8 @@ package v2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
// TODO(rjnagal): Move structs from v1.
|
// TODO(rjnagal): Move structs from v1.
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/cadvisor/info/v1"
|
"github.com/google/cadvisor/info/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -86,3 +88,125 @@ func GetAttributes(mi *v1.MachineInfo, vi *v1.VersionInfo) Attributes {
|
|||||||
InstanceType: mi.InstanceType,
|
InstanceType: mi.InstanceType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MachineStats contains usage statistics for the entire machine.
|
||||||
|
type MachineStats struct {
|
||||||
|
// The time of this stat point.
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
// In nanoseconds (aggregated)
|
||||||
|
Cpu *v1.CpuStats `json:"cpu,omitempty"`
|
||||||
|
// In nanocores per second (instantaneous)
|
||||||
|
CpuInst *CpuInstStats `json:"cpu_inst,omitempty"`
|
||||||
|
// Disk IO statistics
|
||||||
|
DiskIo *v1.DiskIoStats `json:"diskio,omitempty"`
|
||||||
|
// Memory statistics
|
||||||
|
Memory *v1.MemoryStats `json:"memory,omitempty"`
|
||||||
|
// Network statistics
|
||||||
|
Network *NetworkStats `json:"network,omitempty"`
|
||||||
|
// Filesystem statistics
|
||||||
|
Filesystem []MachineFsStats `json:"filesystem,omitempty"`
|
||||||
|
// Task load statistics
|
||||||
|
Load *v1.LoadStats `json:"load_stats,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MachineFsStats contains per filesystem capacity and usage information.
|
||||||
|
type MachineFsStats struct {
|
||||||
|
// The block device name associated with the filesystem.
|
||||||
|
Device string `json:"device"`
|
||||||
|
|
||||||
|
// Number of bytes that can be consumed on this filesystem.
|
||||||
|
Capacity *uint64 `json:"capacity,omitempty"`
|
||||||
|
|
||||||
|
// Number of bytes that is currently consumed on this filesystem.
|
||||||
|
Usage *uint64 `json:"usage,omitempty"`
|
||||||
|
|
||||||
|
// Number of bytes available for non-root user on this filesystem.
|
||||||
|
Available *uint64 `json:"available,omitempty"`
|
||||||
|
|
||||||
|
// DiskStats for this device.
|
||||||
|
DiskStats `json:"inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskStats contains per partition usage information.
|
||||||
|
// This information is only available at the machine level.
|
||||||
|
type DiskStats struct {
|
||||||
|
// Number of reads completed
|
||||||
|
// This is the total number of reads completed successfully.
|
||||||
|
ReadsCompleted *uint64 `json:"reads_completed,omitempty"`
|
||||||
|
|
||||||
|
// Number of reads merged
|
||||||
|
// Reads and writes which are adjacent to each other may be merged for
|
||||||
|
// efficiency. Thus two 4K reads may become one 8K read before it is
|
||||||
|
// ultimately handed to the disk, and so it will be counted (and queued)
|
||||||
|
// as only one I/O. This field lets you know how often this was done.
|
||||||
|
ReadsMerged *uint64 `json:"reads_merged,omitempty"`
|
||||||
|
|
||||||
|
// Number of sectors read
|
||||||
|
// This is the total number of sectors read successfully.
|
||||||
|
SectorsRead *uint64 `json:"sectors_read,omitempty"`
|
||||||
|
|
||||||
|
// Number of milliseconds spent reading
|
||||||
|
// This is the total number of milliseconds spent by all reads (as
|
||||||
|
// measured from __make_request() to end_that_request_last()).
|
||||||
|
ReadTime *uint64 `json:"read_time,omitempty"`
|
||||||
|
|
||||||
|
// Number of writes completed
|
||||||
|
// This is the total number of writes completed successfully.
|
||||||
|
WritesCompleted *uint64 `json:"writes_completed,omitempty"`
|
||||||
|
|
||||||
|
// Number of writes merged
|
||||||
|
// See the description of reads merged.
|
||||||
|
WritesMerged *uint64 `json:"writes_merged,omitempty"`
|
||||||
|
|
||||||
|
// Number of sectors written
|
||||||
|
// This is the total number of sectors written successfully.
|
||||||
|
SectorsWritten *uint64 `json:"sectors_written,omitempty"`
|
||||||
|
|
||||||
|
// Number of milliseconds spent writing
|
||||||
|
// This is the total number of milliseconds spent by all writes (as
|
||||||
|
// measured from __make_request() to end_that_request_last()).
|
||||||
|
WriteTime *uint64 `json:"write_time,omitempty"`
|
||||||
|
|
||||||
|
// Number of I/Os currently in progress
|
||||||
|
// The only field that should go to zero. Incremented as requests are
|
||||||
|
// given to appropriate struct request_queue and decremented as they finish.
|
||||||
|
IoInProgress *uint64 `json:"io_in_progress,omitempty"`
|
||||||
|
|
||||||
|
// Number of milliseconds spent doing I/Os
|
||||||
|
// This field increases so long as field 9 is nonzero.
|
||||||
|
IoTime *uint64 `json:"io_time,omitempty"`
|
||||||
|
|
||||||
|
// weighted number of milliseconds spent doing I/Os
|
||||||
|
// This field is incremented at each I/O start, I/O completion, I/O
|
||||||
|
// merge, or read of these stats by the number of I/Os in progress
|
||||||
|
// (field 9) times the number of milliseconds spent doing I/O since the
|
||||||
|
// last update of this field. This can provide an easy measure of both
|
||||||
|
// I/O completion time and the backlog that may be accumulating.
|
||||||
|
WeightedIoTime *uint64 `json:"weighted_io_time,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMachineFsStats(fsStats []v1.FsStats) []MachineFsStats {
|
||||||
|
var result []MachineFsStats
|
||||||
|
for _, stat := range fsStats {
|
||||||
|
result = append(result, MachineFsStats{
|
||||||
|
Device: stat.Device,
|
||||||
|
Capacity: &stat.Limit,
|
||||||
|
Usage: &stat.Usage,
|
||||||
|
Available: &stat.Available,
|
||||||
|
DiskStats: DiskStats{
|
||||||
|
ReadsCompleted: &stat.ReadsCompleted,
|
||||||
|
ReadsMerged: &stat.ReadsMerged,
|
||||||
|
SectorsRead: &stat.SectorsRead,
|
||||||
|
ReadTime: &stat.ReadTime,
|
||||||
|
WritesCompleted: &stat.WritesCompleted,
|
||||||
|
WritesMerged: &stat.WritesMerged,
|
||||||
|
SectorsWritten: &stat.SectorsWritten,
|
||||||
|
WriteTime: &stat.WriteTime,
|
||||||
|
IoInProgress: &stat.IoInProgress,
|
||||||
|
IoTime: &stat.IoTime,
|
||||||
|
WeightedIoTime: &stat.WeightedIoTime,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user