Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/metrics/service/service-metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
| kube_service_spec_external_ip | Gauge | Service external ips. One series for each ip | | `service`=&lt;service-name&gt; <br> `namespace`=&lt;service-namespace&gt; <br> `uid`=&lt;service-uid&gt; <br> `external_ip`=&lt;external-ip&gt; | STABLE |
| kube_service_status_load_balancer_ingress | Gauge | Service load balancer ingress status | | `service`=&lt;service-name&gt; <br> `namespace`=&lt;service-namespace&gt; <br> `uid`=&lt;service-uid&gt; <br> `ip`=&lt;load-balancer-ingress-ip&gt; <br> `hostname`=&lt;load-balancer-ingress-hostname&gt; | STABLE |
| kube_service_deletion_timestamp | Gauge | Unix deletion timestamp | seconds | `service`=&lt;service-name&gt; <br> `namespace`=&lt;service-namespace&gt; <br> `uid`=&lt;service-uid&gt; | EXPERIMENTAL |
| kube_service_ports | Gauge | Metric providing details about the ports exposed by services. | | `service`=&lt;service-name&gt; <br> `namespace`=&lt;service-namespace&gt; <br> `uid`=&lt;service-uid&gt; <br> `port_name`=&lt;service-port-name&gt; <br> `port_protocol`=&lt;service-port-protocol&gt; <br> `port_number`=&lt;service-port-number&gt; <br> `port_target`=&lt;service-port-target&gt; <br> `port_app_protocol`=&lt;service-port-appProtocol&gt; <br> `port_node_number`=&lt;service-node-port-number&gt; | EXPERIMENTAL |
44 changes: 44 additions & 0 deletions internal/store/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package store

import (
"context"
"strconv"

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -184,6 +185,49 @@ func serviceMetricFamilies(allowAnnotationsList, allowLabelsList []string) []gen
}
}),
),
*generator.NewFamilyGeneratorWithStability(
"kube_service_ports",
"Metric providing details about the ports exposed by services.",
metric.Gauge,
basemetrics.ALPHA,
"",
wrapSvcFunc(func(e *v1.Service) *metric.Family {
ms := []*metric.Metric{}
labelKeys := []string{"port_protocol", "port_number", "port_name", "port_target", "port_node_number", "port_app_protocol"}
Copy link
Contributor

@CatherineF-dev CatherineF-dev Mar 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has the cardinality issue.

Also, could you share what's the use case of this metric? Which alert will be added for this metric?

Copy link
Author

@nahuel11500 nahuel11500 Mar 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,

Thanks for your answer.

For our use case, we want to implement a dashboard that can show the different ports available on a service. And then connect more easily on them with Telepresence.

Regarding the cardinality issue, while we understand the concern, we believe it should be manageable.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CatherineF-dev we've the same requirement and would appreciate if this could be merged.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CatherineF-dev any update on this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you share more details on connecting more easily on them with Telepresence? Do we have other alternatives?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would allow us to expose a direct URL that corresponds to the service’s DSN inside the cluster. This means developers using Telepresence could simply click the link and be routed directly to the service.

Currently, given the metrics kube-state-metrics currently exposes, we don’t have any alternative way to achieve this. But if you can think of another way, I'll be glad to take some time to consider it.

Thanks :)

for _, port := range e.Spec.Ports {
appProtocol := ""
if port.AppProtocol != nil {
appProtocol = *port.AppProtocol
}
var labelValues []string
portNumber := strconv.FormatInt(int64(port.Port), 10)
targetPort := port.TargetPort.String()
nodePort := ""

if port.NodePort != 0 {
nodePort = strconv.FormatInt(int64(port.NodePort), 10)
}

labelValues = []string{
string(port.Protocol),
portNumber,
port.Name,
targetPort,
nodePort,
appProtocol,
}

ms = append(ms, &metric.Metric{
LabelValues: labelValues,
LabelKeys: labelKeys,
Value: 1,
})
}
return &metric.Family{
Metrics: ms,
}
}),
),
*generator.NewFamilyGeneratorWithStability(
"kube_service_deletion_timestamp",
"Unix deletion timestamp",
Expand Down
35 changes: 35 additions & 0 deletions internal/store/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"

generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator"
)
Expand All @@ -44,6 +45,8 @@ func TestServiceStore(t *testing.T) {
# TYPE kube_service_spec_external_ip gauge
# HELP kube_service_status_load_balancer_ingress [STABLE] Service load balancer ingress status
# TYPE kube_service_status_load_balancer_ingress gauge
# HELP kube_service_ports Metric providing details about the ports exposed by services.
# TYPE kube_service_ports gauge
# HELP kube_service_deletion_timestamp Unix deletion timestamp
# TYPE kube_service_deletion_timestamp gauge
`
Expand All @@ -70,11 +73,13 @@ func TestServiceStore(t *testing.T) {
# HELP kube_service_info [STABLE] Information about service.
# HELP kube_service_labels [STABLE] Kubernetes labels converted to Prometheus labels.
# HELP kube_service_spec_type [STABLE] Type about service.
# HELP kube_service_ports Metric providing details about the ports exposed by services.
# TYPE kube_service_annotations gauge
# TYPE kube_service_created gauge
# TYPE kube_service_info gauge
# TYPE kube_service_labels gauge
# TYPE kube_service_spec_type gauge
# TYPE kube_service_ports gauge
kube_service_created{namespace="default",service="test-service1",uid="uid1"} 1.5e+09
kube_service_info{cluster_ip="1.2.3.4",external_name="",external_traffic_policy="",load_balancer_ip="",namespace="default",service="test-service1",uid="uid1"} 1
kube_service_spec_type{namespace="default",service="test-service1",type="ClusterIP",uid="uid1"} 1
Expand All @@ -85,6 +90,7 @@ func TestServiceStore(t *testing.T) {
"kube_service_info",
"kube_service_labels",
"kube_service_spec_type",
"kube_service_ports",
},
},
{
Expand Down Expand Up @@ -261,6 +267,35 @@ func TestServiceStore(t *testing.T) {
kube_service_spec_type{namespace="default",service="test-service8",uid="uid8",type="LoadBalancer"} 1
`,
},

{
Obj: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-service9",
CreationTimestamp: metav1.Time{Time: time.Unix(1500000000, 0)},
Namespace: "default",
UID: "uid9",
Labels: map[string]string{
"app": "example9",
},
},
Spec: v1.ServiceSpec{
ClusterIP: "1.2.3.14",
LoadBalancerIP: "1.2.3.15",
Type: v1.ServiceTypeLoadBalancer,
ExternalTrafficPolicy: "Local",
Ports: []v1.ServicePort{
{Port: 80, Protocol: v1.ProtocolTCP, TargetPort: intstr.FromInt(8080), Name: "http", NodePort: 65000, AppProtocol: func(s string) *string { return &s }("grpc")},
},
},
},
Want: metadata + `
kube_service_created{namespace="default",service="test-service9",uid="uid9"} 1.5e+09
kube_service_info{cluster_ip="1.2.3.14",external_name="",external_traffic_policy="Local",load_balancer_ip="1.2.3.15",namespace="default",service="test-service9",uid="uid9"} 1
kube_service_spec_type{namespace="default",service="test-service9",uid="uid9",type="LoadBalancer"} 1
kube_service_ports{namespace="default",port_app_protocol="grpc",port_name="http",port_node_number="65000",port_number="80",port_protocol="TCP",port_target="8080",service="test-service9",uid="uid9"} 1
`,
},
{
Obj: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Expand Down