Skip to content

feat: Add support for endpoint topology routing hints #2090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Aug 31, 2023
1 change: 1 addition & 0 deletions docs/endpointslice-metrics.md
Original file line number Diff line number Diff line change
@@ -6,5 +6,6 @@
| kube_endpointslice_info | Gauge | | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; | EXPERIMENTAL |
| kube_endpointslice_ports | Gauge | | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; <br> `port_name`=&lt;endpointslice-port-name&gt; <br> `port_protocol`=&lt;endpointslice-port-protocol&gt; <br> `port_number`=&lt;endpointslice-port-number&gt; | EXPERIMENTAL |
| kube_endpointslice_endpoints | Gauge | | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; <br> `ready`=&lt;endpointslice-ready&gt; <br> `serving`=&lt;endpointslice-serving&gt; <br> `terminating`=&lt;endpointslice-terminating&gt; <br> `hostname`=&lt;endpointslice-hostname&gt; <br> `targetref_kind`=&lt;endpointslice-targetref-kind&gt; <br> `targetref_name`=&lt;endpointslice-targetref-name&gt; <br> `targetref_namespace`=&lt;endpointslice-targetref-namespace&gt; <br> `nodename`=&lt;endpointslice-nodename&gt; <br> `endpoint_zone`=&lt;endpointslice-zone&gt; | EXPERIMENTAL |
| kube_endpointslice_endpoints_hints | Gauge | Each line is a hint applied to an endpoint-slice | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; <br> `address`=&lt;endpointslice-address[0]&gt; <br> `hostname`=&lt;endpointslice-hostname&gt; <br> `endpoint_zone`=&lt;endpointslice-zone&gt; <br> `hint`=&lt;endpointslice-hint&gt; | EXPERIMENTAL |
| kube_endpointslice_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](./cli-arguments.md) | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; <br> `label_ENDPOINTSLICE_LABEL`=&lt;ENDPOINTSLICE_LABEL&gt; | EXPERIMENTAL |
| kube_endpointslice_created | Gauge | | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; | EXPERIMENTAL |
54 changes: 53 additions & 1 deletion internal/store/endpointslice.go
Original file line number Diff line number Diff line change
@@ -74,6 +74,56 @@ func endpointSliceMetricFamilies(allowAnnotationsList, allowLabelsList []string)
}
}),
),
*generator.NewFamilyGeneratorWithStability(
"kube_endpointslice_endpoints_hints",
"Topology routing hints attached to endpoints",
metric.Gauge,
basemetrics.ALPHA,
"",
wrapEndpointSliceFunc(func(e *discoveryv1.EndpointSlice) *metric.Family {
m := []*metric.Metric{}
for _, ep := range e.Endpoints {
// Hint is populated when the endpoint is configured to be zone aware and preferentially route requests to its local zone.
// If there is no hint, skip this metric
if ep.Hints != nil && len(ep.Hints.ForZones) > 0 {
var (
labelKeys,
labelValues []string
)

if ep.Hostname != nil {
labelKeys = append(labelKeys, "hostname")
labelValues = append(labelValues, *ep.Hostname)
}

// Did you know that k8s will "assign" endpoints from a zone
// This field is useful for debugging weird network routing issues
if ep.Zone != nil {
labelKeys = append(labelKeys, "endpoint_zone")
labelValues = append(labelValues, *ep.Zone)
}

// Per Docs.
// This must contain at least one address but no more than
// 100. These are all assumed to be fungible and clients may choose to only
// use the first element. Refer to: https://issue.k8s.io/106267
labelKeys = append(labelKeys, "address")
labelValues = append(labelValues, ep.Addresses[0])

for _, zone := range ep.Hints.ForZones {
m = append(m, &metric.Metric{
LabelKeys: append(labelKeys, "hint"),
LabelValues: append(labelValues, zone.Name),
Value: 1,
})
}
}
}
return &metric.Family{
Metrics: m,
}
}),
),
*generator.NewFamilyGeneratorWithStability(
"kube_endpointslice_endpoints",
"Endpoints attached to the endpointslice.",
@@ -134,9 +184,11 @@ func endpointSliceMetricFamilies(allowAnnotationsList, allowLabelsList []string)
for _, address := range ep.Addresses {
newlabelValues := make([]string, len(labelValues))
copy(newlabelValues, labelValues)
newlabelValues = append(newlabelValues, address)

m = append(m, &metric.Metric{
LabelKeys: labelKeys,
LabelValues: append(newlabelValues, address),
LabelValues: newlabelValues,
Value: 1,
})
}
40 changes: 40 additions & 0 deletions internal/store/endpointslice_test.go
Original file line number Diff line number Diff line change
@@ -113,7 +113,9 @@ func TestEndpointSliceStore(t *testing.T) {
},
Want: `
# HELP kube_endpointslice_endpoints Endpoints attached to the endpointslice.
# HELP kube_endpointslice_endpoints_hints Topology routing hints attached to endpoints
# TYPE kube_endpointslice_endpoints gauge
# TYPE kube_endpointslice_endpoints_hints gauge
kube_endpointslice_endpoints{address="10.0.0.1",endpoint_nodename="node",endpoint_zone="west",endpointslice="test_endpointslice-endpoints",hostname="host",ready="true",terminating="false"} 1
kube_endpointslice_endpoints{address="192.168.1.10",endpoint_nodename="node",endpoint_zone="west",endpointslice="test_endpointslice-endpoints",hostname="host",ready="true",terminating="false"} 1
`,
@@ -122,6 +124,44 @@ func TestEndpointSliceStore(t *testing.T) {
"kube_endpointslice_endpoints",
},
},
{
Obj: &discoveryv1.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test_endpointslice-endpoints",
},
AddressType: "IPv4",
Endpoints: []discoveryv1.Endpoint{
{
NodeName: &nodename,
Conditions: discoveryv1.EndpointConditions{
Ready: &ready,
Terminating: &terminating,
},
Hostname: &hostname,
Zone: &zone,
Addresses: addresses,
Hints: &discoveryv1.EndpointHints{
ForZones: []discoveryv1.ForZone{
{Name: "zone1"},
},
},
},
},
},
Want: `
# HELP kube_endpointslice_endpoints Endpoints attached to the endpointslice.
# HELP kube_endpointslice_endpoints_hints Topology routing hints attached to endpoints
# TYPE kube_endpointslice_endpoints gauge
# TYPE kube_endpointslice_endpoints_hints gauge
kube_endpointslice_endpoints_hints{address="10.0.0.1",endpoint_zone="west",endpointslice="test_endpointslice-endpoints",hint="zone1",hostname="host"} 1
kube_endpointslice_endpoints{address="10.0.0.1",endpoint_nodename="node",endpoint_zone="west",endpointslice="test_endpointslice-endpoints",hostname="host",ready="true",terminating="false"} 1
kube_endpointslice_endpoints{address="192.168.1.10",endpoint_nodename="node",endpoint_zone="west",endpointslice="test_endpointslice-endpoints",hostname="host",ready="true",terminating="false"} 1
`,

MetricNames: []string{
"kube_endpointslice_endpoints",
},
},
{
AllowAnnotationsList: []string{
"foo",