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))