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:
Vishnu kannan 2015-12-28 15:21:52 -08:00
parent c285b726ef
commit 6dfdd8eca1
6 changed files with 252 additions and 38 deletions

View File

@ -31,6 +31,7 @@ const (
containersApi = "containers"
subcontainersApi = "subcontainers"
machineApi = "machine"
machineStatsApi = "machinestats"
dockerApi = "docker"
summaryApi = "summary"
statsApi = "stats"
@ -62,6 +63,7 @@ func getApiVersions() []ApiVersion {
v1_2 := newVersion1_2(v1_1)
v1_3 := newVersion1_3(v1_2)
v2_0 := newVersion2_0()
v2_1 := newVersion2_1()
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) {
supportedTypes := map[string]bool{
v2.TypeName: true,

View File

@ -34,8 +34,10 @@ type realFsHandler struct {
sync.RWMutex
lastUpdate time.Time
usageBytes uint64
baseUsageBytes uint64
period time.Duration
storageDirs []string
writable string
other string
fsInfo fs.FsInfo
// Tells the container to stop.
stopChan chan struct{}
@ -45,12 +47,14 @@ const longDu = time.Second
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{
lastUpdate: time.Time{},
usageBytes: 0,
baseUsageBytes: 0,
period: period,
storageDirs: storageDirs,
rootfs: rootfs,
other: other,
fsInfo: fsInfo,
stopChan: make(chan struct{}, 1),
}
@ -61,19 +65,22 @@ func (fh *realFsHandler) needsUpdate() bool {
}
func (fh *realFsHandler) update() error {
var usage uint64
for _, dir := range fh.storageDirs {
// TODO(Vishh): Add support for external mounts.
dirUsage, err := fh.fsInfo.GetDirUsage(dir)
// TODO(vishh): Add support for external mounts.
baseUsage, err := fh.fsInfo.GetDirUsage(fh.rootfs)
if err != nil {
return err
}
usage += dirUsage
otherUsage, error := fh.fsInfo.GetDirUsage(fh.other)
if err != nil {
return err
}
fh.Lock()
defer fh.Unlock()
fh.lastUpdate = time.Now()
fh.usageBytes = usage
fh.usageBytes = baseUsage + otherUsage
fh.baseUsage = baseUsage
return nil
}
@ -104,8 +111,8 @@ func (fh *realFsHandler) stop() {
close(fh.stopChan)
}
func (fh *realFsHandler) usage() uint64 {
func (fh *realFsHandler) usage() (baseUsageBytes, totalUsageBytes uint64) {
fh.RLock()
defer fh.RUnlock()
return fh.usageBytes
return fh.baseUsageBytes, fh.usageBytes
}

View File

@ -61,7 +61,7 @@ type dockerContainerHandler struct {
storageDriver storageDriver
fsInfo fs.FsInfo
storageDirs []string
rootfsStorageDir string
// Time at which this container was created.
creationTime time.Time
@ -118,14 +118,13 @@ func newDockerContainerHandler(
id := ContainerNameToDockerId(name)
// 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 {
case aufsStorageDriver:
// Add writable layer for aufs.
storageDirs = append(storageDirs, path.Join(*dockerRootDir, pathToAufsDir, id))
rootfsStorageDir = path.Join(*dockerRootDir, pathToAufsDir, id)
case overlayStorageDriver:
storageDirs = append(storageDirs, path.Join(*dockerRootDir, pathToOverlayDir, id))
rootfsStorageDir = path.Join(*dockerRootDir, pathToOverlayDir, id)
}
handler := &dockerContainerHandler{
@ -138,8 +137,8 @@ func newDockerContainerHandler(
storageDriver: storageDriver,
fsInfo: fsInfo,
rootFs: rootFs,
storageDirs: storageDirs,
fsHandler: newFsHandler(time.Minute, storageDirs, fsInfo),
rootfsStorageDir: rootfsStorageDir,
fsHandler: newFsHandler(time.Minute, rootfsStorageDir, otherStorageDir, fsInfo),
}
// 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
}
// As of now we assume that all the storage dirs are on the same device.
// The first storage dir will be that of the image layers.
deviceInfo, err := self.fsInfo.GetDirFsDevice(self.storageDirs[0])
deviceInfo, err := self.fsInfo.GetDirFsDevice(self.rootfsStorageDir)
if err != nil {
return err
}
@ -296,7 +293,7 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
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)
return nil

View File

@ -398,6 +398,10 @@ type FsStats struct {
// Number of bytes that is consumed by the container on this filesystem.
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.
Available uint64 `json:"available"`

View File

@ -95,7 +95,7 @@ type ContainerSpec struct {
Image string `json:"image,omitempty"`
}
type ContainerStats struct {
type DeprecatedContainerStats struct {
// The time of this stat point.
Timestamp time.Time `json:"timestamp"`
// CPU statistics
@ -124,6 +124,28 @@ type ContainerStats struct {
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 {
// Indicates whether the stats are present or not.
// If true, values below do not have any data.
@ -262,3 +284,11 @@ type CpuInstUsage struct {
// Unit: nanocores per second
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"`
}

View File

@ -16,6 +16,8 @@ package v2
import (
// TODO(rjnagal): Move structs from v1.
"time"
"github.com/google/cadvisor/info/v1"
)
@ -86,3 +88,125 @@ func GetAttributes(mi *v1.MachineInfo, vi *v1.VersionInfo) Attributes {
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
}