From cdbbadfc73275c5d4291063976ab7498360d8195 Mon Sep 17 00:00:00 2001 From: Divik Chotani Date: Fri, 21 Aug 2026 22:24:29 +0000 Subject: [PATCH] Add zswap metrics: bytes_used and compression_efficiency --- config/system-stats-monitor.json | 6 ++ .../stackdriver/stackdriver_exporter.go | 92 ++++++++++--------- pkg/systemstatsmonitor/memory_collector.go | 39 ++++++-- .../memory_collector_test.go | 37 ++++++++ .../memory_collector_unix.go | 20 ++++ pkg/util/metrics/metric.go | 92 ++++++++++--------- test/e2e/metriconly/metrics_test.go | 2 + 7 files changed, 192 insertions(+), 96 deletions(-) diff --git a/config/system-stats-monitor.json b/config/system-stats-monitor.json index 56e480f66..316d96390 100644 --- a/config/system-stats-monitor.json +++ b/config/system-stats-monitor.json @@ -94,6 +94,12 @@ }, "memory/percent_used": { "displayName": "memory/percent_used" + }, + "memory/zswap_bytes_used": { + "displayName": "memory/zswap_bytes_used" + }, + "memory/zswap_compression_efficiency": { + "displayName": "memory/zswap_compression_efficiency" } } }, diff --git a/pkg/exporters/stackdriver/stackdriver_exporter.go b/pkg/exporters/stackdriver/stackdriver_exporter.go index 243601b93..d880ff76c 100644 --- a/pkg/exporters/stackdriver/stackdriver_exporter.go +++ b/pkg/exporters/stackdriver/stackdriver_exporter.go @@ -48,51 +48,53 @@ func init() { const exporterName = "stackdriver" var NPDMetricToSDMetric = map[metrics.MetricID]string{ - metrics.CPURunnableTaskCountID: "compute.googleapis.com/guest/cpu/runnable_task_count", - metrics.CPUUsageTimeID: "compute.googleapis.com/guest/cpu/usage_time", - metrics.CPULoad1m: "compute.googleapis.com/guest/cpu/load_1m", - metrics.CPULoad5m: "compute.googleapis.com/guest/cpu/load_5m", - metrics.CPULoad15m: "compute.googleapis.com/guest/cpu/load_15m", - metrics.DiskAvgQueueLenID: "compute.googleapis.com/guest/disk/queue_length", - metrics.DiskBytesUsedID: "compute.googleapis.com/guest/disk/bytes_used", - metrics.DiskPercentUsedID: "compute.googleapis.com/guest/disk/percent_used", - metrics.DiskIOTimeID: "compute.googleapis.com/guest/disk/io_time", - metrics.DiskMergedOpsCountID: "compute.googleapis.com/guest/disk/merged_operation_count", - metrics.DiskOpsBytesID: "compute.googleapis.com/guest/disk/operation_bytes_count", - metrics.DiskOpsCountID: "compute.googleapis.com/guest/disk/operation_count", - metrics.DiskOpsTimeID: "compute.googleapis.com/guest/disk/operation_time", - metrics.DiskWeightedIOID: "compute.googleapis.com/guest/disk/weighted_io_time", - metrics.HostUptimeID: "compute.googleapis.com/guest/system/uptime", - metrics.MemoryAnonymousUsedID: "compute.googleapis.com/guest/memory/anonymous_used", - metrics.MemoryBytesUsedID: "compute.googleapis.com/guest/memory/bytes_used", - metrics.MemoryDirtyUsedID: "compute.googleapis.com/guest/memory/dirty_used", - metrics.MemoryPageCacheUsedID: "compute.googleapis.com/guest/memory/page_cache_used", - metrics.MemoryUnevictableUsedID: "compute.googleapis.com/guest/memory/unevictable_used", - metrics.MemoryPercentUsedID: "compute.googleapis.com/guest/memory/percent_used", - metrics.ProblemCounterID: "compute.googleapis.com/guest/system/problem_count", - metrics.ProblemGaugeID: "compute.googleapis.com/guest/system/problem_state", - metrics.OSFeatureID: "compute.googleapis.com/guest/system/os_feature_enabled", - metrics.SystemProcessesTotal: "kubernetes.io/internal/node/guest/system/processes_total", - metrics.SystemProcsRunning: "kubernetes.io/internal/node/guest/system/procs_running", - metrics.SystemProcsBlocked: "kubernetes.io/internal/node/guest/system/procs_blocked", - metrics.SystemInterruptsTotal: "kubernetes.io/internal/node/guest/system/interrupts_total", - metrics.SystemCPUStat: "kubernetes.io/internal/node/guest/system/cpu_stat", - metrics.NetDevRxBytes: "kubernetes.io/internal/node/guest/net/rx_bytes", - metrics.NetDevRxPackets: "kubernetes.io/internal/node/guest/net/rx_packets", - metrics.NetDevRxErrors: "kubernetes.io/internal/node/guest/net/rx_errors", - metrics.NetDevRxDropped: "kubernetes.io/internal/node/guest/net/rx_dropped", - metrics.NetDevRxFifo: "kubernetes.io/internal/node/guest/net/rx_fifo", - metrics.NetDevRxFrame: "kubernetes.io/internal/node/guest/net/rx_frame", - metrics.NetDevRxCompressed: "kubernetes.io/internal/node/guest/net/rx_compressed", - metrics.NetDevRxMulticast: "kubernetes.io/internal/node/guest/net/rx_multicast", - metrics.NetDevTxBytes: "kubernetes.io/internal/node/guest/net/tx_bytes", - metrics.NetDevTxPackets: "kubernetes.io/internal/node/guest/net/tx_packets", - metrics.NetDevTxErrors: "kubernetes.io/internal/node/guest/net/tx_errors", - metrics.NetDevTxDropped: "kubernetes.io/internal/node/guest/net/tx_dropped", - metrics.NetDevTxFifo: "kubernetes.io/internal/node/guest/net/tx_fifo", - metrics.NetDevTxCollisions: "kubernetes.io/internal/node/guest/net/tx_collisions", - metrics.NetDevTxCarrier: "kubernetes.io/internal/node/guest/net/tx_carrier", - metrics.NetDevTxCompressed: "kubernetes.io/internal/node/guest/net/tx_compressed", + metrics.CPURunnableTaskCountID: "compute.googleapis.com/guest/cpu/runnable_task_count", + metrics.CPUUsageTimeID: "compute.googleapis.com/guest/cpu/usage_time", + metrics.CPULoad1m: "compute.googleapis.com/guest/cpu/load_1m", + metrics.CPULoad5m: "compute.googleapis.com/guest/cpu/load_5m", + metrics.CPULoad15m: "compute.googleapis.com/guest/cpu/load_15m", + metrics.DiskAvgQueueLenID: "compute.googleapis.com/guest/disk/queue_length", + metrics.DiskBytesUsedID: "compute.googleapis.com/guest/disk/bytes_used", + metrics.DiskPercentUsedID: "compute.googleapis.com/guest/disk/percent_used", + metrics.DiskIOTimeID: "compute.googleapis.com/guest/disk/io_time", + metrics.DiskMergedOpsCountID: "compute.googleapis.com/guest/disk/merged_operation_count", + metrics.DiskOpsBytesID: "compute.googleapis.com/guest/disk/operation_bytes_count", + metrics.DiskOpsCountID: "compute.googleapis.com/guest/disk/operation_count", + metrics.DiskOpsTimeID: "compute.googleapis.com/guest/disk/operation_time", + metrics.DiskWeightedIOID: "compute.googleapis.com/guest/disk/weighted_io_time", + metrics.HostUptimeID: "compute.googleapis.com/guest/system/uptime", + metrics.MemoryAnonymousUsedID: "compute.googleapis.com/guest/memory/anonymous_used", + metrics.MemoryBytesUsedID: "compute.googleapis.com/guest/memory/bytes_used", + metrics.MemoryDirtyUsedID: "compute.googleapis.com/guest/memory/dirty_used", + metrics.MemoryPageCacheUsedID: "compute.googleapis.com/guest/memory/page_cache_used", + metrics.MemoryUnevictableUsedID: "compute.googleapis.com/guest/memory/unevictable_used", + metrics.MemoryPercentUsedID: "compute.googleapis.com/guest/memory/percent_used", + metrics.MemoryZswapBytesUsedID: "compute.googleapis.com/guest/memory/zswap/bytes_used", + metrics.MemoryZswapCompressionEfficiencyID: "compute.googleapis.com/guest/memory/zswap/compression_efficiency", + metrics.ProblemCounterID: "compute.googleapis.com/guest/system/problem_count", + metrics.ProblemGaugeID: "compute.googleapis.com/guest/system/problem_state", + metrics.OSFeatureID: "compute.googleapis.com/guest/system/os_feature_enabled", + metrics.SystemProcessesTotal: "kubernetes.io/internal/node/guest/system/processes_total", + metrics.SystemProcsRunning: "kubernetes.io/internal/node/guest/system/procs_running", + metrics.SystemProcsBlocked: "kubernetes.io/internal/node/guest/system/procs_blocked", + metrics.SystemInterruptsTotal: "kubernetes.io/internal/node/guest/system/interrupts_total", + metrics.SystemCPUStat: "kubernetes.io/internal/node/guest/system/cpu_stat", + metrics.NetDevRxBytes: "kubernetes.io/internal/node/guest/net/rx_bytes", + metrics.NetDevRxPackets: "kubernetes.io/internal/node/guest/net/rx_packets", + metrics.NetDevRxErrors: "kubernetes.io/internal/node/guest/net/rx_errors", + metrics.NetDevRxDropped: "kubernetes.io/internal/node/guest/net/rx_dropped", + metrics.NetDevRxFifo: "kubernetes.io/internal/node/guest/net/rx_fifo", + metrics.NetDevRxFrame: "kubernetes.io/internal/node/guest/net/rx_frame", + metrics.NetDevRxCompressed: "kubernetes.io/internal/node/guest/net/rx_compressed", + metrics.NetDevRxMulticast: "kubernetes.io/internal/node/guest/net/rx_multicast", + metrics.NetDevTxBytes: "kubernetes.io/internal/node/guest/net/tx_bytes", + metrics.NetDevTxPackets: "kubernetes.io/internal/node/guest/net/tx_packets", + metrics.NetDevTxErrors: "kubernetes.io/internal/node/guest/net/tx_errors", + metrics.NetDevTxDropped: "kubernetes.io/internal/node/guest/net/tx_dropped", + metrics.NetDevTxFifo: "kubernetes.io/internal/node/guest/net/tx_fifo", + metrics.NetDevTxCollisions: "kubernetes.io/internal/node/guest/net/tx_collisions", + metrics.NetDevTxCarrier: "kubernetes.io/internal/node/guest/net/tx_carrier", + metrics.NetDevTxCompressed: "kubernetes.io/internal/node/guest/net/tx_compressed", } func getMetricTypeConversionFunction(customMetricPrefix string) func(*view.View) string { diff --git a/pkg/systemstatsmonitor/memory_collector.go b/pkg/systemstatsmonitor/memory_collector.go index 418ad3f1a..28bafe4cb 100644 --- a/pkg/systemstatsmonitor/memory_collector.go +++ b/pkg/systemstatsmonitor/memory_collector.go @@ -24,12 +24,14 @@ import ( ) type memoryCollector struct { - mBytesUsed *metrics.Int64Metric - mPercentUsed *metrics.Float64Metric - mAnonymousUsed *metrics.Int64Metric - mPageCacheUsed *metrics.Int64Metric - mUnevictableUsed *metrics.Int64Metric - mDirtyUsed *metrics.Int64Metric + mBytesUsed *metrics.Int64Metric + mPercentUsed *metrics.Float64Metric + mAnonymousUsed *metrics.Int64Metric + mPageCacheUsed *metrics.Int64Metric + mUnevictableUsed *metrics.Int64Metric + mDirtyUsed *metrics.Int64Metric + mZswapBytesUsed *metrics.Int64Metric + mZswapCompressionEfficiency *metrics.Float64Metric config *ssmtypes.MemoryStatsConfig } @@ -105,5 +107,30 @@ func NewMemoryCollectorOrDie(memoryConfig *ssmtypes.MemoryStatsConfig) *memoryCo klog.Fatalf("Error initializing metric for %q: %v", metrics.MemoryDirtyUsedID, err) } + if _, ok := memoryConfig.MetricsConfigs[string(metrics.MemoryZswapBytesUsedID)]; ok { + mc.mZswapBytesUsed, err = metrics.NewInt64Metric( + metrics.MemoryZswapBytesUsedID, + memoryConfig.MetricsConfigs[string(metrics.MemoryZswapBytesUsedID)].DisplayName, + "Zswap usage in bytes", + "Byte", + metrics.LastValue, + []string{}) + if err != nil { + klog.Fatalf("Error initializing metric for %q: %v", metrics.MemoryZswapBytesUsedID, err) + } + } + if _, ok := memoryConfig.MetricsConfigs[string(metrics.MemoryZswapCompressionEfficiencyID)]; ok { + mc.mZswapCompressionEfficiency, err = metrics.NewFloat64Metric( + metrics.MemoryZswapCompressionEfficiencyID, + memoryConfig.MetricsConfigs[string(metrics.MemoryZswapCompressionEfficiencyID)].DisplayName, + "Zswap compression efficiency ratio (uncompressed/compressed)", + "1", + metrics.LastValue, + []string{}) + if err != nil { + klog.Fatalf("Error initializing metric for %q: %v", metrics.MemoryZswapCompressionEfficiencyID, err) + } + } + return &mc } diff --git a/pkg/systemstatsmonitor/memory_collector_test.go b/pkg/systemstatsmonitor/memory_collector_test.go index 944b52b16..156bc055c 100644 --- a/pkg/systemstatsmonitor/memory_collector_test.go +++ b/pkg/systemstatsmonitor/memory_collector_test.go @@ -17,12 +17,49 @@ limitations under the License. package systemstatsmonitor import ( + "encoding/json" "testing" + "github.com/stretchr/testify/assert" ssmtypes "k8s.io/node-problem-detector/pkg/systemstatsmonitor/types" ) +const ( + fakeZswapConfig = ` +{ + "metricsConfigs": { + "memory/zswap_bytes_used": { + "displayName": "memory/zswap_bytes_used" + }, + "memory/zswap_compression_efficiency": { + "displayName": "memory/zswap_compression_efficiency" + } + } +} +` +) + func TestMemoryCollector(t *testing.T) { + // Original test mc := NewMemoryCollectorOrDie(&ssmtypes.MemoryStatsConfig{}) mc.collect() + + // Ensure zswap metrics are nil when not in config + assert.Nil(t, mc.mZswapBytesUsed) + assert.Nil(t, mc.mZswapCompressionEfficiency) +} + +func TestMemoryCollectorZswap(t *testing.T) { + cfg := &ssmtypes.MemoryStatsConfig{} + if err := json.Unmarshal([]byte(fakeZswapConfig), cfg); err != nil { + t.Fatalf("cannot load memory config: %s", err) + } + + mc := NewMemoryCollectorOrDie(cfg) + + // Ensure zswap metrics are initialized when in config + assert.NotNil(t, mc.mZswapBytesUsed) + assert.NotNil(t, mc.mZswapCompressionEfficiency) + + mc.collect() } diff --git a/pkg/systemstatsmonitor/memory_collector_unix.go b/pkg/systemstatsmonitor/memory_collector_unix.go index 51ed992e9..86f07960a 100644 --- a/pkg/systemstatsmonitor/memory_collector_unix.go +++ b/pkg/systemstatsmonitor/memory_collector_unix.go @@ -120,4 +120,24 @@ func (mc *memoryCollector) collect() { klog.Errorf("Failed to record unevictable used memory: %v", err) } } + + if mc.mZswapBytesUsed != nil { + var bytesUsed int64 + if meminfo.Zswap != nil && *meminfo.Zswap > 0 { + bytesUsed = int64(*meminfo.Zswap) * 1024 // kB -> B + } + if err := mc.mZswapBytesUsed.Record(map[string]string{}, bytesUsed); err != nil { + klog.Errorf("Failed to record zswap bytes used: %v", err) + } + } + + if mc.mZswapCompressionEfficiency != nil { + var efficiency float64 + if meminfo.Zswap != nil && *meminfo.Zswap > 0 && meminfo.Zswapped != nil { + efficiency = float64(*meminfo.Zswapped) / float64(*meminfo.Zswap) + } + if err := mc.mZswapCompressionEfficiency.Record(map[string]string{}, efficiency); err != nil { + klog.Errorf("Failed to record zswap compression efficiency: %v", err) + } + } } diff --git a/pkg/util/metrics/metric.go b/pkg/util/metrics/metric.go index d1122817f..ea8a8dbf4 100644 --- a/pkg/util/metrics/metric.go +++ b/pkg/util/metrics/metric.go @@ -20,51 +20,53 @@ import ( ) const ( - CPURunnableTaskCountID MetricID = "cpu/runnable_task_count" - CPUUsageTimeID MetricID = "cpu/usage_time" - CPULoad1m MetricID = "cpu/load_1m" - CPULoad5m MetricID = "cpu/load_5m" - CPULoad15m MetricID = "cpu/load_15m" - ProblemCounterID MetricID = "problem_counter" - ProblemGaugeID MetricID = "problem_gauge" - DiskIOTimeID MetricID = "disk/io_time" - DiskWeightedIOID MetricID = "disk/weighted_io" - DiskAvgQueueLenID MetricID = "disk/avg_queue_len" - DiskOpsCountID MetricID = "disk/operation_count" - DiskMergedOpsCountID MetricID = "disk/merged_operation_count" - DiskOpsBytesID MetricID = "disk/operation_bytes_count" - DiskOpsTimeID MetricID = "disk/operation_time" - DiskBytesUsedID MetricID = "disk/bytes_used" - DiskPercentUsedID MetricID = "disk/percent_used" - HostUptimeID MetricID = "host/uptime" - MemoryBytesUsedID MetricID = "memory/bytes_used" - MemoryAnonymousUsedID MetricID = "memory/anonymous_used" - MemoryPageCacheUsedID MetricID = "memory/page_cache_used" - MemoryUnevictableUsedID MetricID = "memory/unevictable_used" - MemoryDirtyUsedID MetricID = "memory/dirty_used" - MemoryPercentUsedID MetricID = "memory/percent_used" - OSFeatureID MetricID = "system/os_feature" - SystemProcessesTotal MetricID = "system/processes_total" - SystemProcsRunning MetricID = "system/procs_running" - SystemProcsBlocked MetricID = "system/procs_blocked" - SystemInterruptsTotal MetricID = "system/interrupts_total" - SystemCPUStat MetricID = "system/cpu_stat" - NetDevRxBytes MetricID = "net/rx_bytes" - NetDevRxPackets MetricID = "net/rx_packets" - NetDevRxErrors MetricID = "net/rx_errors" - NetDevRxDropped MetricID = "net/rx_dropped" - NetDevRxFifo MetricID = "net/rx_fifo" - NetDevRxFrame MetricID = "net/rx_frame" - NetDevRxCompressed MetricID = "net/rx_compressed" - NetDevRxMulticast MetricID = "net/rx_multicast" - NetDevTxBytes MetricID = "net/tx_bytes" - NetDevTxPackets MetricID = "net/tx_packets" - NetDevTxErrors MetricID = "net/tx_errors" - NetDevTxDropped MetricID = "net/tx_dropped" - NetDevTxFifo MetricID = "net/tx_fifo" - NetDevTxCollisions MetricID = "net/tx_collisions" - NetDevTxCarrier MetricID = "net/tx_carrier" - NetDevTxCompressed MetricID = "net/tx_compressed" + CPURunnableTaskCountID MetricID = "cpu/runnable_task_count" + CPUUsageTimeID MetricID = "cpu/usage_time" + CPULoad1m MetricID = "cpu/load_1m" + CPULoad5m MetricID = "cpu/load_5m" + CPULoad15m MetricID = "cpu/load_15m" + ProblemCounterID MetricID = "problem_counter" + ProblemGaugeID MetricID = "problem_gauge" + DiskIOTimeID MetricID = "disk/io_time" + DiskWeightedIOID MetricID = "disk/weighted_io" + DiskAvgQueueLenID MetricID = "disk/avg_queue_len" + DiskOpsCountID MetricID = "disk/operation_count" + DiskMergedOpsCountID MetricID = "disk/merged_operation_count" + DiskOpsBytesID MetricID = "disk/operation_bytes_count" + DiskOpsTimeID MetricID = "disk/operation_time" + DiskBytesUsedID MetricID = "disk/bytes_used" + DiskPercentUsedID MetricID = "disk/percent_used" + HostUptimeID MetricID = "host/uptime" + MemoryBytesUsedID MetricID = "memory/bytes_used" + MemoryAnonymousUsedID MetricID = "memory/anonymous_used" + MemoryPageCacheUsedID MetricID = "memory/page_cache_used" + MemoryUnevictableUsedID MetricID = "memory/unevictable_used" + MemoryDirtyUsedID MetricID = "memory/dirty_used" + MemoryPercentUsedID MetricID = "memory/percent_used" + MemoryZswapBytesUsedID MetricID = "memory/zswap_bytes_used" + MemoryZswapCompressionEfficiencyID MetricID = "memory/zswap_compression_efficiency" + OSFeatureID MetricID = "system/os_feature" + SystemProcessesTotal MetricID = "system/processes_total" + SystemProcsRunning MetricID = "system/procs_running" + SystemProcsBlocked MetricID = "system/procs_blocked" + SystemInterruptsTotal MetricID = "system/interrupts_total" + SystemCPUStat MetricID = "system/cpu_stat" + NetDevRxBytes MetricID = "net/rx_bytes" + NetDevRxPackets MetricID = "net/rx_packets" + NetDevRxErrors MetricID = "net/rx_errors" + NetDevRxDropped MetricID = "net/rx_dropped" + NetDevRxFifo MetricID = "net/rx_fifo" + NetDevRxFrame MetricID = "net/rx_frame" + NetDevRxCompressed MetricID = "net/rx_compressed" + NetDevRxMulticast MetricID = "net/rx_multicast" + NetDevTxBytes MetricID = "net/tx_bytes" + NetDevTxPackets MetricID = "net/tx_packets" + NetDevTxErrors MetricID = "net/tx_errors" + NetDevTxDropped MetricID = "net/tx_dropped" + NetDevTxFifo MetricID = "net/tx_fifo" + NetDevTxCollisions MetricID = "net/tx_collisions" + NetDevTxCarrier MetricID = "net/tx_carrier" + NetDevTxCompressed MetricID = "net/tx_compressed" ) var MetricMap MetricMapping diff --git a/test/e2e/metriconly/metrics_test.go b/test/e2e/metriconly/metrics_test.go index cf0db18f5..cf96f57c1 100644 --- a/test/e2e/metriconly/metrics_test.go +++ b/test/e2e/metriconly/metrics_test.go @@ -94,6 +94,8 @@ var _ = ginkgo.Describe("NPD should export Prometheus metrics.", func() { assertMetricExist(gotMetrics, "memory_unevictable_used", map[string]string{}, true) assertMetricExist(gotMetrics, "memory_dirty_used", map[string]string{}, false) assertMetricExist(gotMetrics, "memory_percent_used", map[string]string{}, false) + assertMetricExist(gotMetrics, "memory_zswap_bytes_used", map[string]string{}, false) + assertMetricExist(gotMetrics, "memory_zswap_compression_efficiency", map[string]string{}, false) assertMetricExist(gotMetrics, "host_uptime", map[string]string{}, false) assertMetricExist(gotMetrics, "system_os_feature", map[string]string{}, false) })