167 lines
7.0 KiB
Bash
167 lines
7.0 KiB
Bash
#!/usr/bin/env bash
|
|
# Outputs per-pod CPU/MEM metrics and namespace quota as JSON.
|
|
# Usage: k8s-metrics.sh <namespace>
|
|
set -euo pipefail
|
|
|
|
NS="${1:-tenant-5}"
|
|
|
|
# ── Unit normalization ───────────────────────────────────────────────────────
|
|
|
|
# Normalize CPU string to millicores integer
|
|
normalize_cpu() {
|
|
local val="$1"
|
|
if [[ "$val" =~ ^([0-9]+)m$ ]]; then
|
|
echo "${BASH_REMATCH[1]}"
|
|
elif [[ "$val" =~ ^([0-9]+(\.[0-9]+)?)$ ]]; then
|
|
awk "BEGIN { printf \"%d\", ${BASH_REMATCH[1]} * 1000 }"
|
|
else
|
|
echo 0
|
|
fi
|
|
}
|
|
|
|
# Normalize memory string to MiB integer
|
|
normalize_mem() {
|
|
local val="$1"
|
|
if [[ "$val" =~ ^([0-9]+)Gi$ ]]; then
|
|
echo $(( ${BASH_REMATCH[1]} * 1024 ))
|
|
elif [[ "$val" =~ ^([0-9]+)Mi$ ]]; then
|
|
echo "${BASH_REMATCH[1]}"
|
|
elif [[ "$val" =~ ^([0-9]+)Ki$ ]]; then
|
|
echo $(( ${BASH_REMATCH[1]} / 1024 ))
|
|
elif [[ "$val" =~ ^([0-9]+)$ ]]; then
|
|
echo $(( ${BASH_REMATCH[1]} / 1048576 ))
|
|
else
|
|
echo 0
|
|
fi
|
|
}
|
|
|
|
# Format millicores for display ("420m" or "1.5c")
|
|
fmt_cpu() {
|
|
local m="$1"
|
|
if [[ $m -lt 1000 ]]; then
|
|
echo "${m}m"
|
|
else
|
|
awk "BEGIN { printf \"%.1fc\", $m / 1000 }"
|
|
fi
|
|
}
|
|
|
|
# Format MiB for display ("343Mi" or "1.50Gi")
|
|
fmt_mem() {
|
|
local mib="$1"
|
|
if [[ $mib -lt 1024 ]]; then
|
|
echo "${mib}Mi"
|
|
else
|
|
awk "BEGIN { printf \"%.2fGi\", $mib / 1024 }"
|
|
fi
|
|
}
|
|
|
|
# ── Fetch data ───────────────────────────────────────────────────────────────
|
|
|
|
pods_json=$(kubectl get pods -n "$NS" -o json 2>/dev/null) || exit 0
|
|
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) || exit 0
|
|
|
|
# ── Build podName → app map ──────────────────────────────────────────────────
|
|
|
|
declare -A name_to_app
|
|
while IFS= read -r line; do
|
|
pod_name=$(echo "$line" | jq -r '.name')
|
|
app=$(echo "$line" | jq -r '.app')
|
|
name_to_app["$pod_name"]="$app"
|
|
done < <(echo "$pods_json" | jq -c '.items[] | {name: .metadata.name, app: (.metadata.labels["app.kubernetes.io/instance"] // .metadata.name)}')
|
|
|
|
# ── Parse kubectl top ────────────────────────────────────────────────────────
|
|
|
|
declare -A top_cpu # app → cpuM
|
|
declare -A top_mem # app → memMi
|
|
cpu_actual=0
|
|
mem_actual=0
|
|
|
|
while IFS= read -r line; do
|
|
[[ -z "$line" ]] && continue
|
|
read -r pod_name cpu_str mem_str <<< "$line"
|
|
app="${name_to_app[$pod_name]:-}"
|
|
[[ -z "$app" ]] && continue
|
|
cpu_m=$(normalize_cpu "$cpu_str")
|
|
mem_mib=$(normalize_mem "$mem_str")
|
|
top_cpu["$app"]=$cpu_m
|
|
top_mem["$app"]=$mem_mib
|
|
cpu_actual=$(( cpu_actual + cpu_m ))
|
|
mem_actual=$(( mem_actual + mem_mib ))
|
|
done <<< "$top_output"
|
|
|
|
# ── Build podMetrics JSON array ──────────────────────────────────────────────
|
|
|
|
pod_metrics="["
|
|
first=1
|
|
while IFS= read -r line; do
|
|
app=$(echo "$line" | jq -r '.app')
|
|
cpu_m="${top_cpu[$app]:-"-1"}"
|
|
mem_mib="${top_mem[$app]:-"-1"}"
|
|
[[ $first -eq 0 ]] && pod_metrics+=","
|
|
pod_metrics+=$(jq -nc --arg app "$app" --argjson cpu "$cpu_m" --argjson mem "$mem_mib" \
|
|
'{app: $app, cpuM: $cpu, memMi: $mem}')
|
|
first=0
|
|
done < <(echo "$pods_json" | jq -c '.items[] | {app: (.metadata.labels["app.kubernetes.io/instance"] // .metadata.name)}')
|
|
pod_metrics+="]"
|
|
|
|
# ── Parse resourcequota ──────────────────────────────────────────────────────
|
|
|
|
q_cpu_req_used=$(echo "$quota_json" | jq -r '.items[0].status.used["requests.cpu"] // "0"')
|
|
q_cpu_req_hard=$(echo "$quota_json" | jq -r '.items[0].status.hard["requests.cpu"] // "0"')
|
|
q_cpu_lim_used=$(echo "$quota_json" | jq -r '.items[0].status.used["limits.cpu"] // "0"')
|
|
q_cpu_lim_hard=$(echo "$quota_json" | jq -r '.items[0].status.hard["limits.cpu"] // "0"')
|
|
q_mem_req_used=$(echo "$quota_json" | jq -r '.items[0].status.used["requests.memory"] // "0"')
|
|
q_mem_req_hard=$(echo "$quota_json" | jq -r '.items[0].status.hard["requests.memory"] // "0"')
|
|
q_mem_lim_used=$(echo "$quota_json" | jq -r '.items[0].status.used["limits.memory"] // "0"')
|
|
q_mem_lim_hard=$(echo "$quota_json" | jq -r '.items[0].status.hard["limits.memory"] // "0"')
|
|
|
|
cpu_req_used_m=$(normalize_cpu "$q_cpu_req_used")
|
|
cpu_req_hard_m=$(normalize_cpu "$q_cpu_req_hard")
|
|
cpu_lim_used_m=$(normalize_cpu "$q_cpu_lim_used")
|
|
cpu_lim_hard_m=$(normalize_cpu "$q_cpu_lim_hard")
|
|
mem_req_used_mib=$(normalize_mem "$q_mem_req_used")
|
|
mem_req_hard_mib=$(normalize_mem "$q_mem_req_hard")
|
|
mem_lim_used_mib=$(normalize_mem "$q_mem_lim_used")
|
|
mem_lim_hard_mib=$(normalize_mem "$q_mem_lim_hard")
|
|
|
|
cpu_req_pct=$(awk "BEGIN { printf \"%.4f\", $cpu_req_used_m / ($cpu_req_hard_m > 0 ? $cpu_req_hard_m : 1) }")
|
|
cpu_lim_pct=$(awk "BEGIN { printf \"%.4f\", $cpu_lim_used_m / ($cpu_lim_hard_m > 0 ? $cpu_lim_hard_m : 1) }")
|
|
mem_req_pct=$(awk "BEGIN { printf \"%.4f\", $mem_req_used_mib / ($mem_req_hard_mib > 0 ? $mem_req_hard_mib : 1) }")
|
|
mem_lim_pct=$(awk "BEGIN { printf \"%.4f\", $mem_lim_used_mib / ($mem_lim_hard_mib > 0 ? $mem_lim_hard_mib : 1) }")
|
|
|
|
cpu_req_label="$(fmt_cpu $cpu_req_used_m) / $(fmt_cpu $cpu_req_hard_m)"
|
|
cpu_lim_label="$(fmt_cpu $cpu_lim_used_m) / $(fmt_cpu $cpu_lim_hard_m)"
|
|
mem_req_label="$(fmt_mem $mem_req_used_mib) / $(fmt_mem $mem_req_hard_mib)"
|
|
mem_lim_label="$(fmt_mem $mem_lim_used_mib) / $(fmt_mem $mem_lim_hard_mib)"
|
|
|
|
# ── Output ───────────────────────────────────────────────────────────────────
|
|
|
|
jq -nc \
|
|
--argjson podMetrics "$pod_metrics" \
|
|
--argjson cpuActualM "$cpu_actual" \
|
|
--argjson memActualMi "$mem_actual" \
|
|
--argjson cpuReqPct "$cpu_req_pct" \
|
|
--argjson cpuLimPct "$cpu_lim_pct" \
|
|
--argjson memReqPct "$mem_req_pct" \
|
|
--argjson memLimPct "$mem_lim_pct" \
|
|
--arg cpuReqLabel "$cpu_req_label" \
|
|
--arg cpuLimLabel "$cpu_lim_label" \
|
|
--arg memReqLabel "$mem_req_label" \
|
|
--arg memLimLabel "$mem_lim_label" \
|
|
'{
|
|
podMetrics: $podMetrics,
|
|
quota: {
|
|
cpuActualM: $cpuActualM,
|
|
memActualMi: $memActualMi,
|
|
cpuReqPct: $cpuReqPct,
|
|
cpuLimPct: $cpuLimPct,
|
|
memReqPct: $memReqPct,
|
|
memLimPct: $memLimPct,
|
|
cpuReqLabel: $cpuReqLabel,
|
|
cpuLimLabel: $cpuLimLabel,
|
|
memReqLabel: $memReqLabel,
|
|
memLimLabel: $memLimLabel
|
|
}
|
|
}'
|