k8s-resource-check-v1.0sh

진웅·2025년 7월 28일

K8S Basics

목록 보기
24/40

#!/bin/bash

================================================================

Kubernetes 노드 리소스 분석 스크립트 v1.4

================================================================

🎯 주요 기능:

• 노드별 CPU/메모리 할당 vs 요청량 비교 분석

• 리소스 사용률을 색상으로 시각화 (정상/주의/위험)

• CPU는 기가코어(G), 메모리는 기가바이트(Gi) 단위로 통일

• 테이블 형태로 깔끔한 정보 제공

• Pod별 리소스 소비량 상세 분석

• 스케줄링 실패 Pod 및 원인 파악

• 높은 리소스 요청 Pod 필터링

• 실용적인 분석 가이드 및 해결방안 제시

🔍 사용 목적:

kubectl describe node로는 여유가 있어 보이지만 실제로는

Pod들의 resource request로 인해 스케줄링이 안 되는 문제 진단

📋 사용법:

chmod +x k8s-resource-check.sh

./k8s-resource-check.sh

📅 작성일: 2025-07-28

👤 버전: v1.4 - CPU 기가코어 단위 적용

================================================================

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="1"locallength=1" local length={#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 "BOLD─────────────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────{BOLD}┌─────────────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┐{NC}"
echo -e "BOLD│노드명│CPU할당│CPU요청│CPU사용률│메모리할당│메모리요청│메모리사용률│{BOLD}│ 노드명 │ CPU 할당 │ CPU 요청 │ CPU 사용률 │ 메모리 할당 │ 메모리 요청 │ 메모리 사용률 │{NC}"
echo -e "BOLD││(G)(G)({BOLD}│ │ (G) │ (G) │ (%) │ (Gi) │ (Gi) │ (%) │{NC}"
echo -e "BOLD├─────────────────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤{BOLD}├─────────────────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤{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 "BOLD─────────────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────{BOLD}└─────────────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┘{NC}"
}

함수: CPU 단위를 기가코어로 정규화 (m -> G 변환)

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

}

함수: 안전한 비율 계산 (awk 사용)

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

}

함수: 숫자 비교 (awk 사용)

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 "BOLD🚨상태알림:{BOLD}🚨 상태 알림:{NC}"
high_usage_nodes=()
for data in "nodedata[@]";doIFS=readrnodecpupctmempct<<<"{node_data[@]}"; do IFS='|' read -r node cpu_pct mem_pct <<< "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 nodes;doechoe"nodes; do echo -e "{PURPLE}📍 노드: BOLD{BOLD}nodeNC"echoe"{NC}" echo -e "{BOLD}┌─────────┬─────────────┬──────────────────────────────────────────────────────────────┐NC"echoe"{NC}" echo -e "{BOLD}│ CPU(G) │ Memory(Gi) │ Pod Name │NC"echoe"{NC}" echo -e "{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 "📋 클러스터 전체 상황 요약"

스케줄링 실패한 Pod 확인

echo -e "RED🚫스케줄링실패한Pod목록:{RED}🚫 스케줄링 실패한 Pod 목록:{NC}"
echo -e "BOLD─────────────────────────┬─────────────────────────────────────┬──────────────────{BOLD}┌─────────────────────────┬─────────────────────────────────────┬──────────────────┐{NC}"
echo -e "BOLDNamespacePodNameStatus{BOLD}│ Namespace │ Pod Name │ Status │{NC}"
echo -e "BOLD├─────────────────────────┼─────────────────────────────────────┼──────────────────┤{BOLD}├─────────────────────────┼─────────────────────────────────────┼──────────────────┤{NC}"

