This is an automated email from the ASF dual-hosted git repository.

cryptoe pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new b89f5d7e9ac feat: For k8s-overlord-extensions users, emit the pod 
template as a dimension for peon metrics (#20001)
b89f5d7e9ac is described below

commit b89f5d7e9acc89729a0f3c6c8647141f37c67c3a
Author: Lucas Capistrant <[email protected]>
AuthorDate: Fri Aug 14 09:57:02 2026 -0500

    feat: For k8s-overlord-extensions users, emit the pod template as a 
dimension for peon metrics (#20001)
    
    * For k8s-overlord-extensions users, emit the pod template as a dimension 
for peon metrics
    
    * more test
---
 docs/development/extensions-core/k8s-jobs.md       |  8 ++
 .../k8s/overlord/KubernetesPeonMetricsModule.java  | 81 ++++++++++++++++++
 .../k8s/overlord/common/DruidK8sConstants.java     |  2 +
 .../taskadapter/PodTemplateTaskAdapter.java        | 10 ++-
 .../org.apache.druid.initialization.DruidModule    |  1 +
 .../overlord/KubernetesPeonMetricsModuleTest.java  | 96 ++++++++++++++++++++++
 .../src/test/resources/expectedNoopJobLongIds.yaml |  4 +
 .../test/resources/expectedNoopJobNoTaskJson.yaml  |  4 +
 .../resources/expectedNoopJobTlsEnabledBase.yaml   |  4 +
 9 files changed, 209 insertions(+), 1 deletion(-)

diff --git a/docs/development/extensions-core/k8s-jobs.md 
b/docs/development/extensions-core/k8s-jobs.md
index 8ba0e215922..242d17a9924 100644
--- a/docs/development/extensions-core/k8s-jobs.md
+++ b/docs/development/extensions-core/k8s-jobs.md
@@ -844,6 +844,14 @@ Set the `podTemplateSelectionKey` key in a task's context 
to pick a configured p
 
 This is gated by the runtime property 
`druid.indexer.runner.allowTaskPodTemplateSelection`, which defaults to 
`false`. If the key doesn't match any configured template, the task fails to 
launch.
 
+##### Pod template metrics dimension
+
+Every metric emitted by a task pod carries a `podTemplate` dimension naming 
the pod template the pod runs under, alongside the existing `taskType`, 
`dataSource`, `taskId`, and `groupId` dimensions. Use it to group metrics by 
task type and pod template, for example to see which task types run on which 
templates.
+
+The dimension reflects the template actually applied to the pod, whichever 
selection strategy or context override chose it. Druid passes the name to the 
pod through the `DRUID_POD_TEMPLATE` environment variable, sourced from the 
pod's own `task.jobTemplate` annotation; you don't need to declare it in your 
pod templates.
+
+The dimension requires `druid-kubernetes-overlord-extensions` in the task 
pod's `druid.extensions.loadList`. Task pods that have no pod template, such as 
those launched by another task adapter, omit the dimension.
+
 #### Running Task Pods in Another Namespace
 
 It is possible to run task pods in a different namespace from the rest of your 
Druid cluster.
diff --git 
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesPeonMetricsModule.java
 
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesPeonMetricsModule.java
new file mode 100644
index 00000000000..f22954297ed
--- /dev/null
+++ 
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesPeonMetricsModule.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.k8s.overlord;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.inject.Binder;
+import com.google.inject.multibindings.MapBinder;
+import org.apache.druid.discovery.NodeRole;
+import org.apache.druid.guice.annotations.LoadScope;
+import org.apache.druid.initialization.DruidModule;
+import org.apache.druid.java.util.common.logger.Logger;
+import org.apache.druid.k8s.overlord.common.DruidK8sConstants;
+import org.apache.druid.server.emitter.ExtraServiceDimensions;
+
+import javax.annotation.Nullable;
+
+/**
+ * Tags every metric a task pod emits with the pod template it runs under, so 
metrics can be grouped
+ * by pod template alongside the task dimensions the peon already reports.
+ * <p>
+ * The template name arrives in {@link DruidK8sConstants#POD_TEMPLATE_ENV}, 
which
+ * {@link org.apache.druid.k8s.overlord.taskadapter.PodTemplateTaskAdapter} 
populates from the pod's
+ * own {@link DruidK8sConstants#TASK_JOB_TEMPLATE} annotation. Peons launched 
without a pod template,
+ * such as those from another task adapter, leave the variable unset and emit 
no extra dimension.
+ */
+@LoadScope(roles = NodeRole.PEON_JSON_NAME)
+public class KubernetesPeonMetricsModule implements DruidModule
+{
+  private static final Logger log = new 
Logger(KubernetesPeonMetricsModule.class);
+
+  @Override
+  public void configure(Binder binder)
+  {
+    final MapBinder<String, String> extraServiceDimensions = 
MapBinder.newMapBinder(
+        binder,
+        String.class,
+        String.class,
+        ExtraServiceDimensions.class
+    );
+
+    final String podTemplate = getPodTemplateName();
+    if (podTemplate == null || podTemplate.isEmpty()) {
+      log.debug(
+          "Env variable [%s] is not set, so metrics will not carry a [%s] 
dimension.",
+          DruidK8sConstants.POD_TEMPLATE_ENV,
+          DruidK8sConstants.POD_TEMPLATE_DIMENSION
+      );
+      return;
+    }
+
+    log.info("Emitting metrics with dimension [%s] set to [%s].", 
DruidK8sConstants.POD_TEMPLATE_DIMENSION, podTemplate);
+    
extraServiceDimensions.addBinding(DruidK8sConstants.POD_TEMPLATE_DIMENSION).toInstance(podTemplate);
+  }
+
+  /**
+   * Overridden by tests, which cannot set an environment variable on the 
running process.
+   */
+  @VisibleForTesting
+  @Nullable
+  String getPodTemplateName()
+  {
+    return System.getenv(DruidK8sConstants.POD_TEMPLATE_ENV);
+  }
+}
diff --git 
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/common/DruidK8sConstants.java
 
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/common/DruidK8sConstants.java
index f0eba8c63e6..bd43dfbb57d 100644
--- 
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/common/DruidK8sConstants.java
+++ 
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/common/DruidK8sConstants.java
@@ -35,6 +35,8 @@ public class DruidK8sConstants
   public static final String DEFAULT_JAVA_HEAP_SIZE = "1G";
   public static final String TLS_ENABLED = "tls.enabled";
   public static final String TASK_JSON_ENV = "TASK_JSON";
+  public static final String POD_TEMPLATE_ENV = "DRUID_POD_TEMPLATE";
+  public static final String POD_TEMPLATE_DIMENSION = "podTemplate";
   public static final String TASK_DIR_ENV = "TASK_DIR";
   public static final String TASK_ID_ENV = "TASK_ID";
   public static final String LOAD_BROADCAST_DATASOURCE_MODE_ENV = 
"LOAD_BROADCAST_DATASOURCE_MODE";
diff --git 
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/taskadapter/PodTemplateTaskAdapter.java
 
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/taskadapter/PodTemplateTaskAdapter.java
index b7976e1dab3..bcabfb08438 100644
--- 
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/taskadapter/PodTemplateTaskAdapter.java
+++ 
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/taskadapter/PodTemplateTaskAdapter.java
@@ -240,7 +240,15 @@ public class PodTemplateTaskAdapter implements TaskAdapter
         new EnvVarBuilder()
             .withName(DruidK8sConstants.LOAD_BROADCAST_SEGMENTS_ENV)
             
.withValue(Boolean.toString(task.getBroadcastDatasourceLoadingSpec().getMode().needsBroadcastSegments()))
-            .build()
+            .build(),
+        // Lets the peon tag its metrics with the template it runs under. 
Sourced from the annotation
+        // rather than passed by value so it always reflects the template 
actually applied to the pod.
+        new EnvVarBuilder()
+            .withName(DruidK8sConstants.POD_TEMPLATE_ENV)
+            .withValueFrom(new EnvVarSourceBuilder().withFieldRef(new 
ObjectFieldSelector(
+                null,
+                StringUtils.format("metadata.annotations['%s']", 
DruidK8sConstants.TASK_JOB_TEMPLATE)
+            )).build()).build()
     );
     if (!shouldUseDeepStorageForTaskPayload(task)) {
       envVars.add(new EnvVarBuilder()
diff --git 
a/extensions-core/kubernetes-overlord-extensions/src/main/resources/META-INF/services/org.apache.druid.initialization.DruidModule
 
b/extensions-core/kubernetes-overlord-extensions/src/main/resources/META-INF/services/org.apache.druid.initialization.DruidModule
index 927a4349a2e..550cdff5390 100644
--- 
a/extensions-core/kubernetes-overlord-extensions/src/main/resources/META-INF/services/org.apache.druid.initialization.DruidModule
+++ 
b/extensions-core/kubernetes-overlord-extensions/src/main/resources/META-INF/services/org.apache.druid.initialization.DruidModule
@@ -14,3 +14,4 @@
 # limitations under the License.
 
 org.apache.druid.k8s.overlord.KubernetesOverlordModule
+org.apache.druid.k8s.overlord.KubernetesPeonMetricsModule
diff --git 
a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/KubernetesPeonMetricsModuleTest.java
 
b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/KubernetesPeonMetricsModuleTest.java
new file mode 100644
index 00000000000..1c8b0891032
--- /dev/null
+++ 
b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/KubernetesPeonMetricsModuleTest.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.k8s.overlord;
+
+import com.google.inject.Guice;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+import org.apache.druid.initialization.DruidModule;
+import org.apache.druid.k8s.overlord.common.DruidK8sConstants;
+import org.apache.druid.server.emitter.ExtraServiceDimensions;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+public class KubernetesPeonMetricsModuleTest
+{
+  private static final Key<Map<String, String>> EXTRA_DIMENSIONS_KEY =
+      Key.get(new TypeLiteral<>() {}, ExtraServiceDimensions.class);
+
+  @Test
+  public void test_moduleIsRegisteredAsDruidModule()
+  {
+    final List<String> registered = ServiceLoader.load(DruidModule.class)
+                                                 .stream()
+                                                 .map(provider -> 
provider.type().getName())
+                                                 .toList();
+
+    Assertions.assertTrue(
+        registered.contains(KubernetesPeonMetricsModule.class.getName()),
+        "KubernetesPeonMetricsModule must be listed in 
META-INF/services/org.apache.druid.initialization.DruidModule"
+    );
+  }
+
+  @Test
+  public void test_podTemplateEnvSet_addsDimension()
+  {
+    Assertions.assertEquals(
+        Map.of(DruidK8sConstants.POD_TEMPLATE_DIMENSION, "podSpec1"),
+        extraDimensions("podSpec1")
+    );
+  }
+
+  @Test
+  public void test_podTemplateEnvUnset_addsNoDimension()
+  {
+    Assertions.assertEquals(Map.of(), extraDimensions(null));
+  }
+
+  @Test
+  public void test_podTemplateEnvEmpty_addsNoDimension()
+  {
+    Assertions.assertEquals(Map.of(), extraDimensions(""));
+  }
+
+  /**
+   * Resolves what the module contributes to {@link ExtraServiceDimensions}. 
{@code EmitterModule}
+   * owns getting these onto emitted events, and {@code EmitterModuleTest} 
covers that.
+   */
+  private static Map<String, String> extraDimensions(@Nullable String 
podTemplateName)
+  {
+    return 
Guice.createInjector(podTemplateModule(podTemplateName)).getInstance(EXTRA_DIMENSIONS_KEY);
+  }
+
+  private static KubernetesPeonMetricsModule podTemplateModule(@Nullable 
String podTemplateName)
+  {
+    return new KubernetesPeonMetricsModule()
+    {
+      @Override
+      String getPodTemplateName()
+      {
+        return podTemplateName;
+      }
+    };
+  }
+}
diff --git 
a/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobLongIds.yaml
 
b/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobLongIds.yaml
index eec68115b1d..e8ceb2100ff 100644
--- 
a/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobLongIds.yaml
+++ 
b/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobLongIds.yaml
@@ -52,6 +52,10 @@ spec:
               value: "NONE"
             - name: "LOAD_BROADCAST_SEGMENTS"
               value: "false"
+            - name: "DRUID_POD_TEMPLATE"
+              valueFrom:
+                fieldRef:
+                  fieldPath: "metadata.annotations['task.jobTemplate']"
             - name: "TASK_JSON"
               valueFrom:
                 fieldRef:
diff --git 
a/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobNoTaskJson.yaml
 
b/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobNoTaskJson.yaml
index 499eff1df05..e910d1e0fe9 100644
--- 
a/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobNoTaskJson.yaml
+++ 
b/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobNoTaskJson.yaml
@@ -51,5 +51,9 @@ spec:
               value: "NONE"
             - name: "LOAD_BROADCAST_SEGMENTS"
               value: "false"
+            - name: "DRUID_POD_TEMPLATE"
+              valueFrom:
+                fieldRef:
+                  fieldPath: "metadata.annotations['task.jobTemplate']"
           image: one
           name: primary
diff --git 
a/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobTlsEnabledBase.yaml
 
b/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobTlsEnabledBase.yaml
index ef7641ec987..68ba7fa49ef 100644
--- 
a/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobTlsEnabledBase.yaml
+++ 
b/extensions-core/kubernetes-overlord-extensions/src/test/resources/expectedNoopJobTlsEnabledBase.yaml
@@ -52,6 +52,10 @@ spec:
               value: "NONE"
             - name: "LOAD_BROADCAST_SEGMENTS"
               value: "false"
+            - name: "DRUID_POD_TEMPLATE"
+              valueFrom:
+                fieldRef:
+                  fieldPath: "metadata.annotations['task.jobTemplate']"
             - name: "TASK_JSON"
               valueFrom:
                 fieldRef:


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to