npx skills add ...
npx skills add dynatrace/dynatrace-for-ai --skill dt-obs-kubernetes
Kubernetes cluster, pod, node, and workload monitoring. Use when analyzing K8s health, resource optimization, pod failures, OOMKills, scheduling, or security posture. Also use for Kubernetes operational events like pod restarts, OOM events, evictions, and cluster event history. Trigger: "Kubernetes pods", "K8s cluster health", "OOMKill", "pod restarts", "container CPU", "namespace resource usage", "over-provisioned pods", "privileged containers", "pod placement", "K8s node capacity", "running containers by cluster", "workload scheduling", "pod evictions", "K8s labels and annotations", "kubernetes events", "pod restart events", "OOM events", "K8s event history". Do NOT use for explaining existing queries, product documentation questions, AWS-specific resource queries, service-level RED metrics, distributed tracing, or log analysis — use the relevant skill instead.
npx skills add dynatrace/dynatrace-for-ai --skill dt-obs-kubernetes
Monitor and analyze Kubernetes infrastructure using Dynatrace DQL. Query cluster resources, monitor workload health, analyze pod placement, optimize costs, and assess security posture.
references/cluster-inventory.md - Clusters,
namespaces, resource distributionreferences/labels-annotations.md - Parse
k8s.object, labels, annotationsreferences/pod-node-placement.md - Node
selectors, affinity, taints, HAWorkloads: K8S_DEPLOYMENT, K8S_STATEFULSET, K8S_DAEMONSET,
K8S_JOB, K8S_CRONJOB, K8S_HORIZONTALPODAUTOSCALER
Infrastructure: K8S_CLUSTER, K8S_NAMESPACE, K8S_NODE, K8S_POD
Configuration: K8S_SERVICE, K8S_CONFIGMAP, K8S_SECRET,
K8S_PERSISTENTVOLUMECLAIM, K8S_PERSISTENTVOLUME, K8S_INGRESS,
K8S_NETWORKPOLICY
smartscapeNodes - Query K8s entities:
timeseries - Monitor metrics over time:
fetch logs - Analyze log events:
k8s.cluster.name, k8s.namespace.name, k8s.pod.name, k8s.node.namek8s.workload.name, k8s.workload.kind, k8s.container.namek8s.object - Full JSON configuration for deep inspectiontags[label] - Access labels and annotationsCPU: dt.kubernetes.container.cpu_usage, cpu_throttled, limits_cpu,
requests_cpu
Memory: dt.kubernetes.container.memory_working_set, limits_memory,
requests_memory
Operations: dt.kubernetes.container.restarts, oom_kills
Node: dt.kubernetes.node.pods_allocatable, cpu_allocatable,
memory_allocatable, dt.kubernetes.pods
K8S_POD vs CONTAINER: these are different entity types in Dynatrace.
K8S_POD — K8s-native entities with k8s.object JSON, scheduling state, conditions, and K8s metrics. Use this skill.CONTAINER — Host-level container inventory (image, lifetime, host assignment). Use dt-obs-hosts skill instead.The smartscape edge is CONTAINER --(is_part_of)--> K8S_POD. To reach containers from a pod, traverse backward:
No direct smartscape edge exists between SERVICE and K8S_POD. The correlation key is the shared dimension k8s.workload.name. See Service → Pod Drill-Down in references/pod-debugging.md for the full two-step pattern.
List all clusters:
Check node capacity:
Identify pods in non-Running state:
Find over-provisioned pods (usage < 30%):
Identify containers without limits:
Pod troubleshooting benefits from combining metrics (timeseries) with Kubernetes events (event stream) for a complete picture.
Find pods with OOMKills:
Analyze pod restart patterns:
For operational events (pod restarts, OOM kills, evictions, scheduling failures), Kubernetes events provide richer context than metrics alone — including event reasons, messages, and timestamps.
When to use Kubernetes events over metrics:
Kubernetes events are available through the get-events-for-kubernetes-cluster
tool. Prefer this tool when the user asks about OOM events, pod restarts,
evictions, or cluster-wide event history.
Important: distinguish event types when filtering results. Kubernetes events cover many categories. When the user asks about a specific event type, filter the results accordingly — do not report unrelated events:
| User Asks About | Relevant Event Reasons | NOT Related |
|---|---|---|
| Pod restarts | BackOff, CrashLoopBackOff, Killing | Readiness probe failures, CPU throttling |
| OOM events | OOMKilling, OOMKilled | Memory pressure warnings |
| Evictions | Evicted, Preempting | Node pressure |
| Scheduling failures | FailedScheduling, Unschedulable | Resource quotas |
For a complete answer, combine both approaches:
Pod restart and operational events can also be queried via DQL from the events table:
Filter for specific event reasons:
Field names in fetch events: Use event.reason and event.message — not
dt.kubernetes.event.reason. The dt.kubernetes.* prefix is for timeseries metrics,
not the events table. Queries using the wrong prefix return zero results.
Identify privileged containers:
Find containers running as root:
Verify pod distribution (HA compliance):
Find active DAVIS problems affecting K8s entities:
smartscape.affected_entities is a record array; each record has id, type, and name. Use
[][id] to get the array of Smartscape IDs to look up the affected entity, or
[id] after expand smartscape.affected_entities. Without a preceding expand, [id] returns
null silently. A filter cannot take a bare iterative expression, so wrap it in iAny(...).
| User Question | Best Approach | Why |
|---|---|---|
| "Show me OOM events" | Events tool + metrics | Events give reasons/messages; metrics show trends |
| "Show me pod restart events" | Events tool + timeseries metrics | Events reveal the reason (BackOff, Killing, CrashLoopBackOff); dt.kubernetes.container.restarts metric gives the actual restart counts |
| "How many pod restarts?" | Timeseries metrics | Quantitative data over time |
| "What happened to my pods in the last 48h?" | Events tool | Operational event history with context |
| "Which pods are using the most CPU?" | Timeseries metrics | Resource utilization analysis |
| "List all clusters/namespaces" | smartscapeNodes | Entity discovery and inventory |
| "Are there scheduling failures?" | Events tool | Event reasons explain why |
limit for exploration| Problem | Cause | Solution |
|---|---|---|
| No pod data returned | Wrong entity type or missing cluster filter | Use K8S_POD (not POD); add k8s.cluster.name filter |
k8s.object parsing errors | Complex JSON structure | Use parse k8s.object, "JSON:config" then access nested fields |
| Pod network metrics unavailable | Not available in Grail | Use service mesh metrics or host-level network metrics |
| Large result sets | No time range or cluster filter | Add time range and filter by cluster/namespace early |
| Missing labels in output | Labels accessed incorrectly | Use tags[label_name] to access labels |
Unavailable Metrics:
Query Considerations:
k8s.object field if not necessary→ references/cluster-inventory.md
k8s.object for detailed configuration inspection→ references/labels-annotations.md
→ references/pod-node-placement.md
→ references/workload-health.md
→ references/network-policies.md
dt.smartscape_source.id with K8S_ prefix filters)fetch logs
| filter k8s.namespace.name == "production" and loglevel == "ERROR"smartscapeNodes K8S_POD
| filter k8s.namespace.name == "<namespace>"
| traverse edgeTypes: {is_part_of}, targetTypes: {CONTAINER}, direction: backward, fieldsKeep: {id}
| fields k8s.cluster.name, k8s.namespace.name, k8s.pod.name, container.id=idsmartscapeNodes K8S_CLUSTER
| fields k8s.cluster.name, k8s.cluster.version, k8s.cluster.distributiontimeseries {
current_pods = avg(dt.kubernetes.pods),
max_pods = avg(dt.kubernetes.node.pods_allocatable)
}, by: {k8s.node.name, k8s.cluster.name}
| fieldsAdd pod_capacity_pct = (arrayAvg(current_pods) / arrayAvg(max_pods)) * 100
| filter pod_capacity_pct > 80smartscapeNodes K8S_POD
| parse k8s.object, "JSON:config"
| fieldsAdd phase = config[status][phase]
| filter phase != "Running"
| fields k8s.cluster.name, k8s.namespace.name, k8s.pod.name, phasetimeseries {
cpu_usage = sum(dt.kubernetes.container.cpu_usage),
cpu_requests = avg(dt.kubernetes.container.requests_cpu)
}, by: {k8s.pod.name, k8s.namespace.name, k8s.cluster.name}
| fieldsAdd usage_pct = (arrayAvg(cpu_usage) / arrayAvg(cpu_requests)) * 100
| filter usage_pct < 30 and arrayAvg(cpu_requests) > 0smartscapeNodes K8S_POD
| parse k8s.object, "JSON:config"
| expand container = config[spec][containers]
| fieldsAdd
container_name = container[name],
cpu_limit = container[resources][limits][cpu],
memory_limit = container[resources][limits][memory]
| filter isNull(cpu_limit) or isNull(memory_limit)timeseries oom_kills = sum(dt.kubernetes.container.oom_kills),
by: {k8s.pod.name, k8s.namespace.name, k8s.cluster.name}
| filter arraySum(oom_kills) > 0
| fieldsAdd total_oom_kills = arraySum(oom_kills)
| sort total_oom_kills desctimeseries restarts = sum(dt.kubernetes.container.restarts),
by: {k8s.pod.name, k8s.namespace.name, k8s.cluster.name}
| fieldsAdd total_restarts = arraySum(restarts)
| filter total_restarts > 5fetch events
| filter event.kind == "K8S_EVENT"
| filter event.type == "Warning"
| fields timestamp, k8s.cluster.name, k8s.namespace.name, k8s.pod.name,
event.reason, event.message
| sort timestamp desc
| limit 50fetch events
| filter event.kind == "K8S_EVENT"
| filter in(event.reason, {"OOMKilling", "BackOff", "Evicted", "FailedScheduling"})
| fields timestamp, k8s.cluster.name, k8s.namespace.name, k8s.pod.name,
event.reason, event.message
| sort timestamp descsmartscapeNodes K8S_POD
| parse k8s.object, "JSON:config"
| expand container = config[spec][containers]
| fieldsAdd
container_name = container[name],
privileged = container[securityContext][privileged]
| filter privileged == truesmartscapeNodes K8S_POD
| parse k8s.object, "JSON:config"
| expand container = config[spec][containers]
| fieldsAdd
container_name = container[name],
run_as_user = container[securityContext][runAsUser],
run_as_non_root = container[securityContext][runAsNonRoot]
| filter (isNull(run_as_user) or run_as_user == 0) and run_as_non_root != truesmartscapeNodes K8S_POD
| filter k8s.workload.kind == "deployment"
| summarize pod_count = count(),
node_count = countDistinct(k8s.node.name),
by: {k8s.cluster.name, k8s.namespace.name, k8s.workload.name}
| fieldsAdd ha_compliant = node_count > 1
| filter pod_count >= 2 and not ha_compliantfetch dt.davis.problems, from:now() - 2h
| filter not(dt.davis.is_duplicate) and event.status == "ACTIVE"
| filter iAny(startsWith(smartscape.affected_entities[][type], "K8S_"))
| fields display_id, event.name, event.category, affected_entity_ids = smartscape.affected_entities[][id]