pending_pods=(kubectlgetpodsallnamespacesfieldselectorstatus.phase=Pendingnoheaders2>/dev/null)if[[n"(kubectl get pods --all-namespaces --field-selector status.phase=Pending --no-headers 2>/dev/null) if [[ -n "pending_pods" ]]; then
echo "pendingpods"whilereadrnamespacenamereadystatusrestartsage;doprintf"pending_pods" | while read -r namespace name ready status restarts age; do printf "│ %-23s │ %-35s │ ${RED}%-16s{NC} │\n" "namespace""namespace" "name" "status" done else printf "│ ${GREEN}%-23s{NC} │ GREEN{GREEN}%-35s{NC} │ GREEN{GREEN}%-16s{NC} │\n" "없음" "모든 Pod가 정상 스케줄됨" "✅ 정상"
fi

echo -e "BOLD─────────────────────────┴─────────────────────────────────────┴──────────────────{BOLD}└─────────────────────────┴─────────────────────────────────────┴──────────────────┘{NC}"

echo
echo -e "YELLOW🔥높은리소스요청Pod(CPU>0.5G또는Memory>0.5Gi):{YELLOW}🔥 높은 리소스 요청 Pod (CPU > 0.5G 또는 Memory > 0.5Gi):{NC}"
echo -e "BOLD─────────┬─────────────┬─────────────────────┬──────────────────────────────────────{BOLD}┌─────────┬─────────────┬─────────────────────┬──────────────────────────────────────┐{NC}"
echo -e "BOLDCPU(G)Memory(Gi)NodePodName{BOLD}│ CPU(G) │ Memory(Gi) │ Node │ Pod Name │{NC}"
echo -e "BOLD├─────────┼─────────────┼─────────────────────┼──────────────────────────────────────┤{BOLD}├─────────┼─────────────┼─────────────────────┼──────────────────────────────────────┤{NC}"

high_resource_pods=(kubectlgetpodsallnamespacesojson jqr.items[]select(.spec.containers[]?.resources.requests!=null)name:.metadata.name,namespace:.metadata.namespace,node:.spec.nodeName,requests:[.spec.containers[]?.resources.requests//]addselect((.requests.cpu//"0"iftest("m(kubectl get pods --all-namespaces -o json | \ jq -r '.items[] | select(.spec.containers[]?.resources.requests != null) | { name: .metadata.name, namespace: .metadata.namespace, node: .spec.nodeName, requests: [.spec.containers[]?.resources.requests // {}] | add } | select( (.requests.cpu // "0" | if test("m") then (rtrimstr("m") | tonumber / 1000) else tonumber end) > 0.5 or
(.requests.memory // "0" | if test("Mi")then(rtrimstr("Mi")tonumber/1024)eliftest("Gi") then (rtrimstr("Mi") | tonumber / 1024) elif test("Gi") then (rtrimstr("Gi") | tonumber) else 0 end) > 0.5
) |
[
(.requests.cpu // "0" | if test("m")then(rtrimstr("m")tonumber/1000)elsetonumberend),(.requests.memory//"0"iftest("Mi") then (rtrimstr("m") | tonumber / 1000) else tonumber end), (.requests.memory // "0" | if test("Mi") 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 "highresourcepods"whileIFS=high_resource_pods" | while IFS='\t' read -r cpu_g memory_gi node pod; do
if [ ${#pod} -gt 36 ]; then
pod_display="pod:0:33..."elsepoddisplay="{pod:0:33}..." else 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 "│ GREEN{GREEN}%-7s{NC} │ GREEN{GREEN}%-11s{NC} │ GREEN{GREEN}%-19s{NC} │ GREEN{GREEN}%-36s{NC} │\n" "없음" "없음" "모든 노드" "높은 리소스 요청 Pod 없음"
fi

echo -e "BOLD─────────┴─────────────┴─────────────────────┴──────────────────────────────────────{BOLD}└─────────┴─────────────┴─────────────────────┴──────────────────────────────────────┘{NC}"

echo
echo -e "BLUE🔍최근스케줄링실패이벤트:{BLUE}🔍 최근 스케줄링 실패 이벤트:{NC}"
recent_events=(kubectlgeteventsallnamespacesfieldselectorreason=FailedSchedulingsortby=.lastTimestamp2>/dev/nulltail5)if[[n"(kubectl get events --all-namespaces --field-selector reason=FailedScheduling --sort-by='.lastTimestamp' 2>/dev/null | tail -5) if [[ -n "recent_events" ]]; then
echo "recentevents"elseechoe"recent_events" else echo -e "{GREEN}✅ 최근 스케줄링 실패 이벤트가 없습니다${NC}"
fi

print_header "💡 분석 가이드 및 권장사항 v1.4"

echo -e "BOLD🎯새로운단위시스템:{BOLD}🎯 새로운 단위 시스템:{NC}"
echo -e " CYAN📊CPU:{CYAN}📊 CPU:{NC} 기가코어(G) 단위 - 1G = 1000m (밀리코어)"
echo -e " CYAN💾Memory:{CYAN}💾 Memory:{NC} 기가바이트(Gi) 단위 - 일관된 용량 표시"

echo -e "BOLD🎯색상가이드:{BOLD}🎯 색상 가이드:{NC}"
echo -e " GREEN{GREEN}●{NC} 초록색: 정상 범위 (0-60%)"
echo -e " YELLOW{YELLOW}●{NC} 노란색: 주의 범위 (60-80%)"
echo -e " RED{RED}●{NC} 빨간색: 위험 범위 (80%+)"

echo
echo -e "BOLD📊분석포인트:{BOLD}📊 분석 포인트:{NC}"
echo -e " CYAN1.{CYAN}1.{NC} 기가 단위로 더 직관적인 리소스 용량 파악 가능"
echo -e " CYAN2.{CYAN}2.{NC} CPU 0.5G = 500m (밀리코어), Memory 0.5Gi = 512Mi"
echo -e " CYAN3.{CYAN}3.{NC} 사용률 80% 이상 노드는 새로운 Pod 스케줄링 어려움"
echo -e " CYAN4.{CYAN}4.{NC} 빨간색 Pod들이 많은 리소스를 점유하고 있음"

echo
echo -e "{BOLD}🛠️ 권장 조치사항:{NC}"
echo -e " PURPLE{PURPLE}•{NC} 높은 사용률 노드의 Pod 재분배 고려"
echo -e " PURPLE{PURPLE}•{NC} 과도한 리소스 요청 Pod의 request 값 조정"
echo -e " PURPLE{PURPLE}•{NC} 새로운 노드 추가 또는 기존 노드 스케일업"
echo -e " PURPLE{PURPLE}•{NC} Pod의 실제 사용량 vs 요청량 모니터링으로 최적화"

echo
echo -e "BOLD🔧상세분석명령어:{BOLD}🔧 상세 분석 명령어:{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 "GREEN기가단위시스템으로더직관적인분석이완료되었습니다!🚀{GREEN}기가 단위 시스템으로 더 직관적인 분석이 완료되었습니다! 🚀{NC}"
echo -e "GRAY💡추가문의사항이나개선사항이있으시면클러스터관리자에게문의하세요.{GRAY}💡 추가 문의사항이나 개선사항이 있으시면 클러스터 관리자에게 문의하세요.{NC}"

profile
bytebliss

0개의 댓글