Skip to content

Commit d1612ee

Browse files
committed
refactor: replace count-based with explicit rule-based pod affinity metrics
- Replace 4 count-based metrics with single kube_deployment_spec_affinity metric - Add granular labels: affinity, type, topology_key, label_selector - Enable individual rule visibility and flexible querying - Update tests and documentation for new metric structure
1 parent d8cafe9 commit d1612ee

File tree

5 files changed

+74
-124
lines changed

5 files changed

+74
-124
lines changed

docs/metrics/workload/deployment-metrics.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@
1212
| kube_deployment_status_condition | Gauge | | `deployment`=&lt;deployment-name&gt; <br> `namespace`=&lt;deployment-namespace&gt; <br> `condition`=&lt;deployment-condition&gt; <br> `status`=&lt;true\|false\|unknown&gt; | STABLE |
1313
| kube_deployment_spec_replicas | Gauge | | `deployment`=&lt;deployment-name&gt; <br> `namespace`=&lt;deployment-namespace&gt; | STABLE |
1414
| kube_deployment_spec_paused | Gauge | | `deployment`=&lt;deployment-name&gt; <br> `namespace`=&lt;deployment-namespace&gt; | STABLE |
15-
| kube_deployment_spec_pod_affinity_preferred_rules | Gauge | Number of preferred pod affinity rules in the deployment's pod template | `deployment`=&lt;deployment-name&gt; <br> `namespace`=&lt;deployment-namespace&gt; | ALPHA |
16-
| kube_deployment_spec_pod_affinity_required_rules | Gauge | Number of required pod affinity rules in the deployment's pod template | `deployment`=&lt;deployment-name&gt; <br> `namespace`=&lt;deployment-namespace&gt; | ALPHA |
17-
| kube_deployment_spec_pod_anti_affinity_preferred_rules | Gauge | Number of preferred pod anti-affinity rules in the deployment's pod template | `deployment`=&lt;deployment-name&gt; <br> `namespace`=&lt;deployment-namespace&gt; | ALPHA |
18-
| kube_deployment_spec_pod_anti_affinity_required_rules | Gauge | Number of required pod anti-affinity rules in the deployment's pod template | `deployment`=&lt;deployment-name&gt; <br> `namespace`=&lt;deployment-namespace&gt; | ALPHA |
15+
| kube_deployment_spec_affinity | Gauge | Pod affinity and anti-affinity rules defined in the deployment's pod template specification | `deployment`=&lt;deployment-name&gt; <br> `namespace`=&lt;deployment-namespace&gt; <br> `affinity`=&lt;podaffinity\|podantiaffinity&gt; <br> `type`=&lt;requiredDuringSchedulingIgnoredDuringExecution\|preferredDuringSchedulingIgnoredDuringExecution&gt; <br> `topology_key`=&lt;topology-key&gt; <br> `label_selector`=&lt;selector-string&gt; | ALPHA |
1916
| kube_deployment_spec_strategy_rollingupdate_max_unavailable | Gauge | | `deployment`=&lt;deployment-name&gt; <br> `namespace`=&lt;deployment-namespace&gt; | STABLE |
2017
| kube_deployment_spec_strategy_rollingupdate_max_surge | Gauge | | `deployment`=&lt;deployment-name&gt; <br> `namespace`=&lt;deployment-namespace&gt; | STABLE |
2118
| kube_deployment_metadata_generation | Gauge | | `deployment`=&lt;deployment-name&gt; <br> `namespace`=&lt;deployment-namespace&gt; | STABLE |

examples/autosharding/kustomization.yaml

Lines changed: 0 additions & 13 deletions
This file was deleted.

examples/standard/kustomization.yaml

Lines changed: 0 additions & 10 deletions
This file was deleted.

internal/store/deployment.go

