#!/bin/bash
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
if ! command -v kubectl &> /dev/null; then
echo -e "{NC}"
exit 1
fi
if ! kubectl cluster-info &> /dev/null; then
echo -e "{NC}"
exit 1
fi
if ! kubectl top node &> /dev/null; then
echo -e "{NC}"
echo -e "{NC}"
echo ""
fi
echo -e "{NC}"
echo ""
NS_OPTION=""
if [[ -n "$NAMESPACE" ]]; then
NS_OPTION="--namespace={YELLOW}네임스페이스 필터: {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)" "사용률" "컨테이너" "상태"
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="{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"
}
output_json() {
echo "{"
echo " \"timestamp\": \"{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 "}"
}
output_csv() {
echo "노드명,사용량(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