diff --git a/docs/metrics/cluster/node-metrics.md b/docs/metrics/cluster/node-metrics.md
index 63d4d76b8..bb98d6571 100644
--- a/docs/metrics/cluster/node-metrics.md
+++ b/docs/metrics/cluster/node-metrics.md
@@ -12,5 +12,6 @@
| kube_node_status_addresses | Gauge | The addresses of a node | | `node`=<node-address>
`type`=<address-type>
`address`=<address-value> | EXPERIMENTAL |
| kube_node_status_allocatable | Gauge | The amount of resources allocatable for pods (after reserving some for system daemons) | `cpu`=<core>
`ephemeral_storage`=<byte>
`pods`=<integer>
`attachable_volumes_*`=<byte>
`hugepages_*`=<byte>
`memory`=<byte> | `node`=<node-address>
`resource`=<resource-name>
`unit`=<resource-unit> | STABLE |
| kube_node_status_condition | Gauge | The condition of a cluster node | | `node`=<node-address>
`condition`=<node-condition>
`status`=<true\|false\|unknown> | STABLE |
+| kube_node_status_images | Gauge | The Container Images that are present on the Node, including the size in bytes. | bytes | `node`=<node-address>
`image_digest`=<image_digest>
`image_name`=<image_name>
`image_size_bytes`=<image_size_bytes> | EXPERIMENTAL |
| kube_node_created | Gauge | Unix creation timestamp | seconds | `node`=<node-address> | STABLE |
| kube_node_deletion_timestamp | Gauge | Unix deletion timestamp | seconds | `node`=<node-address> | EXPERIMENTAL |
diff --git a/internal/store/node.go b/internal/store/node.go
index 7bd6e4aa8..0e4819f40 100644
--- a/internal/store/node.go
+++ b/internal/store/node.go
@@ -18,6 +18,7 @@ package store
import (
"context"
+ "strconv"
"strings"
basemetrics "k8s.io/component-base/metrics"
@@ -56,9 +57,43 @@ func nodeMetricFamilies(allowAnnotationsList, allowLabelsList []string) []genera
createNodeStatusCapacityFamilyGenerator(),
createNodeStatusConditionFamilyGenerator(),
createNodeStateAddressFamilyGenerator(),
+ createNodeStatusImagesFamilyGenerator(),
}
}
+func createNodeStatusImagesFamilyGenerator() generator.FamilyGenerator {
+ return *generator.NewFamilyGeneratorWithStability(
+ "kube_node_status_images",
+ "Container Images on the Node",
+ metric.Gauge,
+ basemetrics.ALPHA,
+ "",
+ wrapNodeFunc(func(n *v1.Node) *metric.Family {
+ ms := []*metric.Metric{}
+ for _, images := range n.Status.Images {
+ imageDigest := ""
+ imageName := ""
+
+ if len(images.Names) == 2 {
+ imageDigest = images.Names[0]
+ imageName = images.Names[1]
+ } else if len(images.Names) == 1 {
+ imageName = images.Names[0]
+ }
+
+ ms = append(ms, &metric.Metric{
+ LabelKeys: []string{"image_digest", "image_name", "image_size_bytes"},
+ LabelValues: []string{imageDigest, imageName, strconv.FormatInt(images.SizeBytes, 10)},
+ Value: 1,
+ })
+ }
+ return &metric.Family{
+ Metrics: ms,
+ }
+ }),
+ )
+}
+
func createNodeDeletionTimestampFamilyGenerator() generator.FamilyGenerator {
return *generator.NewFamilyGeneratorWithStability(
"kube_node_deletion_timestamp",
diff --git a/internal/store/node_test.go b/internal/store/node_test.go
index 5eb515a90..6dc27389a 100644
--- a/internal/store/node_test.go
+++ b/internal/store/node_test.go
@@ -386,6 +386,33 @@ func TestNodeStore(t *testing.T) {
"kube_node_created",
},
},
+ // verify image metrics
+ {
+ Obj: &v1.Node{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "127.0.0.1",
+ },
+ Status: v1.NodeStatus{
+ Images: []v1.ContainerImage{
+ {
+ Names: []string{"registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.13.0"},
+ SizeBytes: 253421,
+ },
+ {
+ Names: []string{"registry.k8s.io/busybox/busybox@sha256:5d462a2f5f5eae1e9a9a5dd0f8a0b7d5a5d12", "registry.k8s.io/busybox/busybox:v1.28.0"},
+ SizeBytes: 23342213,
+ },
+ },
+ },
+ },
+ Want: `
+ # HELP kube_node_status_images Container Images on the Node
+ # TYPE kube_node_status_images gauge
+ kube_node_status_images{image_digest="",image_name="registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.13.0",image_size_bytes="253421",node="127.0.0.1"} 1
+ kube_node_status_images{image_digest="registry.k8s.io/busybox/busybox@sha256:5d462a2f5f5eae1e9a9a5dd0f8a0b7d5a5d12",image_name="registry.k8s.io/busybox/busybox:v1.28.0",image_size_bytes="23342213",node="127.0.0.1"} 1
+ `,
+ MetricNames: []string{"kube_node_status_images"},
+ },
}
for i, c := range cases {
c.Func = generator.ComposeMetricGenFuncs(nodeMetricFamilies(nil, nil))