#!/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_table_header() {
echo -e "{NC}"
echo -e "{NC}"
echo -e "{NC}"
echo -e "{NC}"
}
print_table_row() {
local node="$1"
local cpu_alloc="$2"
local cpu_req="$3"
local cpu_usage="$4"
local mem_alloc="$5"
local mem_req="$6"
local mem_usage="$7"
# 사용률에 따른 색상 결정
local cpu_color="${GREEN}"
local mem_color="${GREEN}"
if [[ $(compare_numbers $cpu_usage "> 80") -eq 1 ]]; then
cpu_color="${RED}"
elif [[ $(compare_numbers $cpu_usage "> 60") -eq 1 ]]; then
cpu_color="${YELLOW}"
fi
if [[ $(compare_numbers $mem_usage "> 80") -eq 1 ]]; then
mem_color="${RED}"
elif [[ $(compare_numbers $mem_usage "> 60") -eq 1 ]]; then
mem_color="${YELLOW}"
fi
printf "│ %-19s │ %11s │ %11s │ ${cpu_color}%10s%%${NC} │ %11s │ %11s │ ${mem_color}%12s%%${NC} │\n" \
"$node" "$cpu_alloc" "$cpu_req" "$cpu_usage" "$mem_alloc" "$mem_req" "$mem_usage"
}
print_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 = 1, 1G = 1000000m)
local milli=${BASH_REMATCH[1]}
awk "BEGIN {printf \"%.3f\", $milli / 1000}"
elif [[ $cpu =~ ^([0-9]+)$ ]]; then
# 정수형 CPU (1 = 1000m = 1G)
echo ${BASH_REMATCH[1]}.000
elif [[ $cpu =~ ^([0-9]*\.?[0-9]+)$ ]]; then
# 소수점이 있는 경우 (예: 0.5 = 500m = 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
}
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.4"
nodes=$(kubectl get nodes --no-headers -o custom-columns=":metadata.name")
print_header "📊 노드별 리소스 할당 현황 (CPU: 기가코어, Memory: 기가바이트)"
print_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")
# 사용률 계산
cpu_usage_percent=$(safe_percentage $requested_cpu_g $allocatable_cpu_g)
memory_usage_percent=$(safe_percentage $requested_memory_gi $allocatable_memory_gi)
# 테이블 행 출력
print_table_row "$node" "${allocatable_cpu_g}G" "${requested_cpu_g}G" "$cpu_usage_percent" "${allocatable_memory_gi}Gi" "${requested_memory_gi}Gi" "$memory_usage_percent"
# 노드 데이터 저장 (나중에 상세 분석용)
node_data+=("$node|$cpu_usage_percent|$memory_usage_percent")
done
print_table_footer
echo
echo -e "{NC}"
high_usage_nodes=()
for data in "data"
if [[ $(compare_numbers $cpu_pct "> 80") -eq 1 ]] || [[ $(compare_numbers $mem_pct "> 80") -eq 1 ]]; then
high_usage_nodes+=("node") echo -e " ${RED}⚠️ $node: 높은 리소스 사용률 (CPU: ${cpu_pct}%, Memory: ${mem_pct}%){NC}"
fi
done
if [ {#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.4"
echo -e "{NC}"
echo -e " {NC} 기가코어(G) 단위 - 1G = 1000m (밀리코어)"
echo -e " {NC} 기가바이트(Gi) 단위 - 일관된 용량 표시"
echo -e "{NC}"
echo -e " {NC} 초록색: 정상 범위 (0-60%)"
echo -e " {NC} 노란색: 주의 범위 (60-80%)"
echo -e " {NC} 빨간색: 위험 범위 (80%+)"
echo
echo -e "{NC}"
echo -e " {NC} 기가 단위로 더 직관적인 리소스 용량 파악 가능"
echo -e " {NC} CPU 0.5G = 500m (밀리코어), Memory 0.5Gi = 512Mi"
echo -e " {NC} 사용률 80% 이상 노드는 새로운 Pod 스케줄링 어려움"
echo -e " {NC} 빨간색 Pod들이 많은 리소스를 점유하고 있음"
echo
echo -e "{BOLD}🛠️ 권장 조치사항:{NC}"
echo -e " {NC} 높은 사용률 노드의 Pod 재분배 고려"
echo -e " {NC} 과도한 리소스 요청 Pod의 request 값 조정"
echo -e " {NC} 새로운 노드 추가 또는 기존 노드 스케일업"
echo -e " {NC} Pod의 실제 사용량 vs 요청량 모니터링으로 최적화"
echo
echo -e "{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}# 실시간 사용량: kubectl top nodes && kubectl top pods --all-namespaces{NC}"
print_header "✅ 분석 완료 - K8s 리소스 진단 v1.4"
echo -e "{NC}"
echo -e "{NC}"