Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions cmd/nerdctl/container/container_stats_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,31 @@ func TestStats(t *testing.T) {
base.Cmd("container", "stats", "--no-stream").AssertOutContains(testContainerName)
base.Cmd("container", "stats", "--no-stream", testContainerName).AssertOK()
}

func TestStatsMemoryLimitNotSet(t *testing.T) {
if rootlessutil.IsRootless() && infoutil.CgroupsVersion() == "1" {
t.Skip("test skipped for rootless containers on cgroup v1")
}
testContainerName := testutil.Identifier(t)[:12]

base := testutil.NewBase(t)
defer base.Cmd("rm", "-f", testContainerName).Run()

base.Cmd("run", "-d", "--name", testContainerName, testutil.AlpineImage, "sleep", "10").AssertOK()
base.Cmd("stats", "--no-stream").AssertOutNotContains("16EiB")
base.Cmd("stats", "--no-stream", testContainerName).AssertOK()
}

func TestStatsMemoryLimitSet(t *testing.T) {
if rootlessutil.IsRootless() && infoutil.CgroupsVersion() == "1" {
t.Skip("test skipped for rootless containers on cgroup v1")
}
testContainerName := testutil.Identifier(t)[:12]

base := testutil.NewBase(t)
defer base.Cmd("rm", "-f", testContainerName).Run()

base.Cmd("run", "-d", "--name", testContainerName, "--memory", "1g", testutil.AlpineImage, "sleep", "10").AssertOK()
base.Cmd("stats", "--no-stream").AssertOutContains("1GiB")
base.Cmd("stats", "--no-stream", testContainerName).AssertOK()
}
40 changes: 36 additions & 4 deletions pkg/statsutil/stats_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
package statsutil

import (
"bufio"
"os"
"strconv"
"strings"
"time"

"github.com/vishvananda/netlink"
Expand All @@ -35,11 +39,10 @@ func calculateMemPercent(limit float64, usedNo float64) float64 {
}

func SetCgroupStatsFields(previousStats *ContainerStats, data *v1.Metrics, links []netlink.Link) (StatsEntry, error) {

cpuPercent := calculateCgroupCPUPercent(previousStats, data)
blkRead, blkWrite := calculateCgroupBlockIO(data)
mem := calculateCgroupMemUsage(data)
memLimit := float64(data.Memory.Usage.Limit)
memLimit := getCgroupMemLimit(float64(data.Memory.Usage.Limit))
memPercent := calculateMemPercent(memLimit, mem)
pidsStatsCurrent := data.Pids.Current
netRx, netTx := calculateCgroupNetwork(links)
Expand All @@ -59,11 +62,10 @@ func SetCgroupStatsFields(previousStats *ContainerStats, data *v1.Metrics, links
}

func SetCgroup2StatsFields(previousStats *ContainerStats, metrics *v2.Metrics, links []netlink.Link) (StatsEntry, error) {

cpuPercent := calculateCgroup2CPUPercent(previousStats, metrics)
blkRead, blkWrite := calculateCgroup2IO(metrics)
mem := calculateCgroup2MemUsage(metrics)
memLimit := float64(metrics.Memory.UsageLimit)
memLimit := getCgroupMemLimit(float64(metrics.Memory.UsageLimit))
memPercent := calculateMemPercent(memLimit, mem)
pidsStatsCurrent := metrics.Pids.Current
netRx, netTx := calculateCgroupNetwork(links)
Expand All @@ -82,6 +84,36 @@ func SetCgroup2StatsFields(previousStats *ContainerStats, metrics *v2.Metrics, l

}

func getCgroupMemLimit(memLimit float64) float64 {
if memLimit == float64(^uint64(0)) {
return getHostMemLimit()
}
return memLimit
}

func getHostMemLimit() float64 {
file, err := os.Open("/proc/meminfo")
if err != nil {
return float64(^uint64(0))
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
if strings.HasPrefix(scanner.Text(), "MemTotal:") {
fields := strings.Fields(scanner.Text())
if len(fields) >= 2 {
memKb, err := strconv.ParseUint(fields[1], 10, 64)
if err == nil {
return float64(memKb * 1024) // kB to bytes
}
}
break
}
}
return float64(^uint64(0))
}

func calculateCgroupCPUPercent(previousStats *ContainerStats, metrics *v1.Metrics) float64 {
var (
cpuPercent = 0.0
Expand Down