-
Notifications
You must be signed in to change notification settings - Fork 268
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
Implement Prometheus metrics for LocalQueue #3673
base: main
Are you sure you want to change the base?
Changes from 5 commits
5a78a30
3901168
a2c952d
9493af3
671f425
a59534e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -237,6 +237,11 @@ func (c *clusterQueue) updateQueueStatus() { | |
if status != c.Status { | ||
c.Status = status | ||
metrics.ReportClusterQueueStatus(c.Name, c.Status) | ||
if features.Enabled(features.LocalQueueMetrics) { | ||
for _, lq := range c.localQueues { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This iteration might be adding unnecessary performance cost. What is the scenario that it needs calling here? Maybe we could move the call per LQ, when we update the specific LQ. PTAL. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The lq status is equal to the cq status. So when the cq status updates, all the cq's associated lqs should have their statuses updated as well |
||
metrics.ReportLocalQueueStatus(metrics.LQRefFromLocalQueueKey(lq.key), c.Status) | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
@@ -500,6 +505,12 @@ func (c *clusterQueue) reportActiveWorkloads() { | |
metrics.ReservingActiveWorkloads.WithLabelValues(c.Name).Set(float64(len(c.Workloads))) | ||
} | ||
|
||
func (q *queue) reportActiveWorkloads() { | ||
qKeySlice := strings.Split(q.key, "/") | ||
metrics.LocalQueueAdmittedActiveWorkloads.WithLabelValues(qKeySlice[1], qKeySlice[0]).Set(float64(q.admittedWorkloads)) | ||
metrics.LocalQueueReservingActiveWorkloads.WithLabelValues(qKeySlice[1], qKeySlice[0]).Set(float64(q.reservingWorkloads)) | ||
} | ||
|
||
// updateWorkloadUsage updates the usage of the ClusterQueue for the workload | ||
// and the number of admitted workloads for local queues. | ||
func (c *clusterQueue) updateWorkloadUsage(wi *workload.Info, m int64) { | ||
|
@@ -537,6 +548,9 @@ func (c *clusterQueue) updateWorkloadUsage(wi *workload.Info, m int64) { | |
updateFlavorUsage(frUsage, lq.admittedUsage, m) | ||
lq.admittedWorkloads += int(m) | ||
} | ||
if features.Enabled(features.LocalQueueMetrics) { | ||
lq.reportActiveWorkloads() | ||
} | ||
} | ||
} | ||
|
||
|
@@ -581,11 +595,18 @@ func (c *clusterQueue) addLocalQueue(q *kueue.LocalQueue) error { | |
} | ||
} | ||
c.localQueues[qKey] = qImpl | ||
if features.Enabled(features.LocalQueueMetrics) { | ||
qImpl.reportActiveWorkloads() | ||
metrics.ReportLocalQueueStatus(metrics.LQRefFromLocalQueueKey(qKey), c.Status) | ||
} | ||
return nil | ||
} | ||
|
||
func (c *clusterQueue) deleteLocalQueue(q *kueue.LocalQueue) { | ||
qKey := queueKey(q) | ||
if features.Enabled(features.LocalQueueMetrics) { | ||
metrics.ClearLocalQueueCacheMetrics(metrics.LQRefFromLocalQueueKey(qKey)) | ||
} | ||
delete(c.localQueues, qKey) | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason to favor API rather than a feature gate? We don't guard other metrics by API. So, I don't see such a need, but let us know if there is something specific about them. If the concern is stability of the system due to potential bugs, then feature gate is enough, we can start from alpha. It would also allow us to simplify the code as feature gate status can be checked from any place, so no need to pass parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I very much agree, especially when it comes to passing parameters
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a comment about increasing cardinality and wanting to leave this behind a long term config field
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, but in that case I would like to go via the KEP process. Pity the comment does not mention why cardinality is a problem - is it for usability (this could be solved by aggregation), or performance. Do you have some other references why cardinality might be a problem in k8s.
I assume we don't have many more LQs than namepaces, which also let me check what we do in the core k8s. I see that we have metrics depending on Namespace, example. However, in this case we use explicitly CounterOpts.Namespace. Maybe we could also do it this way? PTAL.
If you want this feature in 0.10 I think the only chance is a short KEP, don't change API, and guard it by Alpha feature gate (disabled by default). Then for second iteration of alpha investigate if we need the API switch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
namespace
in the example you link isn't a K8 namespace from what I understand. It is the project namespace to avoidprometheus
metrics clashingThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, I thought I found such an example in the k8s l, but I was wrong.. seeing no such metrics in k8s suggests that indeed it might be better not to multiply the metrics by namespace. DISCLAIMER: I haven't done extensive search, just looked at a couple places
Since we have such a use case in Kueue I would be ok with the API knob, but anyway a KEP would be useful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://kubernetes.io/docs/reference/instrumentation/metrics/
There's a few metrics here with a namespace label.
There's an existing KEP that I was having a bit of trouble implementing since it included the ability to use namespace/
local_queue
selectors for metric collection.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, are these metrics opt-in or enabled by default? If k8s core enables them by default I don't think we need to worry. I would like to better understand why cardinality is a problem basically
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EDIT: to be clear I'm hesitant, maybe it is actually ok to just preemptively prevent very large outputs from the metrics endpoint. So, maybe the API is fine, I will look tomorrow. Cc @tenzen-y.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a handful that have graduated to stable and about a dozen that are
alpha