Skip to content

Commit 5068d92

Browse files
fvoznikagvisor-bot
authored andcommitted
Adjust CPU usage based on host cgroup usage
gVisor approximates CPU usage because it doesn't have full control over task scheduling. Scheduling is handled by the Golang's scheduler and the host kernel. This can cause gVisor to overeatimate CPU usage, especially in cases where there are multiple processes executing or cgroups restricts usage to fractions of a CPU. In order to report CPU usage more accurately, we use the sandbox's cgroup CPU usage to estimate how much CPU each container is using. PiperOrigin-RevId: 829002053
1 parent 605acfd commit 5068d92

File tree

2 files changed

+32
-12
lines changed

2 files changed

+32
-12
lines changed

runsc/boot/events.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,11 @@ func (cm *containerManager) Event(cid *string, out *EventOut) error {
154154
}
155155

156156
// Memory usage.
157-
memFile := control.CgroupControlFile{"memory", "/" + *cid, "memory.usage_in_bytes"}
157+
memFile := control.CgroupControlFile{
158+
Controller: "memory",
159+
Path: "/" + *cid,
160+
Name: "memory.usage_in_bytes",
161+
}
158162
memUsage, err := cm.getUsageFromCgroups(memFile)
159163
if err != nil {
160164
// Cgroups is not installed or there was an error to get usage
@@ -183,16 +187,34 @@ func (cm *containerManager) Event(cid *string, out *EventOut) error {
183187
out.Event.Data.Memory.Usage.Usage = memUsage
184188

185189
// CPU usage by container.
186-
cpuacctFile := control.CgroupControlFile{"cpuacct", "/" + *cid, "cpuacct.usage"}
187-
if cpuUsage, err := cm.getUsageFromCgroups(cpuacctFile); err != nil {
190+
out.ContainerUsage, err = cm.getCPUUsageFromCgroups()
191+
if err != nil {
188192
// Cgroups is not installed or there was an error to get usage
189193
// from the cgroups. Fall back to the old method of getting the
190194
// usage from the sentry and host cgroups.
191195
log.Warningf("could not get container cpu usage from cgroups, error: %v", err)
192-
193196
out.ContainerUsage = control.ContainerUsage(cm.l.k)
194-
} else {
195-
out.Event.Data.CPU.Usage.Total = cpuUsage
196197
}
197198
return nil
198199
}
200+
201+
func (cm *containerManager) getCPUUsageFromCgroups() (map[string]uint64, error) {
202+
usage := make(map[string]uint64)
203+
204+
cm.l.mu.Lock()
205+
defer cm.l.mu.Unlock()
206+
207+
for cid := range cm.l.containerIDs {
208+
cpuacctFile := control.CgroupControlFile{
209+
Controller: "cpuacct",
210+
Path: "/" + cid,
211+
Name: "cpuacct.usage",
212+
}
213+
cpuUsage, err := cm.getUsageFromCgroups(cpuacctFile)
214+
if err != nil {
215+
return nil, err
216+
}
217+
usage[cid] = cpuUsage
218+
}
219+
return usage, nil
220+
}

runsc/container/container.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -574,10 +574,8 @@ func (c *Container) Event() (*boot.EventOut, error) {
574574
return nil, err
575575
}
576576

577-
if len(event.ContainerUsage) > 0 {
578-
// Some stats can utilize host cgroups for accuracy.
579-
c.populateStats(event)
580-
}
577+
// CPU stats can utilize host cgroups for accuracy.
578+
c.populateStats(event)
581579

582580
return event, nil
583581
}
@@ -1708,9 +1706,9 @@ func (c *Container) populateStats(event *boot.EventOut) {
17081706

17091707
var containerUsage uint64
17101708
var allContainersUsage uint64
1711-
for ID, usage := range event.ContainerUsage {
1709+
for id, usage := range event.ContainerUsage {
17121710
allContainersUsage += usage
1713-
if ID == c.ID {
1711+
if id == c.ID {
17141712
containerUsage = usage
17151713
}
17161714
}

0 commit comments

Comments
 (0)