Lines changed: 68 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -268,85 +268,16 @@ func deploymentMetricFamilies(allowAnnotationsList, allowLabelsList []string) []
268268
}),
269269
),
270270
*generator.NewFamilyGeneratorWithStability(
271-
"kube_deployment_spec_pod_affinity_required_rules",
272-
"Number of required pod affinity rules in the deployment's pod template.",
271+
"kube_deployment_spec_affinity",
272+
"Pod affinity and anti-affinity rules defined in the deployment's pod template specification.",
273273
metric.Gauge,
274274
basemetrics.ALPHA,
275275
"",
276276
wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family {
277-
count := 0
278-
if d.Spec.Template.Spec.Affinity != nil && d.Spec.Template.Spec.Affinity.PodAffinity != nil {
279-
count = len(d.Spec.Template.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution)
280-
}
281-
return &metric.Family{
282-
Metrics: []*metric.Metric{
283-
{
284-
Value: float64(count),
285-
},
286-
},
287-
}
288-
}),
289-
),
290-
*generator.NewFamilyGeneratorWithStability(
291-
"kube_deployment_spec_pod_affinity_preferred_rules",
292-
"Number of preferred pod affinity rules in the deployment's pod template.",
293-
metric.Gauge,
294-
basemetrics.ALPHA,
295-
"",
296-
wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family {
297-
count := 0
298-
if d.Spec.Template.Spec.Affinity != nil && d.Spec.Template.Spec.Affinity.PodAffinity != nil {
299-
count = len(d.Spec.Template.Spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution)
300-
}
301-
return &metric.Family{
302-
Metrics: []*metric.Metric{
303-
{
304-
Value: float64(count),
305-
},
306-
},
307-
}
308-
}),
309-
),
310-
*generator.NewFamilyGeneratorWithStability(
311-
"kube_deployment_spec_pod_anti_affinity_required_rules",
312-
"Number of required pod anti-affinity rules in the deployment's pod template.",
313-
metric.Gauge,
314-
basemetrics.ALPHA,
315-
"",
316-
wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family {
317-
count := 0
318-
if d.Spec.Template.Spec.Affinity != nil && d.Spec.Template.Spec.Affinity.PodAntiAffinity != nil {
319-
count = len(d.Spec.Template.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution)
320-
}
321-
return &metric.Family{
322-
Metrics: []*metric.Metric{
323-
{
324-
Value: float64(count),
325-
},
326-
},
327-
}
328-
}),
329-
),
330-
*generator.NewFamilyGeneratorWithStability(
331-
"kube_deployment_spec_pod_anti_affinity_preferred_rules",
332-
"Number of preferred pod anti-affinity rules in the deployment's pod template.",
333-
metric.Gauge,
334-
basemetrics.ALPHA,
335-
"",
336-
wrapDeploymentFunc(func(d *v1.Deployment) *metric.Family {
337-
count := 0
338-
if d.Spec.Template.Spec.Affinity != nil && d.Spec.Template.Spec.Affinity.PodAntiAffinity != nil {
339-
count = len(d.Spec.Template.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution)
340-
}
341-
return &metric.Family{
342-
Metrics: []*metric.Metric{
343-
{
344-
Value: float64(count),
345-
},
346-
},
347-
}
277+
return generateDeploymentAffinityMetrics(d)
348278
}),
349279
),
280+
350281
*generator.NewFamilyGeneratorWithStability(
351282
"kube_deployment_metadata_generation",
352283
"Sequence number representing a specific generation of the desired state.",
@@ -436,3 +367,67 @@ func createDeploymentListWatch(kubeClient clientset.Interface, ns string, fieldS
436367
},
437368
}
438369
}
370+
func generateDeploymentAffinityMetrics(d *v1.Deployment) *metric.Family {
371+
var metrics []*metric.Metric
372+
373+
if d.Spec.Template.Spec.Affinity == nil {
374+
return &metric.Family{Metrics: metrics}
375+
}
376+
377+
// Handle pod affinity rules
378+
if d.Spec.Template.Spec.Affinity.PodAffinity != nil {
379+
// Required affinity rules
380+
for _, rule := range d.Spec.Template.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution {
381+
labelSelector := formatLabelSelector(rule.LabelSelector)
382+
metrics = append(metrics, &metric.Metric{
383+
LabelKeys: []string{"affinity", "type", "topology_key", "label_selector"},
384+
LabelValues: []string{"podaffinity", "requiredDuringSchedulingIgnoredDuringExecution", rule.TopologyKey, labelSelector},
385+
Value: 1,
386+
})
387+
}
388+
389+
// Preferred affinity rules
390+
for _, rule := range d.Spec.Template.Spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
391+
labelSelector := formatLabelSelector(rule.PodAffinityTerm.LabelSelector)
392+
metrics = append(metrics, &metric.Metric{
393+
LabelKeys: []string{"affinity", "type", "topology_key", "label_selector"},
394+
LabelValues: []string{"podaffinity", "preferredDuringSchedulingIgnoredDuringExecution", rule.PodAffinityTerm.TopologyKey, labelSelector},
395+
Value: 1,
396+
})
397+
}
398+
}
399+
400+
// Handle pod anti-affinity rules
401+
if d.Spec.Template.Spec.Affinity.PodAntiAffinity != nil {
402+
// Required anti-affinity rules
403+
for _, rule := range d.Spec.Template.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution {
404+
labelSelector := formatLabelSelector(rule.LabelSelector)
405+
metrics = append(metrics, &metric.Metric{
406+
LabelKeys: []string{"affinity", "type", "topology_key", "label_selector"},
407+
LabelValues: []string{"podantiaffinity", "requiredDuringSchedulingIgnoredDuringExecution", rule.TopologyKey, labelSelector},
408+
Value: 1,
409+
})
410+
}
411+
412+
// Preferred anti-affinity rules
413+
for _, rule := range d.Spec.Template.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
414+
labelSelector := formatLabelSelector(rule.PodAffinityTerm.LabelSelector)
415+
metrics = append(metrics, &metric.Metric{
416+
LabelKeys: []string{"affinity", "type", "topology_key", "label_selector"},
417+
LabelValues: []string{"podantiaffinity", "preferredDuringSchedulingIgnoredDuringExecution", rule.PodAffinityTerm.TopologyKey, labelSelector},
418+
Value: 1,
419+
})
420+
}
421+
}
422+
423+
return &metric.Family{Metrics: metrics}
424+
}
425+
426+
// formatLabelSelector converts a LabelSelector to a string representation
427+
func formatLabelSelector(selector *metav1.LabelSelector) string {
428+
if selector == nil {
429+
return ""
430+
}
431+
// Use Kubernetes helper function as suggested by @mrueg
432+
return metav1.FormatLabelSelector(selector)
433+
}

internal/store/deployment_test.go

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,8 @@ func TestDeploymentStore(t *testing.T) {
5151
# TYPE kube_deployment_metadata_generation gauge
5252
# HELP kube_deployment_spec_paused [STABLE] Whether the deployment is paused and will not be processed by the deployment controller.
5353
# TYPE kube_deployment_spec_paused gauge
54-
# HELP kube_deployment_spec_pod_affinity_preferred_rules Number of preferred pod affinity rules in the deployment's pod template.
55-
# TYPE kube_deployment_spec_pod_affinity_preferred_rules gauge
56-
# HELP kube_deployment_spec_pod_affinity_required_rules Number of required pod affinity rules in the deployment's pod template.
57-
# TYPE kube_deployment_spec_pod_affinity_required_rules gauge
58-
# HELP kube_deployment_spec_pod_anti_affinity_preferred_rules Number of preferred pod anti-affinity rules in the deployment's pod template.
59-
# TYPE kube_deployment_spec_pod_anti_affinity_preferred_rules gauge
60-
# HELP kube_deployment_spec_pod_anti_affinity_required_rules Number of required pod anti-affinity rules in the deployment's pod template.
61-
# TYPE kube_deployment_spec_pod_anti_affinity_required_rules gauge
54+
# HELP kube_deployment_spec_affinity Pod affinity and anti-affinity rules defined in the deployment's pod template specification.
55+
# TYPE kube_deployment_spec_affinity gauge
6256
# HELP kube_deployment_spec_replicas [STABLE] Number of desired pods for a deployment.
6357
# TYPE kube_deployment_spec_replicas gauge
6458
# HELP kube_deployment_status_replicas [STABLE] The number of replicas per deployment.
@@ -125,10 +119,6 @@ func TestDeploymentStore(t *testing.T) {
125119
kube_deployment_created{deployment="depl1",namespace="ns1"} 1.5e+09
126120
kube_deployment_metadata_generation{deployment="depl1",namespace="ns1"} 21
127121
kube_deployment_spec_paused{deployment="depl1",namespace="ns1"} 0
128-
kube_deployment_spec_pod_affinity_preferred_rules{deployment="depl1",namespace="ns1"} 0
129-
kube_deployment_spec_pod_affinity_required_rules{deployment="depl1",namespace="ns1"} 0
130-
kube_deployment_spec_pod_anti_affinity_preferred_rules{deployment="depl1",namespace="ns1"} 0
131-
kube_deployment_spec_pod_anti_affinity_required_rules{deployment="depl1",namespace="ns1"} 0
132122
kube_deployment_spec_replicas{deployment="depl1",namespace="ns1"} 200
133123
kube_deployment_spec_strategy_rollingupdate_max_surge{deployment="depl1",namespace="ns1"} 10
134124
kube_deployment_spec_strategy_rollingupdate_max_unavailable{deployment="depl1",namespace="ns1"} 10
@@ -204,10 +194,9 @@ func TestDeploymentStore(t *testing.T) {
204194
Want: metadata + `
205195
kube_deployment_metadata_generation{deployment="depl-with-affinity",namespace="ns1"} 1
206196
kube_deployment_spec_paused{deployment="depl-with-affinity",namespace="ns1"} 0
207-
kube_deployment_spec_pod_affinity_preferred_rules{deployment="depl-with-affinity",namespace="ns1"} 1
208-
kube_deployment_spec_pod_affinity_required_rules{deployment="depl-with-affinity",namespace="ns1"} 1
209-
kube_deployment_spec_pod_anti_affinity_preferred_rules{deployment="depl-with-affinity",namespace="ns1"} 0
210-
kube_deployment_spec_pod_anti_affinity_required_rules{deployment="depl-with-affinity",namespace="ns1"} 1
197+
kube_deployment_spec_affinity{deployment="depl-with-affinity",namespace="ns1",affinity="podaffinity",type="requiredDuringSchedulingIgnoredDuringExecution",topology_key="kubernetes.io/zone",label_selector="app=cache"} 1
198+
kube_deployment_spec_affinity{deployment="depl-with-affinity",namespace="ns1",affinity="podaffinity",type="preferredDuringSchedulingIgnoredDuringExecution",topology_key="kubernetes.io/hostname",label_selector="app=web"} 1
199+
kube_deployment_spec_affinity{deployment="depl-with-affinity",namespace="ns1",affinity="podantiaffinity",type="requiredDuringSchedulingIgnoredDuringExecution",topology_key="kubernetes.io/hostname",label_selector="app=depl-with-affinity"} 1
211200
kube_deployment_spec_replicas{deployment="depl-with-affinity",namespace="ns1"} 3
212201
kube_deployment_status_observed_generation{deployment="depl-with-affinity",namespace="ns1"} 1
213202
kube_deployment_status_replicas_available{deployment="depl-with-affinity",namespace="ns1"} 3
@@ -244,10 +233,6 @@ func TestDeploymentStore(t *testing.T) {
244233
Want: metadata + `
245234
kube_deployment_metadata_generation{deployment="depl-no-affinity",namespace="ns1"} 1
246235
kube_deployment_spec_paused{deployment="depl-no-affinity",namespace="ns1"} 0
247-
kube_deployment_spec_pod_affinity_preferred_rules{deployment="depl-no-affinity",namespace="ns1"} 0
248-
kube_deployment_spec_pod_affinity_required_rules{deployment="depl-no-affinity",namespace="ns1"} 0
249-
kube_deployment_spec_pod_anti_affinity_preferred_rules{deployment="depl-no-affinity",namespace="ns1"} 0
250-
kube_deployment_spec_pod_anti_affinity_required_rules{deployment="depl-no-affinity",namespace="ns1"} 0
251236
kube_deployment_spec_replicas{deployment="depl-no-affinity",namespace="ns1"} 2
252237
kube_deployment_status_observed_generation{deployment="depl-no-affinity",namespace="ns1"} 1
253238
kube_deployment_status_replicas_available{deployment="depl-no-affinity",namespace="ns1"} 2
@@ -294,10 +279,6 @@ func TestDeploymentStore(t *testing.T) {
294279
Want: metadata + `
295280
kube_deployment_metadata_generation{deployment="depl2",namespace="ns2"} 14
296281
kube_deployment_spec_paused{deployment="depl2",namespace="ns2"} 1
297-
kube_deployment_spec_pod_affinity_preferred_rules{deployment="depl2",namespace="ns2"} 0
298-
kube_deployment_spec_pod_affinity_required_rules{deployment="depl2",namespace="ns2"} 0
299-
kube_deployment_spec_pod_anti_affinity_preferred_rules{deployment="depl2",namespace="ns2"} 0
300-
kube_deployment_spec_pod_anti_affinity_required_rules{deployment="depl2",namespace="ns2"} 0
301282
kube_deployment_spec_replicas{deployment="depl2",namespace="ns2"} 5
302283
kube_deployment_spec_strategy_rollingupdate_max_surge{deployment="depl2",namespace="ns2"} 1
303284
kube_deployment_spec_strategy_rollingupdate_max_unavailable{deployment="depl2",namespace="ns2"} 1

0 commit comments

Comments
 (0)