Skip to content

Instantly share code, notes, and snippets.

@magohl
Created July 3, 2025 14:24
Show Gist options
  • Select an option

  • Save magohl/faade1178554490de33d95a164bf18a1 to your computer and use it in GitHub Desktop.

Select an option

Save magohl/faade1178554490de33d95a164bf18a1 to your computer and use it in GitHub Desktop.
#!/bin/bash
# Kubernetes Node Resource Analysis Script
# Shows CPU/Memory requests vs available per node + top 10 requesters
# Multi-user friendly version
set -e
# Parse command line arguments
CONTEXT=""
NAMESPACE_FILTER=""
HELP=false
while [[ $# -gt 0 ]]; do
case $1 in
-c|--context)
CONTEXT="$2"
shift 2
;;
-n|--namespace)
NAMESPACE_FILTER="$2"
shift 2
;;
-h|--help)
HELP=true
shift
;;
*)
echo "Unknown option $1"
exit 1
;;
esac
done
if [[ $HELP == true ]]; then
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -c, --context CONTEXT Use specific kubectl context"
echo " -n, --namespace NS Filter pods by namespace"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
echo " $0 # Analyze all nodes and pods"
echo " $0 -c prod-cluster # Use specific context"
echo " $0 -n kube-system # Only show kube-system pods"
exit 0
fi
# Set kubectl context if specified
if [[ -n "$CONTEXT" ]]; then
KUBECTL_CMD="kubectl --context=$CONTEXT"
else
KUBECTL_CMD="kubectl"
fi
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
echo -e "${CYAN}===========================================${NC}"
echo -e "${CYAN} Kubernetes Node Resource Analysis${NC}"
echo -e "${CYAN}===========================================${NC}"
echo
# Check if kubectl is available
if ! command -v kubectl &> /dev/null; then
echo -e "${RED}Error: kubectl not found. Please install kubectl first.${NC}"
exit 1
fi
# Check if we can connect to cluster
if ! $KUBECTL_CMD cluster-info &> /dev/null; then
echo -e "${RED}Error: Cannot connect to Kubernetes cluster. Check your kubeconfig.${NC}"
if [[ -n "$CONTEXT" ]]; then
echo -e "${RED}Specified context: $CONTEXT${NC}"
fi
exit 1
fi
# Show current context
current_context=$($KUBECTL_CMD config current-context 2>/dev/null || echo "unknown")
echo -e "${CYAN}Current context: $current_context${NC}"
if [[ -n "$NAMESPACE_FILTER" ]]; then
echo -e "${CYAN}Namespace filter: $NAMESPACE_FILTER${NC}"
fi
echo
# Function to convert memory units to bytes
convert_memory_to_bytes() {
local mem=$1
if [[ $mem =~ ^([0-9.]+)([KMGT]i?)$ ]]; then
local value=${BASH_REMATCH[1]}
local unit=${BASH_REMATCH[2]}
case $unit in
K|Ki) echo $(echo "$value * 1024" | bc -l) ;;
M|Mi) echo $(echo "$value * 1024 * 1024" | bc -l) ;;
G|Gi) echo $(echo "$value * 1024 * 1024 * 1024" | bc -l) ;;
T|Ti) echo $(echo "$value * 1024 * 1024 * 1024 * 1024" | bc -l) ;;
*) echo $value ;;
esac
else
echo $mem
fi
}
# Function to convert CPU units to millicores
convert_cpu_to_millicores() {
local cpu=$1
if [[ $cpu =~ ^([0-9.]+)m$ ]]; then
echo ${BASH_REMATCH[1]}
elif [[ $cpu =~ ^([0-9.]+)$ ]]; then
echo $(echo "${BASH_REMATCH[1]} * 1000" | bc -l)
else
echo $cpu
fi
}
# Function to format bytes to human readable
format_bytes() {
local bytes=$1
if (( $(echo "$bytes >= 1073741824" | bc -l) )); then
echo "$(echo "scale=2; $bytes / 1073741824" | bc -l)Gi"
elif (( $(echo "$bytes >= 1048576" | bc -l) )); then
echo "$(echo "scale=2; $bytes / 1048576" | bc -l)Mi"
elif (( $(echo "$bytes >= 1024" | bc -l) )); then
echo "$(echo "scale=2; $bytes / 1024" | bc -l)Ki"
else
echo "${bytes}B"
fi
}
# Function to format millicores to human readable
format_millicores() {
local millicores=$1
if (( $(echo "$millicores >= 1000" | bc -l) )); then
echo "$(echo "scale=2; $millicores / 1000" | bc -l)"
else
echo "${millicores}m"
fi
}
# Get all nodes
nodes=$($KUBECTL_CMD get nodes -o jsonpath='{.items[*].metadata.name}')
for node in $nodes; do
echo -e "${BLUE}=========================================${NC}"
echo -e "${BLUE}Node: $node${NC}"
echo -e "${BLUE}=========================================${NC}"
# Get node capacity and allocatable resources
node_info=$($KUBECTL_CMD get node $node -o json)
cpu_capacity=$(echo "$node_info" | jq -r '.status.capacity.cpu // "0"')
mem_capacity=$(echo "$node_info" | jq -r '.status.capacity.memory // "0"')
cpu_allocatable=$(echo "$node_info" | jq -r '.status.allocatable.cpu // "0"')
mem_allocatable=$(echo "$node_info" | jq -r '.status.allocatable.memory // "0"')
# Convert to standard units
cpu_capacity_m=$(convert_cpu_to_millicores $cpu_capacity)
mem_capacity_b=$(convert_memory_to_bytes $mem_capacity)
cpu_allocatable_m=$(convert_cpu_to_millicores $cpu_allocatable)
mem_allocatable_b=$(convert_memory_to_bytes $mem_allocatable)
echo -e "${GREEN}Node Capacity:${NC}"
echo -e " CPU: $(format_millicores $cpu_capacity_m) ($(echo "scale=0; $cpu_capacity_m / 1000" | bc -l) cores)"
echo -e " Memory: $(format_bytes $mem_capacity_b)"
echo
echo -e "${GREEN}Node Allocatable:${NC}"
echo -e " CPU: $(format_millicores $cpu_allocatable_m) ($(echo "scale=0; $cpu_allocatable_m / 1000" | bc -l) cores)"
echo -e " Memory: $(format_bytes $mem_allocatable_b)"
echo
# Get all pods on this node with their resource requests
if [[ -n "$NAMESPACE_FILTER" ]]; then
pods_data=$($KUBECTL_CMD get pods -n $NAMESPACE_FILTER --field-selector spec.nodeName=$node -o json)
else
pods_data=$($KUBECTL_CMD get pods --all-namespaces --field-selector spec.nodeName=$node -o json)
fi
# Calculate total requests
total_cpu_requests=0
total_mem_requests=0
# Create temporary file for pod resource data
temp_file=$(mktemp)
echo "$pods_data" | jq -r '.items[] | select(.status.phase == "Running") |
.metadata.namespace + "," + .metadata.name + "," +
((.spec.containers[]?.resources.requests.cpu // "0") | tostring) + "," +
((.spec.containers[]?.resources.requests.memory // "0") | tostring)' > $temp_file
# Process each pod and calculate totals
declare -A pod_cpu_requests
declare -A pod_mem_requests
while IFS=',' read -r namespace pod_name cpu_req mem_req; do
if [[ -n "$namespace" && -n "$pod_name" ]]; then
# Convert to standard units
cpu_m=$(convert_cpu_to_millicores "$cpu_req")
mem_b=$(convert_memory_to_bytes "$mem_req")
# Add to totals
total_cpu_requests=$(echo "$total_cpu_requests + $cpu_m" | bc -l)
total_mem_requests=$(echo "$total_mem_requests + $mem_b" | bc -l)
# Store per pod for top 10 calculation
pod_key="$namespace/$pod_name"
pod_cpu_requests[$pod_key]=$cpu_m
pod_mem_requests[$pod_key]=$mem_b
fi
done < $temp_file
# Calculate percentages
cpu_percent=$(echo "scale=2; $total_cpu_requests * 100 / $cpu_allocatable_m" | bc -l)
mem_percent=$(echo "scale=2; $total_mem_requests * 100 / $mem_allocatable_b" | bc -l)
echo -e "${YELLOW}Resource Requests vs Allocatable:${NC}"
echo -e " CPU: $(format_millicores $total_cpu_requests) / $(format_millicores $cpu_allocatable_m) (${cpu_percent}%)"
echo -e " Memory: $(format_bytes $total_mem_requests) / $(format_bytes $mem_allocatable_b) (${mem_percent}%)"
echo
# Show top 10 CPU requesters
echo -e "${PURPLE}Top 10 CPU Requesters:${NC}"
printf "%-40s %15s\n" "Pod" "CPU Request"
printf "%-40s %15s\n" "---" "-----------"
for pod in "${!pod_cpu_requests[@]}"; do
echo "$pod ${pod_cpu_requests[$pod]}"
done | sort -k2 -nr | head -10 | while read pod cpu; do
printf "%-40s %15s\n" "$pod" "$(format_millicores $cpu)"
done
echo
# Show top 10 Memory requesters
echo -e "${PURPLE}Top 10 Memory Requesters:${NC}"
printf "%-40s %15s\n" "Pod" "Memory Request"
printf "%-40s %15s\n" "---" "--------------"
for pod in "${!pod_mem_requests[@]}"; do
echo "$pod ${pod_mem_requests[$pod]}"
done | sort -k2 -nr | head -10 | while read pod mem; do
printf "%-40s %15s\n" "$pod" "$(format_bytes $mem)"
done
echo
echo
# Clean up
rm -f $temp_file
unset pod_cpu_requests
unset pod_mem_requests
done
echo -e "${CYAN}Analysis Complete!${NC}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment