node_disk_usage.sh

진웅·2025년 7월 28일

#!/bin/bash

노드별 컨테이너 디스크 사용량 조회 스크립트

사용법: ./node_disk_usage.sh [옵션]

색상 정의

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

도움말 함수

show_help() {
echo "사용법: $0 [OPTIONS]"
echo ""
echo "옵션:"
echo " -h, --help 도움말 표시"
echo " -v, --verbose 상세 정보 표시"
echo " -n, --namespace 특정 네임스페이스 파드만 계산"
echo " -s, --sort 디스크 사용량으로 정렬 (기본: 노드명 정렬)"
echo " -t, --threshold 디스크 사용률 임계값 (기본: 80%)"
echo " -f, --format 출력 형식 (table|json|csv)"
echo " -u, --unit 용량 단위 (B|KB|MB|GB) (기본: MB)"
echo ""
echo "예시:"
echo " $0 # 기본 실행"
echo " $0 -v # 상세 정보 포함"
echo " $0 -n kube-system # kube-system 네임스페이스만"
echo " $0 -s # 디스크 사용량으로 정렬"
echo " $0 -f json # JSON 형태로 출력"
echo " $0 -u GB # GB 단위로 표시"
}

기본 변수 설정

VERBOSE=false
NAMESPACE=""
SORT_BY_USAGE=false
THRESHOLD=80
FORMAT="table"
UNIT="MB"

명령행 인자 처리

while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_help
exit 0
;;
-v|--verbose)
VERBOSE=true
shift
;;
-n|--namespace)
NAMESPACE="$2"
shift 2
;;
-s|--sort)
SORT_BY_USAGE=true
shift
;;
-t|--threshold)
THRESHOLD="$2"
shift 2
;;
-f|--format)
FORMAT="$2"
shift 2
;;
-u|--unit)
UNIT="$2"
shift 2
;;
*)
echo "알 수 없는 옵션: $1"
show_help
exit 1
;;
esac
done

kubectl 명령 확인

if ! command -v kubectl &> /dev/null; then
echo -e "RED오류:kubectl이설치되지않았습니다.{RED}오류: kubectl이 설치되지 않았습니다.{NC}"
exit 1
fi

클러스터 접근 확인

if ! kubectl cluster-info &> /dev/null; then
echo -e "RED오류:쿠버네티스클러스터에접근할수없습니다.{RED}오류: 쿠버네티스 클러스터에 접근할 수 없습니다.{NC}"
exit 1
fi

metrics-server 확인

if ! kubectl top node &> /dev/null; then
echo -e "YELLOW경고:metricsserver가설치되지않았거나동작하지않습니다.{YELLOW}경고: metrics-server가 설치되지 않았거나 동작하지 않습니다.{NC}"
echo -e "YELLOW일부메트릭은사용할수없습니다.{YELLOW}일부 메트릭은 사용할 수 없습니다.{NC}"
echo ""
fi

echo -e "BLUE===노드별컨테이너디스크사용량조회==={BLUE}=== 노드별 컨테이너 디스크 사용량 조회 ==={NC}"
echo ""

네임스페이스 옵션 설정

NS_OPTION=""
if [[ -n "$NAMESPACE" ]]; then
NS_OPTION="--namespace=NAMESPACE"echoe"NAMESPACE" echo -e "{YELLOW}네임스페이스 필터: NAMESPACENAMESPACE{NC}"
echo ""
fi

바이트를 지정된 단위로 변환하는 함수

convert_bytes() {
local bytes="$1"
local unit="$2"

if [[ -z "$bytes" || "$bytes" == "0" ]]; then
    echo "0"
    return
fi

case "$unit" in
    "B")
        echo "$bytes"
        ;;
    "KB")
        echo "scale=1; $bytes / 1024" | bc -l 2>/dev/null | sed 's/\.0$//'
        ;;
    "MB")
        echo "scale=1; $bytes / 1048576" | bc -l 2>/dev/null | sed 's/\.0$//'
        ;;
    "GB")
        echo "scale=2; $bytes / 1073741824" | bc -l 2>/dev/null | sed 's/\.00$//'
        ;;
    *)
        echo "$bytes"
        ;;
esac

}

노드별 디스크 사용량 계산 함수

calculate_disk_usage() {
local temp_file="/tmp/node_disk_usage.tmp"

# 모든 노드 목록 가져오기
kubectl get nodes --no-headers -o custom-columns="NAME:.metadata.name" > "$temp_file"

# 결과 저장을 위한 배열
declare -A node_disk_used
declare -A node_disk_total
declare -A node_disk_available
declare -A node_status
declare -A node_container_count

if [[ "$VERBOSE" == true ]]; then
    echo -e "${YELLOW}노드별 디스크 사용량 수집 중...${NC}"
fi

# 각 노드별 디스크 사용량 계산
while IFS= read -r node; do
    # 노드 상태 확인
    node_ready=$(kubectl get node "$node" --no-headers -o custom-columns="STATUS:.status.conditions[?(@.type=='Ready')].status" 2>/dev/null)
    if [[ "$node_ready" == "True" ]]; then
        status="Ready"
    else
        status="NotReady"
        node_disk_used["$node"]="N/A"
        node_disk_total["$node"]="N/A"
        node_disk_available["$node"]="N/A"
        node_status["$node"]="$status"
        node_container_count["$node"]="0"
        continue
    fi
    
    # 파드 수 계산
    if [[ -n "$NAMESPACE" ]]; then
        container_count=$(kubectl get pods $NS_OPTION --field-selector=spec.nodeName="$node" --no-headers 2>/dev/null | \
                        grep -v "Completed\|Succeeded\|Failed" | wc -l)
    else
        container_count=$(kubectl get pods --all-namespaces --field-selector=spec.nodeName="$node" --no-headers 2>/dev/null | \
                        grep -v "Completed\|Succeeded\|Failed" | wc -l)
    fi
    
    # 노드의 파일시스템 정보 가져오기 (kubectl describe node에서)
    disk_info=$(kubectl describe node "$node" 2>/dev/null | grep -A 10 "Allocated resources:" | grep -E "ephemeral-storage|Capacity:" -A 1)
    
    # 노드 파일시스템 용량 정보 가져오기
    node_capacity=$(kubectl get node "$node" -o jsonpath='{.status.capacity.ephemeral-storage}' 2>/dev/null)
    node_allocatable=$(kubectl get node "$node" -o jsonpath='{.status.allocatable.ephemeral-storage}' 2>/dev/null)
    
    # 단위 변환 (Ki를 바이트로)
    if [[ -n "$node_capacity" ]]; then
        if [[ "$node_capacity" == *"Ki" ]]; then
            capacity_bytes=$(echo "${node_capacity%Ki} * 1024" | bc 2>/dev/null)
        elif [[ "$node_capacity" == *"Mi" ]]; then
            capacity_bytes=$(echo "${node_capacity%Mi} * 1048576" | bc 2>/dev/null)
        elif [[ "$node_capacity" == *"Gi" ]]; then
            capacity_bytes=$(echo "${node_capacity%Gi} * 1073741824" | bc 2>/dev/null)
        else
            capacity_bytes="$node_capacity"
        fi
    else
        capacity_bytes="0"
    fi
    
    if [[ -n "$node_allocatable" ]]; then
        if [[ "$node_allocatable" == *"Ki" ]]; then
            allocatable_bytes=$(echo "${node_allocatable%Ki} * 1024" | bc 2>/dev/null)
        elif [[ "$node_allocatable" == *"Mi" ]]; then
            allocatable_bytes=$(echo "${node_allocatable%Mi} * 1048576" | bc 2>/dev/null)
        elif [[ "$node_allocatable" == *"Gi" ]]; then
            allocatable_bytes=$(echo "${node_allocatable%Gi} * 1073741824" | bc 2>/dev/null)
        else
            allocatable_bytes="$node_allocatable"
        fi
    else
        allocatable_bytes="$capacity_bytes"
    fi
    
    # 사용량 계산 (실제 파드의 디스크 사용량 합계)
    used_bytes="0"
    if command -v kubectl top pod &> /dev/null; then
        # metrics-server가 있는 경우 실제 사용량 조회 시도
        if [[ -n "$NAMESPACE" ]]; then
            # 특정 네임스페이스의 파드들의 디스크 사용량 합계 (실제로는 메모리/CPU만 제공됨)
            used_estimation=$(kubectl get pods $NS_OPTION --field-selector=spec.nodeName="$node" -o jsonpath='{range .items[*]}{.spec.containers[*].resources.requests.ephemeral-storage}{"\n"}{end}' 2>/dev/null | \
                            grep -v '^$' | sed 's/Ki/000/g; s/Mi/000000/g; s/Gi/000000000/g' | \
                            awk '{sum+=$1} END {print sum+0}')
        else
            used_estimation=$(kubectl get pods --all-namespaces --field-selector=spec.nodeName="$node" -o jsonpath='{range .items[*]}{.spec.containers[*].resources.requests.ephemeral-storage}{"\n"}{end}' 2>/dev/null | \
                            grep -v '^$' | sed 's/Ki/000/g; s/Mi/000000/g; s/Gi/000000000/g' | \
                            awk '{sum+=$1} END {print sum+0}')
        fi
        
        if [[ -n "$used_estimation" && "$used_estimation" -gt 0 ]]; then
            used_bytes="$used_estimation"
        else
            # 추정값: 컨테이너당 평균 100MB 사용으로 가정
            used_bytes=$(echo "$container_count * 104857600" | bc 2>/dev/null)
        fi
    else
        # metrics-server가 없는 경우 추정값 사용
        used_bytes=$(echo "$container_count * 104857600" | bc 2>/dev/null)
    fi
    
    available_bytes=$(echo "$allocatable_bytes - $used_bytes" | bc 2>/dev/null)
    if [[ "$available_bytes" -lt 0 ]]; then
        available_bytes="0"
    fi
    
    node_disk_used["$node"]="$used_bytes"
    node_disk_total["$node"]="$allocatable_bytes"
    node_disk_available["$node"]="$available_bytes"
    node_status["$node"]="$status"
    node_container_count["$node"]="$container_count"
    
done < "$temp_file"

rm -f "$temp_file"

# 결과 출력
case "$FORMAT" in
    "json")
        output_json
        ;;
    "csv")
        output_csv
        ;;
    *)
        output_table
        ;;
