mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(cli/clistat): read from alternate cgroup path (#8591)
* Attempts reading cgroupv1 quota, period, usage from /sys/fs/cgroup/cpu,cpuacct by default * Fall back to /sys/fs/cgroup/cpu for v1 quota and period * Fall back to /sys/fs/cgroup/cpuacct for v1 usage Fixes https://github.com/coder/coder/issues/8468
This commit is contained in:
+34
-10
@@ -6,6 +6,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/spf13/afero"
|
||||
"golang.org/x/xerrors"
|
||||
"tailscale.com/types/ptr"
|
||||
@@ -15,11 +16,10 @@ import (
|
||||
// Ref: https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
|
||||
const (
|
||||
// CPU usage of all tasks in cgroup in nanoseconds.
|
||||
cgroupV1CPUAcctUsage = "/sys/fs/cgroup/cpu/cpuacct.usage"
|
||||
// Alternate path
|
||||
cgroupV1CPUAcctUsageAlt = "/sys/fs/cgroup/cpu,cpuacct/cpuacct.usage"
|
||||
cgroupV1CPUAcctUsage = "/sys/fs/cgroup/cpu,cpuacct/cpuacct.usage"
|
||||
// CFS quota and period for cgroup in MICROseconds
|
||||
cgroupV1CFSQuotaUs = "/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us"
|
||||
cgroupV1CFSQuotaUs = "/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us"
|
||||
// CFS period for cgroup in MICROseconds
|
||||
cgroupV1CFSPeriodUs = "/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us"
|
||||
// Maximum memory usable by cgroup in bytes
|
||||
cgroupV1MemoryMaxUsageBytes = "/sys/fs/cgroup/memory/memory.max_usage_in_bytes"
|
||||
@@ -153,12 +153,26 @@ func (s *Statter) cGroupV2CPUTotal() (total float64, err error) {
|
||||
func (s *Statter) cGroupV1CPUTotal() (float64, error) {
|
||||
periodUs, err := readInt64(s.fs, cgroupV1CFSPeriodUs)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("read cpu period: %w", err)
|
||||
// Try alternate path under /sys/fs/cpu
|
||||
var merr error
|
||||
merr = multierror.Append(merr, xerrors.Errorf("get cpu period: %w", err))
|
||||
periodUs, err = readInt64(s.fs, strings.Replace(cgroupV1CFSPeriodUs, "cpu,cpuacct", "cpu", 1))
|
||||
if err != nil {
|
||||
merr = multierror.Append(merr, xerrors.Errorf("get cpu period: %w", err))
|
||||
return 0, merr
|
||||
}
|
||||
}
|
||||
|
||||
quotaUs, err := readInt64(s.fs, cgroupV1CFSQuotaUs)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("read cpu quota: %w", err)
|
||||
// Try alternate path under /sys/fs/cpu
|
||||
var merr error
|
||||
merr = multierror.Append(merr, xerrors.Errorf("get cpu quota: %w", err))
|
||||
quotaUs, err = readInt64(s.fs, strings.Replace(cgroupV1CFSQuotaUs, "cpu,cpuacct", "cpu", 1))
|
||||
if err != nil {
|
||||
merr = multierror.Append(merr, xerrors.Errorf("get cpu quota: %w", err))
|
||||
return 0, merr
|
||||
}
|
||||
}
|
||||
|
||||
if quotaUs < 0 {
|
||||
@@ -171,10 +185,13 @@ func (s *Statter) cGroupV1CPUTotal() (float64, error) {
|
||||
func (s *Statter) cGroupV1CPUUsed() (float64, error) {
|
||||
usageNs, err := readInt64(s.fs, cgroupV1CPUAcctUsage)
|
||||
if err != nil {
|
||||
// try alternate path
|
||||
usageNs, err = readInt64(s.fs, cgroupV1CPUAcctUsageAlt)
|
||||
// Try alternate path under /sys/fs/cgroup/cpuacct
|
||||
var merr error
|
||||
merr = multierror.Append(merr, xerrors.Errorf("read cpu used: %w", err))
|
||||
usageNs, err = readInt64(s.fs, strings.Replace(cgroupV1CPUAcctUsage, "cpu,cpuacct", "cpuacct", 1))
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("read cpu used: %w", err)
|
||||
merr = multierror.Append(merr, xerrors.Errorf("read cpu used: %w", err))
|
||||
return 0, merr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +199,14 @@ func (s *Statter) cGroupV1CPUUsed() (float64, error) {
|
||||
usageNs /= 1000
|
||||
periodUs, err := readInt64(s.fs, cgroupV1CFSPeriodUs)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("get cpu period: %w", err)
|
||||
// Try alternate path under /sys/fs/cpu
|
||||
var merr error
|
||||
merr = multierror.Append(merr, xerrors.Errorf("get cpu period: %w", err))
|
||||
periodUs, err = readInt64(s.fs, strings.Replace(cgroupV1CFSPeriodUs, "cpu,cpuacct", "cpu", 1))
|
||||
if err != nil {
|
||||
merr = multierror.Append(merr, xerrors.Errorf("get cpu period: %w", err))
|
||||
return 0, merr
|
||||
}
|
||||
}
|
||||
|
||||
return float64(usageNs) / float64(periodUs), nil
|
||||
|
||||
@@ -153,6 +153,24 @@ func TestStatter(t *testing.T) {
|
||||
assert.Equal(t, "cores", cpu.Unit)
|
||||
})
|
||||
|
||||
t.Run("ContainerCPU/AltPath", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
fs := initFS(t, fsContainerCgroupV1AltPath)
|
||||
fakeWait := func(time.Duration) {
|
||||
// Fake 1 second in ns of usage
|
||||
mungeFS(t, fs, "/sys/fs/cgroup/cpuacct/cpuacct.usage", "100000000")
|
||||
}
|
||||
s, err := New(WithFS(fs), withNproc(2), withWait(fakeWait))
|
||||
require.NoError(t, err)
|
||||
cpu, err := s.ContainerCPU()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cpu)
|
||||
assert.Equal(t, 1.0, cpu.Used)
|
||||
require.NotNil(t, cpu.Total)
|
||||
assert.Equal(t, 2.5, *cpu.Total)
|
||||
assert.Equal(t, "cores", cpu.Unit)
|
||||
})
|
||||
|
||||
t.Run("ContainerMemory", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
fs := initFS(t, fsContainerCgroupV1)
|
||||
@@ -366,4 +384,15 @@ proc /proc/sys proc ro,nosuid,nodev,noexec,relatime 0 0`,
|
||||
cgroupV1MemoryUsageBytes: "536870912",
|
||||
cgroupV1MemoryStat: "total_inactive_file 268435456",
|
||||
}
|
||||
fsContainerCgroupV1AltPath = map[string]string{
|
||||
procOneCgroup: "0::/docker/aa86ac98959eeedeae0ecb6e0c9ddd8ae8b97a9d0fdccccf7ea7a474f4e0bb1f",
|
||||
procMounts: `overlay / overlay rw,relatime,lowerdir=/some/path:/some/path,upperdir=/some/path:/some/path,workdir=/some/path:/some/path 0 0
|
||||
proc /proc/sys proc ro,nosuid,nodev,noexec,relatime 0 0`,
|
||||
"/sys/fs/cgroup/cpuacct/cpuacct.usage": "0",
|
||||
"/sys/fs/cgroup/cpu/cpu.cfs_quota_us": "250000",
|
||||
"/sys/fs/cgroup/cpu/cpu.cfs_period_us": "100000",
|
||||
cgroupV1MemoryMaxUsageBytes: "1073741824",
|
||||
cgroupV1MemoryUsageBytes: "536870912",
|
||||
cgroupV1MemoryStat: "total_inactive_file 268435456",
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user