1d85012b53
Feed the three kubectl outputs into one jq program that does app aggregation, CPU/memory unit normalization, and quota math, replacing the per-pod jq fork loops and eight quota jq reads (~40 forks down to ~4).
87 lines
4.4 KiB
Bash
87 lines
4.4 KiB
Bash
#!/usr/bin/env bash
|
|
# Outputs per-pod CPU/MEM metrics and namespace quota as JSON.
|
|
# Usage: k8s-metrics.sh <namespace>
|
|
#
|
|
# All aggregation and unit normalization happen in a single jq pass — the three
|
|
# kubectl calls below are the only subprocesses besides jq itself.
|
|
set -euo pipefail
|
|
|
|
NS="${1:-tenant-5}"
|
|
|
|
pods_json=$(kubectl get pods -n "$NS" -o json 2>/dev/null) || exit 1
|
|
top_output=$(kubectl top pods -n "$NS" --no-headers 2>/dev/null) || top_output=""
|
|
quota_json=$(kubectl get resourcequota -n "$NS" -o json 2>/dev/null) || quota_json='{"items":[]}'
|
|
|
|
jq -nc \
|
|
--argjson pods "$pods_json" \
|
|
--argjson quota "$quota_json" \
|
|
--arg top "$top_output" '
|
|
# ── unit normalizers ────────────────────────────────────────────────────────
|
|
def ncpu: # CPU string → integer millicores
|
|
if . == null or . == "" then 0
|
|
elif test("^[0-9]+m$") then (rtrimstr("m")|tonumber)
|
|
elif test("^[0-9]+n$") then ((rtrimstr("n")|tonumber)/1000000|floor)
|
|
elif test("^[0-9]+u$") then ((rtrimstr("u")|tonumber)/1000|floor)
|
|
elif test("^[0-9.]+$") then (tonumber*1000|floor)
|
|
else 0 end;
|
|
def nmem: # memory string → integer MiB
|
|
if . == null or . == "" then 0
|
|
elif test("Gi$") then (rtrimstr("Gi")|tonumber*1024|floor)
|
|
elif test("Mi$") then (rtrimstr("Mi")|tonumber|floor)
|
|
elif test("Ki$") then (rtrimstr("Ki")|tonumber/1024|floor)
|
|
elif test("Ti$") then (rtrimstr("Ti")|tonumber*1048576|floor)
|
|
elif test("^[0-9]+$") then (tonumber/1048576|floor)
|
|
else 0 end;
|
|
def fmtcpu: "\(.)m";
|
|
def fmtmem: if . < 1024 then "\(.)Mi" else "\(((./1024)*100|round)/100)Gi" end;
|
|
|
|
# ── podName → app map ───────────────────────────────────────────────────────
|
|
($pods.items // []) as $items
|
|
| ($items
|
|
| map({ (.metadata.name): (.metadata.labels["app.kubernetes.io/instance"] // .metadata.name) })
|
|
| add // {}) as $name2app
|
|
|
|
# ── parse `kubectl top` text (pod / cpu / mem columns) ──────────────────────
|
|
| ($top | split("\n")
|
|
| map(select(test("\\S")) | [match("\\S+";"g").string]
|
|
| { pod: .[0], cpuM: (.[1]|ncpu), memMi: (.[2]|nmem) })) as $tops
|
|
|
|
# ── aggregate per app (sum pods sharing an app label) ───────────────────────
|
|
| (reduce $tops[] as $t ({};
|
|
($name2app[$t.pod]) as $a
|
|
| if $a == null then .
|
|
else .[$a] = { cpuM: ((.[$a].cpuM // 0) + $t.cpuM),
|
|
memMi: ((.[$a].memMi // 0) + $t.memMi) } end)) as $byApp
|
|
|
|
| ([$items[] | (.metadata.labels["app.kubernetes.io/instance"] // .metadata.name)] | unique) as $apps
|
|
| ([ $apps[] | { app: ., cpuM: ($byApp[.].cpuM // -1), memMi: ($byApp[.].memMi // -1) } ]) as $podMetrics
|
|
|
|
| (reduce ($byApp|to_entries[]) as $e (0; . + $e.value.cpuM)) as $cpuActual
|
|
| (reduce ($byApp|to_entries[]) as $e (0; . + $e.value.memMi)) as $memActual
|
|
|
|
# ── resource quota ──────────────────────────────────────────────────────────
|
|
| (($quota.items // [])[0].status // {}) as $qs
|
|
| ($qs.used // {}) as $u
|
|
| ($qs.hard // {}) as $h
|
|
| ($u["requests.cpu"] | ncpu) as $crqu | ($h["requests.cpu"] | ncpu) as $crqh
|
|
| ($u["limits.cpu"] | ncpu) as $clu | ($h["limits.cpu"] | ncpu) as $clh
|
|
| ($u["requests.memory"] | nmem) as $mrqu | ($h["requests.memory"] | nmem) as $mrqh
|
|
| ($u["limits.memory"] | nmem) as $mlu | ($h["limits.memory"] | nmem) as $mlh
|
|
|
|
| {
|
|
podMetrics: $podMetrics,
|
|
quota: {
|
|
cpuActualM: $cpuActual,
|
|
memActualMi: $memActual,
|
|
cpuReqPct: (if $crqh>0 then $crqu/$crqh else 0 end),
|
|
cpuLimPct: (if $clh>0 then $clu/$clh else 0 end),
|
|
memReqPct: (if $mrqh>0 then $mrqu/$mrqh else 0 end),
|
|
memLimPct: (if $mlh>0 then $mlu/$mlh else 0 end),
|
|
cpuReqLabel: "\($crqu|fmtcpu) / \($crqh|fmtcpu)",
|
|
cpuLimLabel: "\($clu|fmtcpu) / \($clh|fmtcpu)",
|
|
memReqLabel: "\($mrqu|fmtmem) / \($mrqh|fmtmem)",
|
|
memLimLabel: "\($mlu|fmtmem) / \($mlh|fmtmem)"
|
|
}
|
|
}
|
|
'
|