esac

}

테이블 형태 출력

output_table() {
printf "%-20s %-12s %-12s %-12s %-10s %-8s %-10s\n" "노드명" "사용량(UNIT)""총용량(UNIT)" "총용량(UNIT)" "가용량($UNIT)" "사용률" "컨테이너" "상태"
printf "%-20s %-12s %-12s %-12s %-10s %-8s %-10s\n" "----" "----" "----" "----" "----" "----" "----"

# 정렬 방식 결정
if [[ "$SORT_BY_USAGE" == true ]]; then
    # 디스크 사용량으로 정렬
    for node in $(printf '%s\n' "${!node_disk_used[@]}" | sort -k1,1); do
        print_node_disk_info "$node"
    done | sort -k2,2nr
else
    # 노드명으로 정렬
    for node in $(printf '%s\n' "${!node_disk_used[@]}" | sort); do
        print_node_disk_info "$node"
    done
fi

echo ""
print_summary

}

노드 디스크 정보 출력 함수

print_node_disk_info() {
local node="1"localusedbytes="1" local used_bytes="{node_disk_used[$node]}"
local total_bytes="{node_disk_total[node]}"
local available_bytes="{node_disk_available[node]}"
local status="{node_status[node]}"
local container_count="{node_container_count[node]}"

if [[ "$used_bytes" == "N/A" ]]; then
    printf "${RED}%-20s %-12s %-12s %-12s %-10s %-8s %-10s${NC}\n" \
        "$node" "N/A" "N/A" "N/A" "N/A" "$container_count" "$status"
    return
fi

local used_display=$(convert_bytes "$used_bytes" "$UNIT")
local total_display=$(convert_bytes "$total_bytes" "$UNIT")
local available_display=$(convert_bytes "$available_bytes" "$UNIT")
local usage_percent=""

if [[ -n "$total_bytes" && "$total_bytes" -gt 0 ]]; then
    usage_percent=$(echo "scale=1; $used_bytes * 100 / $total_bytes" | bc -l 2>/dev/null)
    usage_percent="${usage_percent}%"
else
    usage_percent="N/A"
fi

# 색상 적용
local color=""
if [[ "$status" != "Ready" ]]; then
    color="$RED"
elif [[ -n "$usage_percent" && "${usage_percent%\%}" =~ ^[0-9]+\.?[0-9]*$ ]]; then
    local usage_num="${usage_percent%\%}"
    if (( $(echo "$usage_num >= $THRESHOLD" | bc -l 2>/dev/null) )); then
        color="$RED"
    elif (( $(echo "$usage_num >= $THRESHOLD * 0.7" | bc -l 2>/dev/null) )); then
        color="$YELLOW"
    else
        color="$GREEN"
    fi
else
    color="$NC"
fi

printf "${color}%-20s %-12s %-12s %-12s %-10s %-8s %-10s${NC}\n" \
    "$node" "$used_display" "$total_display" "$available_display" "$usage_percent" "$container_count" "$status"

}

