diff --git a/cmd/cadvisor.go b/cmd/cadvisor.go index cb7f27af98..fc34d967bd 100644 --- a/cmd/cadvisor.go +++ b/cmd/cadvisor.go @@ -100,14 +100,13 @@ func init() { optstr := container.AllMetrics.String() flag.Var(&ignoreMetrics, "disable_metrics", fmt.Sprintf("comma-separated list of `metrics` to be disabled. Options are %s.", optstr)) flag.Var(&enableMetrics, "enable_metrics", fmt.Sprintf("comma-separated list of `metrics` to be enabled. If set, overrides 'disable_metrics'. Options are %s.", optstr)) - - // Default logging verbosity to V(2) - _ = flag.Set("v", "2") } func main() { klog.InitFlags(nil) defer klog.Flush() + // Default logging verbosity to V(2) + _ = flag.Set("v", "2") flag.Parse() if *versionFlag { diff --git a/cmd/go.mod b/cmd/go.mod index ae8e845daf..31606e44f8 100644 --- a/cmd/go.mod +++ b/cmd/go.mod @@ -45,10 +45,10 @@ require ( github.com/containerd/console v1.0.3 // indirect github.com/containerd/ttrpc v1.2.2 // indirect github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 // indirect - github.com/cyphar/filepath-securejoin v0.2.3 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/distribution v2.8.1+incompatible // indirect - github.com/docker/docker v20.10.21+incompatible // indirect + github.com/docker/docker v20.10.24+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/eapache/go-resiliency v1.3.0 // indirect diff --git a/cmd/go.sum b/cmd/go.sum index f6659d2a0d..b1a2a93463 100644 --- a/cmd/go.sum +++ b/cmd/go.sum @@ -91,15 +91,15 @@ github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= -github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE= +github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= diff --git a/container/libcontainer/handler.go b/container/libcontainer/handler.go index 5a7f694776..24405ccff4 100644 --- a/container/libcontainer/handler.go +++ b/container/libcontainer/handler.go @@ -39,7 +39,6 @@ import ( ) var ( - whitelistedUlimits = [...]string{"max_open_files"} referencedResetInterval = flag.Uint64("referenced_reset_interval", 0, "Reset interval for referenced bytes (container_referenced_bytes metric), number of measurement cycles after which referenced bytes are cleared, if set to 0 referenced bytes are never cleared (default: 0)") @@ -205,51 +204,53 @@ func parseUlimit(value string) (int64, error) { return num, nil } -func isUlimitWhitelisted(name string) bool { - for _, whitelist := range whitelistedUlimits { - if name == whitelist { - return true - } - } - return false -} - func processLimitsFile(fileData string) []info.UlimitSpec { + const maxOpenFilesLinePrefix = "Max open files" + limits := strings.Split(fileData, "\n") ulimits := make([]info.UlimitSpec, 0, len(limits)) for _, lim := range limits { // Skip any headers/footers - if strings.HasPrefix(lim, "Max") { - - // Line format: Max open files 16384 16384 files - fields := regexp.MustCompile(`[\s]{2,}`).Split(lim, -1) - name := strings.Replace(strings.ToLower(strings.TrimSpace(fields[0])), " ", "_", -1) - - found := isUlimitWhitelisted(name) - if !found { - continue - } - - soft := strings.TrimSpace(fields[1]) - softNum, softErr := parseUlimit(soft) - - hard := strings.TrimSpace(fields[2]) - hardNum, hardErr := parseUlimit(hard) - - // Omit metric if there were any parsing errors - if softErr == nil && hardErr == nil { - ulimitSpec := info.UlimitSpec{ - Name: name, - SoftLimit: int64(softNum), - HardLimit: int64(hardNum), - } - ulimits = append(ulimits, ulimitSpec) + if strings.HasPrefix(lim, "Max open files") { + // Remove line prefix + ulimit, err := processMaxOpenFileLimitLine( + "max_open_files", + lim[len(maxOpenFilesLinePrefix):], + ) + if err == nil { + ulimits = append(ulimits, ulimit) } } } return ulimits } +// Any caller of processMaxOpenFileLimitLine must ensure that the name prefix is already removed from the limit line. +// with the "Max open files" prefix. +func processMaxOpenFileLimitLine(name, line string) (info.UlimitSpec, error) { + // Remove any leading whitespace + line = strings.TrimSpace(line) + // Split on whitespace + fields := strings.Fields(line) + if len(fields) != 3 { + return info.UlimitSpec{}, fmt.Errorf("unable to parse max open files line: %s", line) + } + // The first field is the soft limit, the second is the hard limit + soft, err := parseUlimit(fields[0]) + if err != nil { + return info.UlimitSpec{}, err + } + hard, err := parseUlimit(fields[1]) + if err != nil { + return info.UlimitSpec{}, err + } + return info.UlimitSpec{ + Name: name, + SoftLimit: soft, + HardLimit: hard, + }, nil +} + func processRootProcUlimits(rootFs string, rootPid int) []info.UlimitSpec { filePath := path.Join(rootFs, "/proc", strconv.Itoa(rootPid), "limits") out, err := os.ReadFile(filePath) diff --git a/container/libcontainer/handler_test.go b/container/libcontainer/handler_test.go index 826aaefd16..82da0b3e67 100644 --- a/container/libcontainer/handler_test.go +++ b/container/libcontainer/handler_test.go @@ -296,3 +296,28 @@ func TestClearReferencedBytesWhenClearRefsMissing(t *testing.T) { err := clearReferencedBytes(pids, 0, 1) assert.Nil(t, err) } + +var ulimits []info.UlimitSpec + +func BenchmarkProcessLimitsFile(b *testing.B) { + content, err := os.ReadFile("testdata/limits") + assert.Nil(b, err) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + ulimits = processLimitsFile(string(content)) + } + + // Ensure the compiler doesn't optimize away the benchmark + _ = ulimits +} + +func TestProcessMaxOpenFileLimitLine(t *testing.T) { + line := " 1073741816 1073741816 files " + + ulimit, err := processMaxOpenFileLimitLine("max_open_files", line) + assert.Nil(t, err) + assert.Equal(t, "max_open_files", ulimit.Name) + assert.Equal(t, int64(1073741816), ulimit.SoftLimit) + assert.Equal(t, int64(1073741816), ulimit.HardLimit) +} diff --git a/container/libcontainer/testdata/limits b/container/libcontainer/testdata/limits new file mode 100644 index 0000000000..03b5a16c87 --- /dev/null +++ b/container/libcontainer/testdata/limits @@ -0,0 +1,17 @@ +Limit Soft Limit Hard Limit Units +Max cpu time unlimited unlimited seconds +Max file size unlimited unlimited bytes +Max data size unlimited unlimited bytes +Max stack size 8388608 unlimited bytes +Max core file size unlimited unlimited bytes +Max resident set unlimited unlimited bytes +Max processes 119958 119958 processes +Max open files 1073741816 1073741816 files +Max locked memory 3932852224 3932852224 bytes +Max address space unlimited unlimited bytes +Max file locks unlimited unlimited locks +Max pending signals 119958 119958 signals +Max msgqueue size 819200 819200 bytes +Max nice priority 0 0 +Max realtime priority 0 0 +Max realtime timeout unlimited unlimited us diff --git a/go.mod b/go.mod index eb39a48bc2..3183835c2e 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/distribution v2.8.1+incompatible // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect github.com/go-logr/logr v1.2.0 // indirect github.com/godbus/dbus/v5 v5.0.6 // indirect github.com/golang/protobuf v1.5.3 // indirect diff --git a/go.sum b/go.sum index 0de87c6460..a153c8a659 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,8 @@ github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=