#!/bin/bash
set -e
RED='\033[1;31m' # 빨간색 (굵게)
GREEN='\033[1;32m' # 초록색 (굵게)
YELLOW='\033[1;33m' # 노란색 (굵게)
BLUE='\033[1;34m' # 파란색 (굵게)
PURPLE='\033[1;35m' # 보라색 (굵게)
CYAN='\033[1;36m' # 시안색 (굵게)
WHITE='\033[1;37m' # 흰색 (굵게)
GRAY='\033[0;37m' # 회색
BOLD='\033[1m' # 굵게
NC='\033[0m' # 색상 초기화
print_header() {
local title="{#title}
local border=$(printf "%*s" $((length + 10)) | tr ' ' '=')
echo
echo -e "${CYAN}$border${NC}"
echo -e "${CYAN} $title${NC}"
echo -e "${CYAN}$border${NC}"
echo
}
print_extended_table_header() {
echo -e "{NC}"
echo -e "{NC}"
echo -e "{NC}"
echo -e "{NC}"
}
print_extended_table_row() {
local node="$1"
local cpu_alloc="$2"
local cpu_req="$3"
local cpu_occupy="$4"
local cpu_usage="$5"
local mem_alloc="$6"
local mem_req="$7"
local mem_occupy="$8"
local mem_usage="$9"
# 점유율에 따른 색상 결정 (Request 기준)
local cpu_occupy_color="${GREEN}"
local mem_occupy_color="${GREEN}"
if [[ $(compare_numbers $cpu_occupy "> 80") -eq 1 ]]; then
cpu_occupy_color="${RED}"
elif [[ $(compare_numbers $cpu_occupy "> 60") -eq 1 ]]; then
cpu_occupy_color="${YELLOW}"
fi
if [[ $(compare_numbers $mem_occupy "> 80") -eq 1 ]]; then
mem_occupy_color="${RED}"
elif [[ $(compare_numbers $mem_occupy "> 60") -eq 1 ]]; then
mem_occupy_color="${YELLOW}"
fi
# 실제 사용률에 따른 색상 결정
local cpu_usage_color="${GREEN}"
local mem_usage_color="${GREEN}"
if [[ "$cpu_usage" != "N/A" ]]; then
if [[ $(compare_numbers $cpu_usage "> 80") -eq 1 ]]; then
cpu_usage_color="${RED}"
elif [[ $(compare_numbers $cpu_usage "> 60") -eq 1 ]]; then
cpu_usage_color="${YELLOW}"
fi
else
cpu_usage_color="${GRAY}"
fi
if [[ "$mem_usage" != "N/A" ]]; then
if [[ $(compare_numbers $mem_usage "> 80") -eq 1 ]]; then
mem_usage_color="${RED}"
elif [[ $(compare_numbers $mem_usage "> 60") -eq 1 ]]; then
mem_usage_color="${YELLOW}"
fi
else
mem_usage_color="${GRAY}"
fi
printf "│ %-15s │ %9s │ %9s │ ${cpu_occupy_color}%8s%%${NC} │ ${cpu_usage_color}%8s%%${NC} │ %9s │ %9s │ ${mem_occupy_color}%8s%%${NC} │ ${mem_usage_color}%8s%%${NC} │\n" \
"$node" "$cpu_alloc" "$cpu_req" "$cpu_occupy" "$cpu_usage" "$mem_alloc" "$mem_req" "$mem_occupy" "$mem_usage"
}
print_extended_table_footer() {
echo -e "{NC}"
}
normalize_cpu_to_giga() {
local cpu=$1
# 빈 값이나 null 체크
if [[ -z "$cpu" ]] || [[ "$cpu" == "null" ]]; then
echo "0.000"
return
fi
# 정규표현식으로 안전하게 파싱하고 기가코어로 변환
if [[ $cpu =~ ^([0-9]+)m$ ]]; then
# 밀리코어를 기가코어로 변환 (1000m = 1G)
local milli=${BASH_REMATCH[1]}
awk "BEGIN {printf \"%.3f\", $milli / 1000}"
elif [[ $cpu =~ ^([0-9]+)$ ]]; then
# 정수형 CPU (1 = 1G)
echo ${BASH_REMATCH[1]}.000
elif [[ $cpu =~ ^([0-9]*\.?[0-9]+)$ ]]; then
# 소수점이 있는 경우 (예: 0.5 = 0.5G)
local cpu_float=${BASH_REMATCH[1]}
awk "BEGIN {printf \"%.3f\", $cpu_float}"
else
echo "0.000"
fi
}
normalize_memory_to_giga() {
local mem=$1
# 빈 값이나 null 체크
if [[ -z "$mem" ]] || [[ "$mem" == "null" ]]; then
echo "0.00"
return
fi
# 정규표현식으로 안전하게 파싱하고 기가바이트로 변환
if [[ $mem =~ ^([0-9]+)Ki$ ]]; then
local ki=${BASH_REMATCH[1]}
awk "BEGIN {printf \"%.2f\", $ki / 1024 / 1024}"
elif [[ $mem =~ ^([0-9]+)Mi$ ]]; then
local mi=${BASH_REMATCH[1]}
awk "BEGIN {printf \"%.2f\", $mi / 1024}"
elif [[ $mem =~ ^([0-9]+)Gi$ ]]; then
echo ${BASH_REMATCH[1]}.00
elif [[ $mem =~ ^([0-9]+)Ti$ ]]; then
local ti=${BASH_REMATCH[1]}
awk "BEGIN {printf \"%.2f\", $ti * 1024}"
elif [[ $mem =~ ^([0-9]+)$ ]]; then
# 단위 없으면 바이트로 가정하고 Gi로 변환
local bytes=$mem
awk "BEGIN {printf \"%.2f\", $bytes / 1024 / 1024 / 1024}"
else
echo "0.00"
fi
}
get_actual_usage() {
local node=$1
local resource_type=$2 # "cpu" or "memory"
# kubectl top nodes에서 해당 노드의 실제 사용량 추출
local top_output=$(kubectl top nodes --no-headers 2>/dev/null | grep "^$node ")
if [[ -z "$top_output" ]]; then
echo "N/A"
return
fi
if [[ "$resource_type" == "cpu" ]]; then
# CPU 사용량 추출 (예: 250m -> 0.25G, 15% -> 15.0)
local cpu_usage=$(echo "$top_output" | awk '{print $2}')
local cpu_percent=$(echo "$top_output" | awk '{print $3}' | tr -d '%')
if [[ -n "$cpu_percent" ]] && [[ "$cpu_percent" != "0" ]]; then
echo "$cpu_percent"
else
echo "N/A"
fi
elif [[ "$resource_type" == "memory" ]]; then
# 메모리 사용률 추출
local mem_percent=$(echo "$top_output" | awk '{print $5}' | tr -d '%')
if [[ -n "$mem_percent" ]] && [[ "$mem_percent" != "0" ]]; then
echo "$mem_percent"
else
echo "N/A"
fi
fi
}
safe_percentage() {
local numerator=$1
local denominator=$2
if [[ $(awk "BEGIN {print ($denominator <= 0)}") -eq 1 ]] || [[ $(awk "BEGIN {print ($numerator <= 0)}") -eq 1 ]]; then
echo "0.0"
else
awk "BEGIN {printf \"%.1f\", $numerator * 100 / $denominator}"
fi
}
compare_numbers() {
local num1=1 local operator=$2 local num2=$3 awk "BEGIN {print (num1 $operator $num2)}"
}
print_header "🚀 KUBERNETES 노드 리소스 분석 v1.5"
echo -e "{NC}"
if kubectl top nodes >/dev/null 2>&1; then
echo -e "{NC}"
METRICS_AVAILABLE=true
else
echo -e "{YELLOW}⚠️ Metrics Server를 사용할 수 없습니다 (실제 사용률은 N/A로 표시됩니다){NC}"
echo -e "{NC}"
METRICS_AVAILABLE=false
fi
nodes=$(kubectl get nodes --no-headers -o custom-columns=":metadata.name")
print_header "📊 노드별 리소스 현황: 점유율 vs 실제 사용률"
echo -e "{NC}"
echo -e " {NC}: Pod Request 기준 리소스 예약 비율 (스케줄링에 영향)"
echo -e " {NC}: 실제 CPU/메모리 소비 비율 (성능에 영향)"
echo
print_extended_table_header
declare -a node_data
for node in $nodes; do
# 노드 정보 가져오기
node_info=$(kubectl describe node $node)
# 노드 용량 추출
allocatable_cpu=$(echo "$node_info" | grep -A 20 "Allocatable:" | grep "cpu:" | awk '{print $2}')
allocatable_memory=$(echo "$node_info" | grep -A 20 "Allocatable:" | grep "memory:" | awk '{print $2}')
# 요청된 리소스 추출 (더 안전한 파싱)
requested_cpu_raw=$(echo "$node_info" | grep -A 10 "Allocated resources:" | grep "cpu" | awk '{print $2}' | tr -d '()')
requested_memory_raw=$(echo "$node_info" | grep -A 10 "Allocated resources:" | grep "memory" | awk '{print $2}' | tr -d '()')
# CPU/메모리를 기가 단위로 정규화
allocatable_cpu_g=$(normalize_cpu_to_giga "$allocatable_cpu")
allocatable_memory_gi=$(normalize_memory_to_giga "$allocatable_memory")
requested_cpu_g=$(normalize_cpu_to_giga "$requested_cpu_raw")
requested_memory_gi=$(normalize_memory_to_giga "$requested_memory_raw")
# 점유율 계산 (Request 기준)
cpu_occupy_percent=$(safe_percentage $requested_cpu_g $allocatable_cpu_g)
memory_occupy_percent=$(safe_percentage $requested_memory_gi $allocatable_memory_gi)
# 실제 사용률 가져오기 (kubectl top 기반)
if [[ "$METRICS_AVAILABLE" == true ]]; then
cpu_usage_percent=$(get_actual_usage "$node" "cpu")
memory_usage_percent=$(get_actual_usage "$node" "memory")
else
cpu_usage_percent="N/A"
memory_usage_percent="N/A"
fi
# 테이블 행 출력
print_extended_table_row "$node" "${allocatable_cpu_g}G" "${requested_cpu_g}G" "$cpu_occupy_percent" "$cpu_usage_percent" "${allocatable_memory_gi}Gi" "${requested_memory_gi}Gi" "$memory_occupy_percent" "$memory_usage_percent"
# 노드 데이터 저장 (나중에 상세 분석용)
node_data+=("$node|$cpu_occupy_percent|$memory_occupy_percent|$cpu_usage_percent|$memory_usage_percent")
done
print_extended_table_footer
echo
echo -e "{NC}"
high_occupy_nodes=()
high_usage_nodes=()
for data in "data"
# 점유율 기준 경고
if [[ $(compare_numbers $cpu_occupy "> 80") -eq 1 ]] || [[ $(compare_numbers $mem_occupy "> 80") -eq 1 ]]; then
high_occupy_nodes+=("$node")
echo -e " ${RED}⚠️ $node: 높은 리소스 점유율 (CPU: ${cpu_occupy}%, Memory: ${mem_occupy}%) - 스케줄링 어려움${NC}"
fi
# 실제 사용률 기준 경고
if [[ "$cpu_usage" != "N/A" ]] && [[ "$mem_usage" != "N/A" ]]; then
if [[ $(compare_numbers $cpu_usage "> 80") -eq 1 ]] || [[ $(compare_numbers $mem_usage "> 80") -eq 1 ]]; then
high_usage_nodes+=("$node")
echo -e " ${YELLOW}🔥 $node: 높은 실제 사용률 (CPU: ${cpu_usage}%, Memory: ${mem_usage}%) - 성능 저하 우려${NC}"
fi
fi
done
if [ {#high_occupy_nodes[@]} -eq 0 ] && [ ${#high_usage_nodes[@]} -eq 0 ]; then echo -e " ${GREEN}✅ 모든 노드의 리소스 점유율과 사용률이 정상 범위입니다{NC}"
fi
print_header "🔍 노드별 상위 리소스 소비 POD"
for node in {PURPLE}📍 노드: node{BOLD}┌─────────┬─────────────┬──────────────────────────────────────────────────────────────┐{BOLD}│ CPU(G) │ Memory(Gi) │ Pod Name │{BOLD}├─────────┼─────────────┼──────────────────────────────────────────────────────────────┤${NC}"
# 노드의 모든 Pod 리소스 요청 상세 정보
kubectl get pods --all-namespaces --field-selector spec.nodeName=$node -o json | \
jq -r '.items[] |
{
name: .metadata.name,
namespace: .metadata.namespace,
containers: .spec.containers[]
} |
select(.containers.resources.requests != null) |
[
(.containers.resources.requests.cpu // "0" | if test("m$") then (rtrimstr("m") | tonumber / 1000) else tonumber end),
(.containers.resources.requests.memory // "0" | if test("Mi$") then (rtrimstr("Mi") | tonumber / 1024) elif test("Gi$") then (rtrimstr("Gi") | tonumber) elif test("Ki$") then (rtrimstr("Ki") | tonumber / 1024 / 1024) else 0 end),
(.namespace + "/" + .name)
] |
@tsv' | \
sort -nr | \
head -10 | \
while IFS=$'\t' read -r cpu_g memory_gi pod; do
# 50자 이내로 Pod 이름 제한
if [ ${#pod} -gt 60 ]; then
pod_display="${pod:0:57}..."
else
pod_display="$pod"
fi
# CPU와 메모리 값 포맷팅
cpu_formatted=$(awk "BEGIN {printf \"%.3f\", $cpu_g}")
memory_formatted=$(awk "BEGIN {printf \"%.2f\", $memory_gi}")
if [[ $(awk "BEGIN {print ($cpu_g > 0.5)}") -eq 1 ]] || [[ $(awk "BEGIN {print ($memory_gi > 0.5)}") -eq 1 ]]; then
printf "│ ${RED}%7s${NC} │ ${RED}%11s${NC} │ %-60s │\n" "$cpu_formatted" "$memory_formatted" "$pod_display"
elif [[ $(awk "BEGIN {print ($cpu_g > 0.2)}") -eq 1 ]] || [[ $(awk "BEGIN {print ($memory_gi > 0.25)}") -eq 1 ]]; then
printf "│ ${YELLOW}%7s${NC} │ ${YELLOW}%11s${NC} │ %-60s │\n" "$cpu_formatted" "$memory_formatted" "$pod_display"
else
printf "│ ${GREEN}%7s${NC} │ ${GREEN}%11s${NC} │ %-60s │\n" "$cpu_formatted" "$memory_formatted" "$pod_display"
fi
done
echo -e "${BOLD}└─────────┴─────────────┴──────────────────────────────────────────────────────────────┘${NC}"
echo
done
print_header "📋 클러스터 전체 상황 요약"
echo -e "{NC}"
echo -e "{NC}"
echo -e "{NC}"
echo -e "{NC}"
pending_pods=pending_pods" ]]; then
echo "{NC} │\n" "name" "status" done else printf "│ ${GREEN}%-23s{NC} │ {NC} │ {NC} │\n" "없음" "모든 Pod가 정상 스케줄됨" "✅ 정상"
fi
echo -e "{NC}"
echo
echo -e "{NC}"
echo -e "{NC}"
echo -e "{NC}"
echo -e "{NC}"
high_resource_pods=") then (rtrimstr("m") | tonumber / 1000) else tonumber end) > 0.5 or
(.requests.memory // "0" | if test("Mi") then (rtrimstr("Gi") | tonumber) else 0 end) > 0.5
) |
[
(.requests.cpu // "0" | if test("m") then (rtrimstr("Mi") | tonumber / 1024) elif test("Gi$") then (rtrimstr("Gi") | tonumber) else 0 end),
(.node // "Unscheduled"),
(.namespace + "/" + .name)
] | @tsv')
if [[ -n "$high_resource_pods" ]]; then
echo "'\t' read -r cpu_g memory_gi node pod; do
if [ ${#pod} -gt 36 ]; then
pod_display="pod"
fi
if [ ${#node} -gt 19 ]; then
node_display="${node:0:16}..."
else
node_display="$node"
fi
cpu_formatted=$(awk "BEGIN {printf \"%.3f\", $cpu_g}")
memory_formatted=$(awk "BEGIN {printf \"%.2f\", $memory_gi}")
printf "│ ${RED}%7s${NC} │ ${RED}%11s${NC} │ %-19s │ %-36s │\n" "$cpu_formatted" "$memory_formatted" "$node_display" "$pod_display"
done
else
printf "│ {NC} │ {NC} │ {NC} │ {NC} │\n" "없음" "없음" "모든 노드" "높은 리소스 요청 Pod 없음"
fi
echo -e "{NC}"
echo
echo -e "{NC}"
recent_events=recent_events" ]]; then
echo "{GREEN}✅ 최근 스케줄링 실패 이벤트가 없습니다${NC}"
fi
print_header "💡 분석 가이드 및 권장사항 v1.5"
echo -e "{NC}"
echo -e " {NC}: 스케줄링을 위해 예약된 리소스 비율"
echo -e " {NC}: 실제로 소비되고 있는 리소스 비율"
echo -e " {NC}"
echo -e "{NC}"
echo -e " {NC} 초록색: 정상 범위 (0-60%)"
echo -e " {NC} 노란색: 주의 범위 (60-80%)"
echo -e " {NC} 빨간색: 위험 범위 (80%+)"
echo -e " {NC} 회색: 데이터 없음 (Metrics Server 필요)"
echo
echo -e "{NC}"
echo -e " {NC} 점유율 높음 + 사용률 낮음 → Request 과다 할당, 리소스 낭비"
echo -e " {NC} 점유율 낮음 + 사용률 높음 → Request 부족 설정, 성능 영향 가능"
echo -e " {NC} 점유율 높음 + 사용률 높음 → 실제 리소스 부족, 스케일업 필요"
echo -e " {NC} 점유율 낮음 + 사용률 낮음 → 정상 상태, 여유 리소스 있음"
echo
echo -e "{BOLD}🛠️ 권장 조치사항:{NC}"
echo -e " {NC} 점유율 > 80%: 새로운 노드 추가 또는 Pod 재분배"
echo -e " {NC} 사용률 > 80%: CPU/메모리 성능 최적화 또는 스케일업"
echo -e " {NC} 점유율 >> 사용률: Pod Request 값 다운사이징"
echo -e " {NC} 사용률 >> 점유율: Pod Request 값 업사이징 (안정성 확보)"
echo -e " {NC} Metrics Server 설치로 실시간 모니터링 강화"
echo
echo -e "{NC}"
echo -e " {GRAY}# 실시간 노드 사용률: kubectl top nodes{NC}"
echo -e " {GRAY}# 실시간 Pod 사용률: kubectl top pods --all-namespaces{NC}"
echo -e " {GRAY}# 특정 Pod 상세 정보: kubectl describe pod <pod-name> -n <namespace>{NC}"
echo -e " {GRAY}# 노드 상세 정보: kubectl describe node <node-name>{NC}"
echo -e " {GRAY}# HPA 상태 확인: kubectl get hpa --all-namespaces{NC}"
echo
echo -e "{NC}"
echo -e " {NC}"
print_header "✅ 분석 완료 - K8s 리소스 진단 v1.5"
echo -e "{NC}"
echo -e "{NC}"
echo -e "{NC}"