Files
beszel-ipv6/internal/entities/container/container.go

200 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package container
import "time"
// Docker container info from /containers/json
type ApiInfo struct {
Id string
IdShort string
Names []string
Status string
State string
Image string
Health struct {
Status string
// FailingStreak int
}
Ports []struct {
// PrivatePort uint16
PublicPort uint16
IP string
// Type string
}
// ImageID string
// Command string
// Created int64
// SizeRw int64 `json:",omitempty"`
// SizeRootFs int64 `json:",omitempty"`
// Labels map[string]string
// HostConfig struct {
// NetworkMode string `json:",omitempty"`
// Annotations map[string]string `json:",omitempty"`
// }
// NetworkSettings *SummaryNetworkSettings
// Mounts []MountPoint
}
// Docker container resources from /containers/{id}/stats
type ApiStats struct {
Read time.Time `json:"read"` // Time of stats generation
NumProcs uint32 `json:"num_procs,omitzero"` // Windows specific, not populated on Linux.
Networks map[string]NetworkStats
CPUStats CPUStats `json:"cpu_stats"`
MemoryStats MemoryStats `json:"memory_stats"`
}
// Docker system info from /info API endpoint
type HostInfo struct {
OperatingSystem string `json:"OperatingSystem"`
KernelVersion string `json:"KernelVersion"`
NCPU int `json:"NCPU"`
MemTotal uint64 `json:"MemTotal"`
}
func (s *ApiStats) CalculateCpuPercentLinux(prevCpuContainer uint64, prevCpuSystem uint64) float64 {
// A counter can read lower than the stored previous value when a stats
// response is processed after a newer one for the same container, or when an
// accounting counter resets. Unsigned subtraction wraps to ~2^64 instead of
// going negative: on the container counter that surfaces as an absurd
// percentage the caller rejects, discarding the whole sample; on the system
// counter it inflates the divisor and silently reports near-zero CPU.
// Treat either direction as a new baseline.
if s.CPUStats.CPUUsage.TotalUsage < prevCpuContainer || s.CPUStats.SystemUsage < prevCpuSystem {
return 0.0
}
cpuDelta := s.CPUStats.CPUUsage.TotalUsage - prevCpuContainer
systemDelta := s.CPUStats.SystemUsage - prevCpuSystem
// Avoid division by zero and handle first run case
if systemDelta == 0 || prevCpuContainer == 0 {
return 0.0
}
return float64(cpuDelta) / float64(systemDelta) * 100.0
}
// CalculateCpuPercentPodman calculates CPU percentage for Podman containers.
// Podman populates system_cpu_usage from cgroup cpu.stat rather than /proc/stat, so it
// represents only cgroup-accounted activity, not total host CPU capacity. Using it as
// a denominator inflates the result. Instead we use elapsed wall-clock time × online_cpus,
// matching the approach used for Windows and recommended in:
// https://github.com/henrygd/beszel/issues/2049
func (s *ApiStats) CalculateCpuPercentPodman(prevCpuContainer uint64, prevRead time.Time) float64 {
if prevCpuContainer == 0 || s.CPUStats.OnlineCPUs == 0 {
return 0.0
}
// Treat a reset or out-of-order counter as a new baseline instead of
// allowing unsigned subtraction to wrap to an enormous percentage.
if s.CPUStats.CPUUsage.TotalUsage < prevCpuContainer {
return 0.0
}
cpuDelta := s.CPUStats.CPUUsage.TotalUsage - prevCpuContainer
elapsedNs := uint64(s.Read.Sub(prevRead).Nanoseconds())
systemCapacity := elapsedNs * uint64(s.CPUStats.OnlineCPUs)
if systemCapacity == 0 {
return 0.0
}
return float64(cpuDelta) / float64(systemCapacity) * 100.0
}
// from: https://github.com/docker/cli/blob/master/cli/command/container/stats_helpers.go#L185
func (s *ApiStats) CalculateCpuPercentWindows(prevCpuUsage uint64, prevRead time.Time) float64 {
// Max number of 100ns intervals between the previous time read and now
possIntervals := uint64(s.Read.Sub(prevRead).Nanoseconds())
possIntervals /= 100 // Convert to number of 100ns intervals
possIntervals *= uint64(s.NumProcs) // Multiple by the number of processors
// Intervals used. Same rollback guard as the Linux path: an out-of-order or
// reset counter would wrap the subtraction to ~2^64.
if s.CPUStats.CPUUsage.TotalUsage < prevCpuUsage {
return 0.0
}
intervalsUsed := s.CPUStats.CPUUsage.TotalUsage - prevCpuUsage
// Percentage avoiding divide-by-zero
if possIntervals > 0 {
return float64(intervalsUsed) / float64(possIntervals) * 100.0
}
return 0.00
}
type CPUStats struct {
// CPU Usage. Linux and Windows.
CPUUsage CPUUsage `json:"cpu_usage"`
// System Usage. Linux only. Populated from /proc/stat on Docker; from cgroup cpu.stat on Podman.
SystemUsage uint64 `json:"system_cpu_usage,omitempty"`
// Number of online CPUs. Linux only. Used by Podman for time-based CPU calculation.
OnlineCPUs uint32 `json:"online_cpus,omitempty"`
}
type CPUUsage struct {
// Total CPU time consumed.
// Units: nanoseconds (Linux)
// Units: 100's of nanoseconds (Windows)
TotalUsage uint64 `json:"total_usage"`
}
type MemoryStats struct {
// current res_counter usage for memory
Usage uint64 `json:"usage,omitempty"`
// all the stats exported via memory.stat.
Stats MemoryStatsStats `json:"stats"`
// private working set (Windows only)
PrivateWorkingSet uint64 `json:"privateworkingset,omitempty"`
}
type MemoryStatsStats struct {
Cache uint64 `json:"cache,omitempty"`
InactiveFile uint64 `json:"inactive_file,omitempty"`
}
type NetworkStats struct {
// Bytes received. Windows and Linux.
RxBytes uint64 `json:"rx_bytes"`
// Bytes sent. Windows and Linux.
TxBytes uint64 `json:"tx_bytes"`
}
type prevNetStats struct {
Sent uint64
Recv uint64
}
type DockerHealth = uint8
const (
DockerHealthNone DockerHealth = iota
DockerHealthStarting
DockerHealthHealthy
DockerHealthUnhealthy
)
var DockerHealthStrings = map[string]DockerHealth{
"none": DockerHealthNone,
"starting": DockerHealthStarting,
"healthy": DockerHealthHealthy,
"unhealthy": DockerHealthUnhealthy,
}
// Docker container stats
type Stats struct {
Name string `json:"n" cbor:"0,keyasint"`
Cpu float64 `json:"c" cbor:"1,keyasint"`
Mem float64 `json:"m" cbor:"2,keyasint"`
NetworkSent float64 `json:"ns,omitzero" cbor:"3,keyasint,omitzero"` // deprecated 0.18.3 (MB) - keep field for old agents/records
NetworkRecv float64 `json:"nr,omitzero" cbor:"4,keyasint,omitzero"` // deprecated 0.18.3 (MB) - keep field for old agents/records
Bandwidth [2]uint64 `json:"b,omitzero" cbor:"9,keyasint,omitzero"` // [sent bytes, recv bytes]
Health DockerHealth `json:"-" cbor:"5,keyasint"`
Status string `json:"-" cbor:"6,keyasint"`
Id string `json:"-" cbor:"7,keyasint"`
Image string `json:"-" cbor:"8,keyasint"`
Ports string `json:"-" cbor:"10,keyasint"`
// PrevCpu [2]uint64 `json:"-"`
CpuSystem uint64 `json:"-"`
CpuContainer uint64 `json:"-"`
PrevNet prevNetStats `json:"-"`
PrevReadTime time.Time `json:"-"`
}