All checks were successful
ci / build (push) Successful in 30s
Add cgroup-based process grouping to the resource collector. Processes are grouped by their cgroup path, with container names resolved via configurable process-to-container mapping. New features: - Read cgroup info from /proc/[pid]/cgroup (supports v1 and v2) - Parse K8s resource notation (500m, 1Gi, etc.) for CPU/memory limits - Group metrics by container using CGROUP_PROCESS_MAP env var - Calculate usage percentages against limits from CGROUP_LIMITS env var - Output cgroup metrics with CPU cores used, memory RSS, and percentages Environment variables: - CGROUP_PROCESS_MAP: Map process names to container names for discovery - CGROUP_LIMITS: Define CPU/memory limits per container in K8s notation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
84 lines
2.3 KiB
Go
84 lines
2.3 KiB
Go
// ABOUTME: Configuration types and parsing for cgroup limits and process mapping.
|
|
// ABOUTME: Parses CGROUP_LIMITS and CGROUP_PROCESS_MAP environment variables.
|
|
package cgroup
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// CgroupLimit holds the resource limits for a container/cgroup
|
|
type CgroupLimit struct {
|
|
CPUCores float64 // CPU limit in cores (e.g., 0.5 for "500m", 2.0 for "2")
|
|
MemoryBytes uint64 // Memory limit in bytes
|
|
}
|
|
|
|
// CgroupLimits maps container names to their resource limits
|
|
type CgroupLimits map[string]CgroupLimit
|
|
|
|
// ProcessMapping maps process names to container names (for cgroup path discovery)
|
|
type ProcessMapping map[string]string
|
|
|
|
// CgroupPathMapping maps cgroup paths to container names (built at runtime)
|
|
type CgroupPathMapping map[string]string
|
|
|
|
// rawLimitEntry is the JSON structure for each entry in CGROUP_LIMITS
|
|
type rawLimitEntry struct {
|
|
CPU string `json:"cpu"`
|
|
Memory string `json:"memory"`
|
|
}
|
|
|
|
// ParseCgroupLimitsEnv parses the CGROUP_LIMITS environment variable.
|
|
// Expected format: {"container-name": {"cpu": "500m", "memory": "1Gi"}, ...}
|
|
func ParseCgroupLimitsEnv() (CgroupLimits, error) {
|
|
raw := os.Getenv("CGROUP_LIMITS")
|
|
if raw == "" {
|
|
return nil, nil // No limits configured
|
|
}
|
|
|
|
var parsed map[string]rawLimitEntry
|
|
if err := json.Unmarshal([]byte(raw), &parsed); err != nil {
|
|
return nil, fmt.Errorf("parsing CGROUP_LIMITS: %w", err)
|
|
}
|
|
|
|
limits := make(CgroupLimits)
|
|
for name, entry := range parsed {
|
|
var limit CgroupLimit
|
|
var err error
|
|
|
|
if entry.CPU != "" {
|
|
limit.CPUCores, err = ParseCPU(entry.CPU)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parsing CPU for %q: %w", name, err)
|
|
}
|
|
}
|
|
|
|
if entry.Memory != "" {
|
|
limit.MemoryBytes, err = ParseMemory(entry.Memory)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parsing memory for %q: %w", name, err)
|
|
}
|
|
}
|
|
|
|
limits[name] = limit
|
|
}
|
|
|
|
return limits, nil
|
|
}
|
|
|
|
// ParseProcessMappingEnv parses the CGROUP_PROCESS_MAP environment variable.
|
|
// Expected format: {"process-name": "container-name", ...}
|
|
func ParseProcessMappingEnv() (ProcessMapping, error) {
|
|
raw := os.Getenv("CGROUP_PROCESS_MAP")
|
|
if raw == "" {
|
|
return nil, nil // No mapping configured
|
|
}
|
|
|
|
var parsed map[string]string
|
|
if err := json.Unmarshal([]byte(raw), &parsed); err != nil {
|
|
return nil, fmt.Errorf("parsing CGROUP_PROCESS_MAP: %w", err)
|
|
}
|
|
|
|
return ProcessMapping(parsed), nil
|
|
}
|