cgroup2: implement `docker stats`

The following fields are unsupported:

* BlkioStats: all fields other than IoServiceBytesRecursive
* CPUStats: CPUUsage.PercpuUsage
* MemoryStats: MaxUsage and Failcnt

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
Akihiro Suda 2020-03-10 06:40:34 +09:00
parent bd81cf2859
commit 3802830989
6 changed files with 3847 additions and 4 deletions

View File

@ -5564,6 +5564,12 @@ paths:
If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is
nil then for compatibility with older daemons the length of the
corresponding `cpu_usage.percpu_usage` array should be used.
On a cgroup v2 host, the following fields are not set
* `blkio_stats`: all fields other than `io_service_bytes_recursive`
* `cpu_stats`: `cpu_usage.percpu_usage`
* `memory_stats`: `max_usage` and `failcnt`
Also, `memory_stats.stats` fields are incompatible with cgroup v1.
operationId: "ContainerStats"
produces: ["application/json"]
responses:

View File

@ -17,6 +17,7 @@ import (
"time"
statsV1 "github.com/containerd/cgroups/stats/v1"
statsV2 "github.com/containerd/cgroups/v2/stats"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/blkiodev"
pblkiodev "github.com/docker/docker/api/types/blkiodev"
@ -1446,6 +1447,17 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
s := &types.StatsJSON{}
s.Read = cs.Read
stats := cs.Metrics
switch t := stats.(type) {
case *statsV1.Metrics:
return daemon.statsV1(s, t)
case *statsV2.Metrics:
return daemon.statsV2(s, t)
default:
return nil, errors.Errorf("unexpected type of metrics %+v", t)
}
}
func (daemon *Daemon) statsV1(s *types.StatsJSON, stats *statsV1.Metrics) (*types.StatsJSON, error) {
if stats.Blkio != nil {
s.BlkioStats = types.BlkioStats{
IoServiceBytesRecursive: copyBlkioEntry(stats.Blkio.IoServiceBytesRecursive),
@ -1539,6 +1551,104 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
return s, nil
}
func (daemon *Daemon) statsV2(s *types.StatsJSON, stats *statsV2.Metrics) (*types.StatsJSON, error) {
if stats.Io != nil {
var isbr []types.BlkioStatEntry
for _, re := range stats.Io.Usage {
isbr = append(isbr,
types.BlkioStatEntry{
Major: re.Major,
Minor: re.Minor,
Op: "read",
Value: re.Rbytes,
},
types.BlkioStatEntry{
Major: re.Major,
Minor: re.Minor,
Op: "write",
Value: re.Wbytes,
},
)
}
s.BlkioStats = types.BlkioStats{
IoServiceBytesRecursive: isbr,
// Other fields are unsupported
}
}
if stats.CPU != nil {
s.CPUStats = types.CPUStats{
CPUUsage: types.CPUUsage{
TotalUsage: stats.CPU.UsageUsec * 1000,
// PercpuUsage is not supported
UsageInKernelmode: stats.CPU.SystemUsec * 1000,
UsageInUsermode: stats.CPU.UserUsec * 1000,
},
ThrottlingData: types.ThrottlingData{
Periods: stats.CPU.NrPeriods,
ThrottledPeriods: stats.CPU.NrThrottled,
ThrottledTime: stats.CPU.ThrottledUsec * 1000,
},
}
}
if stats.Memory != nil {
raw := make(map[string]uint64)
raw["anon"] = stats.Memory.Anon
raw["file"] = stats.Memory.File
raw["kernel_stack"] = stats.Memory.KernelStack
raw["slab"] = stats.Memory.Slab
raw["sock"] = stats.Memory.Sock
raw["shmem"] = stats.Memory.Shmem
raw["file_mapped"] = stats.Memory.FileMapped
raw["file_dirty"] = stats.Memory.FileDirty
raw["file_writeback"] = stats.Memory.FileWriteback
raw["anon_thp"] = stats.Memory.AnonThp
raw["inactive_anon"] = stats.Memory.InactiveAnon
raw["active_anon"] = stats.Memory.ActiveAnon
raw["inactive_file"] = stats.Memory.InactiveFile
raw["active_file"] = stats.Memory.ActiveFile
raw["unevictable"] = stats.Memory.Unevictable
raw["slab_reclaimable"] = stats.Memory.SlabReclaimable
raw["slab_unreclaimable"] = stats.Memory.SlabUnreclaimable
raw["pgfault"] = stats.Memory.Pgfault
raw["pgmajfault"] = stats.Memory.Pgmajfault
raw["workingset_refault"] = stats.Memory.WorkingsetRefault
raw["workingset_activate"] = stats.Memory.WorkingsetActivate
raw["workingset_nodereclaim"] = stats.Memory.WorkingsetNodereclaim
raw["pgrefill"] = stats.Memory.Pgrefill
raw["pgscan"] = stats.Memory.Pgscan
raw["pgsteal"] = stats.Memory.Pgsteal
raw["pgactivate"] = stats.Memory.Pgactivate
raw["pgdeactivate"] = stats.Memory.Pgdeactivate
raw["pglazyfree"] = stats.Memory.Pglazyfree
raw["pglazyfreed"] = stats.Memory.Pglazyfreed
raw["thp_fault_alloc"] = stats.Memory.ThpFaultAlloc
raw["thp_collapse_alloc"] = stats.Memory.ThpCollapseAlloc
s.MemoryStats = types.MemoryStats{
// Stats is not compatible with v1
Stats: raw,
Usage: stats.Memory.Usage,
// MaxUsage is not supported
Limit: stats.Memory.UsageLimit,
// TODO: Failcnt
}
// if the container does not set memory limit, use the machineMemory
if s.MemoryStats.Limit > daemon.machineMemory && daemon.machineMemory > 0 {
s.MemoryStats.Limit = daemon.machineMemory
}
}
if stats.Pids != nil {
s.PidsStats = types.PidsStats{
Current: stats.Pids.Current,
Limit: stats.Pids.Limit,
}
}
return s, nil
}
// setDefaultIsolation determines the default isolation mode for the
// daemon to run in. This is only applicable on Windows
func (daemon *Daemon) setDefaultIsolation() error {

View File

@ -3,7 +3,6 @@ package types // import "github.com/docker/docker/libcontainerd/types"
import (
"time"
statsV1 "github.com/containerd/cgroups/stats/v1"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
@ -12,14 +11,17 @@ type Summary struct{}
// Stats holds metrics properties as returned by containerd
type Stats struct {
Read time.Time
Metrics *statsV1.Metrics
Read time.Time
// Metrics is expected to be either one of:
// * github.com/containerd/cgroups/stats/v1.Metrics
// * github.com/containerd/cgroups/stats/v2.Metrics
Metrics interface{}
}
// InterfaceToStats returns a stats object from the platform-specific interface.
func InterfaceToStats(read time.Time, v interface{}) *Stats {
return &Stats{
Metrics: v.(*statsV1.Metrics),
Metrics: v,
Read: read,
}
}

17
vendor/github.com/containerd/cgroups/v2/stats/doc.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package stats

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,96 @@
syntax = "proto3";
package io.containerd.cgroups.v2;
import "gogoproto/gogo.proto";
message Metrics {
PidsStat pids = 1;
CPUStat cpu = 2 [(gogoproto.customname) = "CPU"];
MemoryStat memory = 4;
RdmaStat rdma = 5;
IOStat io = 6;
repeated HugeTlbStat hugetlb = 7;
}
message PidsStat {
uint64 current = 1;
uint64 limit = 2;
}
message CPUStat {
uint64 usage_usec = 1;
uint64 user_usec = 2;
uint64 system_usec = 3;
uint64 nr_periods = 4;
uint64 nr_throttled = 5;
uint64 throttled_usec = 6;
}
message MemoryStat {
uint64 anon = 1;
uint64 file = 2;
uint64 kernel_stack = 3;
uint64 slab = 4;
uint64 sock = 5;
uint64 shmem = 6;
uint64 file_mapped = 7;
uint64 file_dirty = 8;
uint64 file_writeback = 9;
uint64 anon_thp = 10;
uint64 inactive_anon = 11;
uint64 active_anon = 12;
uint64 inactive_file = 13;
uint64 active_file = 14;
uint64 unevictable = 15;
uint64 slab_reclaimable = 16;
uint64 slab_unreclaimable = 17;
uint64 pgfault = 18;
uint64 pgmajfault = 19;
uint64 workingset_refault = 20;
uint64 workingset_activate = 21;
uint64 workingset_nodereclaim = 22;
uint64 pgrefill = 23;
uint64 pgscan = 24;
uint64 pgsteal = 25;
uint64 pgactivate = 26;
uint64 pgdeactivate = 27;
uint64 pglazyfree = 28;
uint64 pglazyfreed = 29;
uint64 thp_fault_alloc = 30;
uint64 thp_collapse_alloc = 31;
uint64 usage = 32;
uint64 usage_limit = 33;
uint64 swap_usage = 34;
uint64 swap_limit = 35;
}
message RdmaStat {
repeated RdmaEntry current = 1;
repeated RdmaEntry limit = 2;
}
message RdmaEntry {
string device = 1;
uint32 hca_handles = 2;
uint32 hca_objects = 3;
}
message IOStat {
repeated IOEntry usage = 1;
}
message IOEntry {
uint64 major = 1;
uint64 minor = 2;
uint64 rbytes = 3;
uint64 wbytes = 4;
uint64 rios = 5;
uint64 wios = 6;
}
message HugeTlbStat {
uint64 current = 1;
uint64 max = 2;
string pagesize = 3;
}