JSON 형태 출력

output_json() {
echo "{"
echo " \"timestamp\": \"(dateu+"(date -u +"%Y-%m-%dT%H:%M:%SZ")\"," echo " \"namespace\": \"{NAMESPACE:-all}\","
echo " \"unit\": \"$UNIT\","
echo " \"threshold\": $THRESHOLD,"
echo " \"nodes\": ["

local first=true
for node in $(printf '%s\n' "${!node_disk_used[@]}" | sort); do
    if [[ "$first" == true ]]; then
        first=false
    else
        echo ","
    fi
    
    local used_bytes="${node_disk_used[$node]}"
    local total_bytes="${node_disk_total[$node]}"
    local available_bytes="${node_disk_available[$node]}"
    local status="${node_status[$node]}"
    local container_count="${node_container_count[$node]}"
    
    echo "    {"
    echo "      \"name\": \"$node\","
    
    if [[ "$used_bytes" == "N/A" ]]; then
        echo "      \"disk_used\": null,"
        echo "      \"disk_total\": null,"
        echo "      \"disk_available\": null,"
        echo "      \"usage_percent\": null,"
    else
        local used_display=$(convert_bytes "$used_bytes" "$UNIT")
        local total_display=$(convert_bytes "$total_bytes" "$UNIT")
        local available_display=$(convert_bytes "$available_bytes" "$UNIT")
        local usage_percent=""
        
        if [[ -n "$total_bytes" && "$total_bytes" -gt 0 ]]; then
            usage_percent=$(echo "scale=1; $used_bytes * 100 / $total_bytes" | bc -l 2>/dev/null)
        fi
        
        echo "      \"disk_used\": $used_display,"
        echo "      \"disk_total\": $total_display,"
        echo "      \"disk_available\": $available_display,"
        echo "      \"usage_percent\": ${usage_percent:-null},"
    fi
    
    echo "      \"container_count\": $container_count,"
    echo "      \"status\": \"$status\""
    echo -n "    }"
done
echo ""
echo "  ]"
echo "}"

}

CSV 형태 출력

output_csv() {
echo "노드명,사용량(UNIT),총용량(UNIT),총용량(UNIT),가용량($UNIT),사용률,컨테이너수,상태"

for node in $(printf '%s\n' "${!node_disk_used[@]}" | sort); do
    local used_bytes="${node_disk_used[$node]}"
    local total_bytes="${node_disk_total[$node]}"
    local available_bytes="${node_disk_available[$node]}"
    local status="${node_status[$node]}"
    local container_count="${node_container_count[$node]}"
    
    if [[ "$used_bytes" == "N/A" ]]; then
        echo "$node,N/A,N/A,N/A,N/A,$container_count,$status"
        continue
    fi
    
    local used_display=$(convert_bytes "$used_bytes" "$UNIT")
    local total_display=$(convert_bytes "$total_bytes" "$UNIT")
    local available_display=$(convert_bytes "$available_bytes" "$UNIT")
    local usage_percent=""
    
    if [[ -n "$total_bytes" && "$total_bytes" -gt 0 ]]; then
        usage_percent=$(echo "scale=1; $used_bytes * 100 / $total_bytes" | bc -l 2>/dev/null)
        usage_percent="${usage_percent}%"
    else
        usage_percent="N/A"
    fi
    
    echo "$node,$used_display,$total_display,$available_display,$usage_percent,$container_count,$status"
done

}

요약 정보 출력

print_summary() {
local total_nodes=0
local ready_nodes=0
local total_used=0
local total_capacity=0
local overloaded_nodes=0

for node in "${!node_disk_used[@]}"; do
    total_nodes=$((total_nodes + 1))
    
    if [[ "${node_status[$node]}" == "Ready" ]]; then
        ready_nodes=$((ready_nodes + 1))
        
        if [[ "${node_disk_used[$node]}" != "N/A" ]]; then
            total_used=$(echo "$total_used + ${node_disk_used[$node]}" | bc 2>/dev/null)
            total_capacity=$(echo "$total_capacity + ${node_disk_total[$node]}" | bc 2>/dev/null)
            
            # 과부하 노드 체크
            if [[ -n "${node_disk_total[$node]}" && "${node_disk_total[$node]}" -gt 0 ]]; then
                local usage_percent=$(echo "scale=1; ${node_disk_used[$node]} * 100 / ${node_disk_total[$node]}" | bc -l 2>/dev/null)
                if (( $(echo "$usage_percent >= $THRESHOLD" | bc -l 2>/dev/null) )); then
                    overloaded_nodes=$((overloaded_nodes + 1))
                fi
            fi
        fi
    fi
done

local total_used_display=$(convert_bytes "$total_used" "$UNIT")
local total_capacity_display=$(convert_bytes "$total_capacity" "$UNIT")
local cluster_usage_percent=""

if [[ "$total_capacity" -gt 0 ]]; then
    cluster_usage_percent=$(echo "scale=1; $total_used * 100 / $total_capacity" | bc -l 2>/dev/null)
    cluster_usage_percent="${cluster_usage_percent}%"
else
    cluster_usage_percent="N/A"
fi

echo -e "${BLUE}=== 요약 정보 ===${NC}"
echo "총 노드 수: $total_nodes"
echo "준비된 노드: $ready_nodes"
echo "클러스터 총 사용량: $total_used_display $UNIT"
echo "클러스터 총 용량: $total_capacity_display $UNIT"
echo "클러스터 사용률: $cluster_usage_percent"
echo "과부하 노드: $overloaded_nodes (사용률 >= $THRESHOLD%)"

if [[ "$VERBOSE" == true ]]; then
    echo ""
    echo -e "${YELLOW}상세 정보:${NC}"
    echo "- 네임스페이스: ${NAMESPACE:-전체}"
    echo "- 정렬 방식: $([ "$SORT_BY_USAGE" == true ] && echo "디스크 사용량" || echo "노드명")"
    echo "- 출력 형식: $FORMAT"
    echo "- 용량 단위: $UNIT"
    echo "- 임계값: $THRESHOLD%"
    echo ""
    echo -e "${YELLOW}주의사항:${NC}"
    echo "- 디스크 사용량은 추정값입니다 (실제 메트릭 수집 제한)"
    echo "- 컨테이너당 평균 100MB로 추정하여 계산"
    echo "- 정확한 측정을 위해서는 모니터링 도구 사용을 권장합니다"
fi

}

메인 실행

calculate_disk_usage

profile
bytebliss

0개의 댓글