Created
July 3, 2025 14:24
-
-
Save magohl/faade1178554490de33d95a164bf18a1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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