github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/statsutil/stats_linux.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package statsutil
    18  
    19  import (
    20  	"time"
    21  
    22  	v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
    23  	v2 "github.com/containerd/cgroups/v3/cgroup2/stats"
    24  	"github.com/vishvananda/netlink"
    25  )
    26  
    27  func SetCgroupStatsFields(previousStats *ContainerStats, data *v1.Metrics, links []netlink.Link) (StatsEntry, error) {
    28  
    29  	cpuPercent := calculateCgroupCPUPercent(previousStats, data)
    30  	blkRead, blkWrite := calculateCgroupBlockIO(data)
    31  	mem := calculateCgroupMemUsage(data)
    32  	memLimit := float64(data.Memory.Usage.Limit)
    33  	memPercent := calculateMemPercent(memLimit, mem)
    34  	pidsStatsCurrent := data.Pids.Current
    35  	netRx, netTx := calculateCgroupNetwork(links)
    36  
    37  	return StatsEntry{
    38  		CPUPercentage:    cpuPercent,
    39  		Memory:           mem,
    40  		MemoryPercentage: memPercent,
    41  		MemoryLimit:      memLimit,
    42  		NetworkRx:        netRx,
    43  		NetworkTx:        netTx,
    44  		BlockRead:        float64(blkRead),
    45  		BlockWrite:       float64(blkWrite),
    46  		PidsCurrent:      pidsStatsCurrent,
    47  	}, nil
    48  
    49  }
    50  
    51  func SetCgroup2StatsFields(previousStats *ContainerStats, metrics *v2.Metrics, links []netlink.Link) (StatsEntry, error) {
    52  
    53  	cpuPercent := calculateCgroup2CPUPercent(previousStats, metrics)
    54  	blkRead, blkWrite := calculateCgroup2IO(metrics)
    55  	mem := calculateCgroup2MemUsage(metrics)
    56  	memLimit := float64(metrics.Memory.UsageLimit)
    57  	memPercent := calculateMemPercent(memLimit, mem)
    58  	pidsStatsCurrent := metrics.Pids.Current
    59  	netRx, netTx := calculateCgroupNetwork(links)
    60  
    61  	return StatsEntry{
    62  		CPUPercentage:    cpuPercent,
    63  		Memory:           mem,
    64  		MemoryPercentage: memPercent,
    65  		MemoryLimit:      memLimit,
    66  		NetworkRx:        netRx,
    67  		NetworkTx:        netTx,
    68  		BlockRead:        float64(blkRead),
    69  		BlockWrite:       float64(blkWrite),
    70  		PidsCurrent:      pidsStatsCurrent,
    71  	}, nil
    72  
    73  }
    74  
    75  func calculateCgroupCPUPercent(previousStats *ContainerStats, metrics *v1.Metrics) float64 {
    76  	var (
    77  		cpuPercent = 0.0
    78  		// calculate the change for the cpu usage of the container in between readings
    79  		cpuDelta = float64(metrics.CPU.Usage.Total) - float64(previousStats.CgroupCPU)
    80  		// calculate the change for the entire system between readings
    81  		systemDelta = float64(metrics.CPU.Usage.Kernel) - float64(previousStats.CgroupSystem)
    82  		onlineCPUs  = float64(len(metrics.CPU.Usage.PerCPU))
    83  	)
    84  
    85  	if systemDelta > 0.0 && cpuDelta > 0.0 {
    86  		cpuPercent = (cpuDelta / systemDelta) * onlineCPUs * 100.0
    87  	}
    88  	return cpuPercent
    89  }
    90  
    91  // PercpuUsage is not supported in CgroupV2
    92  func calculateCgroup2CPUPercent(previousStats *ContainerStats, metrics *v2.Metrics) float64 {
    93  	var (
    94  		cpuPercent = 0.0
    95  		// calculate the change for the cpu usage of the container in between readings
    96  		cpuDelta = float64(metrics.CPU.UsageUsec*1000) - float64(previousStats.Cgroup2CPU)
    97  		// calculate the change for the entire system between readings
    98  		_ = float64(metrics.CPU.SystemUsec*1000) - float64(previousStats.Cgroup2System)
    99  		// time duration
   100  		timeDelta = time.Since(previousStats.Time)
   101  	)
   102  	if cpuDelta > 0.0 {
   103  		cpuPercent = cpuDelta / float64(timeDelta.Nanoseconds()) * 100.0
   104  	}
   105  	return cpuPercent
   106  }
   107  
   108  func calculateCgroupMemUsage(metrics *v1.Metrics) float64 {
   109  	if v := metrics.Memory.TotalInactiveFile; v < metrics.Memory.Usage.Usage {
   110  		return float64(metrics.Memory.Usage.Usage - v)
   111  	}
   112  	return float64(metrics.Memory.Usage.Usage)
   113  }
   114  
   115  func calculateCgroup2MemUsage(metrics *v2.Metrics) float64 {
   116  	if v := metrics.Memory.InactiveFile; v < metrics.Memory.Usage {
   117  		return float64(metrics.Memory.Usage - v)
   118  	}
   119  	return float64(metrics.Memory.Usage)
   120  }
   121  
   122  func calculateCgroupBlockIO(metrics *v1.Metrics) (uint64, uint64) {
   123  	var blkRead, blkWrite uint64
   124  	for _, bioEntry := range metrics.Blkio.IoServiceBytesRecursive {
   125  		if len(bioEntry.Op) == 0 {
   126  			continue
   127  		}
   128  		switch bioEntry.Op[0] {
   129  		case 'r', 'R':
   130  			blkRead = blkRead + bioEntry.Value
   131  		case 'w', 'W':
   132  			blkWrite = blkWrite + bioEntry.Value
   133  		}
   134  	}
   135  	return blkRead, blkWrite
   136  }
   137  
   138  func calculateCgroup2IO(metrics *v2.Metrics) (uint64, uint64) {
   139  	var ioRead, ioWrite uint64
   140  
   141  	for _, iOEntry := range metrics.Io.Usage {
   142  		if iOEntry.Rios == 0 && iOEntry.Wios == 0 {
   143  			continue
   144  		}
   145  
   146  		if iOEntry.Rios != 0 {
   147  			ioRead = ioRead + iOEntry.Rbytes
   148  		}
   149  
   150  		if iOEntry.Wios != 0 {
   151  			ioWrite = ioWrite + iOEntry.Wbytes
   152  		}
   153  	}
   154  
   155  	return ioRead, ioWrite
   156  }
   157  
   158  func calculateCgroupNetwork(links []netlink.Link) (float64, float64) {
   159  	var rx, tx float64
   160  
   161  	for _, l := range links {
   162  		stats := l.Attrs().Statistics
   163  		if stats != nil {
   164  			rx += float64(stats.RxBytes)
   165  			tx += float64(stats.TxBytes)
   166  		}
   167  	}
   168  	return rx, tx
   169  }