bash
#!/bin/bash
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 日志函数
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] 错误: $1${NC}"
}
warn() {
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] 警告: $1${NC}"
}
info() {
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
# 检查 kubectl
check_kubectl() {
if ! command -v kubectl &> /dev/null; then
error "kubectl 未安装或不在 PATH 中"
exit 1
fi
# 检查集群连接
if ! kubectl cluster-info &> /dev/null; then
error "无法连接到 Kubernetes 集群"
exit 1
fi
}
# 获取 PV 列表
get_pv_list() {
kubectl get pv --no-headers 2>/dev/null | while read line; do
if [ -n "$line" ]; then
echo "$line"
fi
done
}
# 获取 PVC 列表(所有命名空间)
get_pvc_list() {
kubectl get pvc --all-namespaces --no-headers 2>/dev/null | while read line; do
if [ -n "$line" ]; then
echo "$line"
fi
done
}
# 显示并选择 PV
select_and_delete_pv() {
local pvs=()
local pv_count=0
# 获取 PV 列表到数组
while IFS= read -r line; do
if [ -n "$line" ]; then
pvs+=("$line")
fi
done < <(get_pv_list)
pv_count=${#pvs[@]}
if [ $pv_count -eq 0 ]; then
warn "没有找到 PV"
return
fi
echo -e "\n${CYAN}=== 可用的 PV 列表 ===${NC}"
printf "%-3s %-20s %-10s %-10s %-15s %-12s %s\n" "ID" "NAME" "CAPACITY" "ACCESS" "RECLAIM" "STATUS" "CLAIM"
echo "----------------------------------------------------------------------------------------"
for i in "${!pvs[@]}"; do
IFS=' ' read -r name capacity access_mode reclaim_policy status claim <<< "${pvs[$i]}"
printf "%-3d %-20s %-10s %-10s %-15s %-12s %s\n" \
$((i+1)) "$name" "$capacity" "$access_mode" "$reclaim_policy" "$status" "$claim"
done
while true; do
echo
echo -e "${YELLOW}选择操作:${NC}"
echo " 1-${pv_count} : 删除对应的 PV"
echo " a : 删除所有 PV"
echo " r : 刷新列表"
echo " b : 返回上级菜单"
echo " q : 退出程序"
read -r -p "请输入选择: " choice
case "$choice" in
[qQ])
log "退出程序"
exit 0
;;
[bB])
return
;;
[rR])
select_and_delete_pv
return
;;
[aA])
warn "即将删除所有 PV!此操作不可逆!"
read -r -p "确认删除所有 PV? (输入 'YES' 确认): " confirm
if [ "$confirm" = "YES" ]; then
for pv_line in "${pvs[@]}"; do
pv_name=$(echo "$pv_line" | awk '{print $1}')
log "删除 PV: $pv_name"
kubectl delete pv "$pv_name"
done
log "所有 PV 删除操作已完成"
else
log "取消删除所有 PV"
fi
;;
*)
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "$pv_count" ]; then
pv_line="${pvs[$((choice-1))]}"
pv_name=$(echo "$pv_line" | awk '{print $1}')
pv_status=$(echo "$pv_line" | awk '{print $5}')
echo -e "${YELLOW}选择的 PV 信息:${NC}"
echo " 名称: $pv_name"
echo " 状态: $pv_status"
echo " 完整信息: $pv_line"
read -r -p "确认删除这个 PV? (y/N): " confirm
if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
log "删除 PV: $pv_name"
kubectl delete pv "$pv_name"
if [ $? -eq 0 ]; then
log "✓ 成功删除 PV: $pv_name"
else
error "✗ 删除 PV 失败: $pv_name"
fi
else
log "取消删除 PV: $pv_name"
fi
else
error "无效的选择,请输入 1-${pv_count} 之间的数字,或 a/r/b/q"
fi
;;
esac
done
}
# 显示并选择 PVC
select_and_delete_pvc() {
local pvcs=()
local pvc_count=0
# 获取 PVC 列表到数组
while IFS= read -r line; do
if [ -n "$line" ]; then
pvcs+=("$line")
fi
done < <(get_pvc_list)
pvc_count=${#pvcs[@]}
if [ $pvc_count -eq 0 ]; then
warn "没有找到 PVC"
return
fi
echo -e "\n${PURPLE}=== 可用的 PVC 列表 (所有命名空间) ===${NC}"
printf "%-3s %-15s %-20s %-10s %-12s %-15s %s\n" "ID" "NAMESPACE" "NAME" "STATUS" "VOLUME" "CAPACITY" "ACCESS"
echo "------------------------------------------------------------------------------------------------"
for i in "${!pvcs[@]}"; do
IFS=' ' read -r namespace name status volume capacity access_mode <<< "${pvcs[$i]}"
printf "%-3d %-15s %-20s %-10s %-12s %-15s %s\n" \
$((i+1)) "$namespace" "$name" "$status" "$volume" "$capacity" "$access_mode"
done
while true; do
echo
echo -e "${YELLOW}选择操作:${NC}"
echo " 1-${pvc_count} : 删除对应的 PVC"
echo " a : 删除所有 PVC"
echo " r : 刷新列表"
echo " b : 返回上级菜单"
echo " q : 退出程序"
read -r -p "请输入选择: " choice
case "$choice" in
[qQ])
log "退出程序"
exit 0
;;
[bB])
return
;;
[rR])
select_and_delete_pvc
return
;;
[aA])
warn "即将删除所有 PVC!此操作不可逆!"
read -r -p "确认删除所有 PVC? (输入 'YES' 确认): " confirm
if [ "$confirm" = "YES" ]; then
for pvc_line in "${pvcs[@]}"; do
pvc_namespace=$(echo "$pvc_line" | awk '{print $1}')
pvc_name=$(echo "$pvc_line" | awk '{print $2}')
log "删除 PVC: $pvc_namespace/$pvc_name"
kubectl delete pvc "$pvc_name" -n "$pvc_namespace"
done
log "所有 PVC 删除操作已完成"
else
log "取消删除所有 PVC"
fi
;;
*)
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "$pvc_count" ]; then
pvc_line="${pvcs[$((choice-1))]}"
pvc_namespace=$(echo "$pvc_line" | awk '{print $1}')
pvc_name=$(echo "$pvc_line" | awk '{print $2}')
pvc_status=$(echo "$pvc_line" | awk '{print $3}')
pvc_volume=$(echo "$pvc_line" | awk '{print $4}')
echo -e "${YELLOW}选择的 PVC 信息:${NC}"
echo " 命名空间: $pvc_namespace"
echo " 名称: $pvc_name"
echo " 状态: $pvc_status"
echo " 关联 Volume: $pvc_volume"
echo " 完整信息: $pvc_line"
read -r -p "确认删除这个 PVC? (y/N): " confirm
if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
log "删除 PVC: $pvc_namespace/$pvc_name"
kubectl delete pvc "$pvc_name" -n "$pvc_namespace"
if [ $? -eq 0 ]; then
log "✓ 成功删除 PVC: $pvc_namespace/$pvc_name"
else
error "✗ 删除 PVC 失败: $pvc_namespace/$pvc_name"
fi
else
log "取消删除 PVC: $pvc_namespace/$pvc_name"
fi
else
error "无效的选择,请输入 1-${pvc_count} 之间的数字,或 a/r/b/q"
fi
;;
esac
done
}
# 主菜单
main_menu() {
while true; do
echo -e "\n${BLUE}=== Kubernetes PV/PVC 管理工具 ===${NC}"
echo -e "${CYAN}请选择操作类型:${NC}"
echo " 1. 管理 PV (PersistentVolume)"
echo " 2. 管理 PVC (PersistentVolumeClaim)"
echo " 3. 查看集群存储状态"
echo " q. 退出程序"
read -r -p "请输入选择 (1-3 或 q): " main_choice
case "$main_choice" in
1)
select_and_delete_pv
;;
2)
select_and_delete_pvc
;;
3)
echo -e "\n${GREEN}=== 集群存储状态 ===${NC}"
echo -e "${YELLOW}--- PV 状态 ---${NC}"
kubectl get pv 2>/dev/null || error "获取 PV 状态失败"
echo -e "\n${YELLOW}--- PVC 状态 (所有命名空间) ---${NC}"
kubectl get pvc --all-namespaces 2>/dev/null || error "获取 PVC 状态失败"
;;
[qQ])
log "退出程序"
exit 0
;;
*)
error "无效的选择,请输入 1-3 或 q"
;;
esac
done
}
# 脚本开始
check_kubectl
log "连接到 Kubernetes 集群成功"
main_menu
如果有格式不正确
方法一:使用 dos2unix 转换(推荐)
安装 dos2unix
bash
yum install -y dos2unix
bash
使用 wget 下载
# 对于 CentOS 7
wget http://mirror.centos.org/centos/7/os/x86_64/Packages/dos2unix-7.4.0-1.el7.x86_64.rpm
# 对于 CentOS 8
wget http://mirror.centos.org/centos/8/BaseOS/x86_64/os/Packages/dos2unix-7.4.2-1.el8.x86_64.rpm
# 对于 Rocky/AlmaLinux 8
wget https://download.rockylinux.org/pub/rocky/8/BaseOS/x86_64/os/Packages/d/dos2unix-7.4.2-1.el8.x86_64.rpm
转换文件格式
bash
dos2unix k8s-pv-pvc-manager.sh

重新运行
bash
chmod +x k8s-pv-pvc-manager.sh
./k8s-pv-pvc-manager.sh
方法二:使用 sed 命令删除 CR 字符
bash
# 删除 CR 字符
sed -i 's/\r$//' k8s-pv-pvc-manager.sh
重新运行
bash
chmod +x k8s-pv-pvc-manager.sh
./k8s-pv-pvc-manager.sh
方法三:使用 vim 转换
用 vim 打开文件
bash
vim k8s-pv-pvc-manager.sh
在 vim 中执行以下命令:
bash
# :set ff=unix
# :wq
重新运行
bash
chmod +x k8s-pv-pvc-manager.sh
./k8s-pv-pvc-manager.sh
方法四:重新创建脚本
如果上述方法都不行,可以直接重新创建脚本:
bash
# 删除原文件
rm -f k8s-pv-pvc-manager.sh
# 使用 cat 命令重新创建(确保在 Linux 环境中执行)
cat > k8s-pv-pvc-manager.sh << 'EOF'
#!/bin/bash
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 日志函数
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] 错误: $1${NC}"
}
warn() {
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] 警告: $1${NC}"
}
info() {
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
# 检查 kubectl
check_kubectl() {
if ! command -v kubectl &> /dev/null; then
error "kubectl 未安装或不在 PATH 中"
exit 1
fi
# 检查集群连接
if ! kubectl cluster-info &> /dev/null; then
error "无法连接到 Kubernetes 集群"
exit 1
fi
}
# 获取 PV 列表
get_pv_list() {
kubectl get pv --no-headers 2>/dev/null
}
# 获取 PVC 列表(所有命名空间)
get_pvc_list() {
kubectl get pvc --all-namespaces --no-headers 2>/dev/null
}
# 显示并选择 PV
select_and_delete_pv() {
local pvs=()
local pv_count=0
# 获取 PV 列表到数组
while IFS= read -r line; do
if [ -n "$line" ]; then
pvs+=("$line")
fi
done < <(get_pv_list)
pv_count=${#pvs[@]}
if [ $pv_count -eq 0 ]; then
warn "没有找到 PV"
return
fi
echo -e "\n${CYAN}=== 可用的 PV 列表 ===${NC}"
printf "%-3s %-20s %-10s %-10s %-15s %-12s %s\n" "ID" "NAME" "CAPACITY" "ACCESS" "RECLAIM" "STATUS" "CLAIM"
echo "----------------------------------------------------------------------------------------"
for i in "${!pvs[@]}"; do
IFS=' ' read -r name capacity access_mode reclaim_policy status claim <<< "${pvs[$i]}"
printf "%-3d %-20s %-10s %-10s %-15s %-12s %s\n" \
$((i+1)) "$name" "$capacity" "$access_mode" "$reclaim_policy" "$status" "$claim"
done
while true; do
echo
echo -e "${YELLOW}选择操作:${NC}"
echo " 1-${pv_count} : 删除对应的 PV"
echo " a : 删除所有 PV"
echo " r : 刷新列表"
echo " b : 返回上级菜单"
echo " q : 退出程序"
read -r -p "请输入选择: " choice
case "$choice" in
[qQ])
log "退出程序"
exit 0
;;
[bB])
return
;;
[rR])
select_and_delete_pv
return
;;
[aA])
warn "即将删除所有 PV!此操作不可逆!"
read -r -p "确认删除所有 PV? (输入 'YES' 确认): " confirm
if [ "$confirm" = "YES" ]; then
for pv_line in "${pvs[@]}"; do
pv_name=$(echo "$pv_line" | awk '{print $1}')
log "删除 PV: $pv_name"
kubectl delete pv "$pv_name"
done
log "所有 PV 删除操作已完成"
else
log "取消删除所有 PV"
fi
;;
*)
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "$pv_count" ]; then
pv_line="${pvs[$((choice-1))]}"
pv_name=$(echo "$pv_line" | awk '{print $1}')
pv_status=$(echo "$pv_line" | awk '{print $5}')
echo -e "${YELLOW}选择的 PV 信息:${NC}"
echo " 名称: $pv_name"
echo " 状态: $pv_status"
echo " 完整信息: $pv_line"
read -r -p "确认删除这个 PV? (y/N): " confirm
if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
log "删除 PV: $pv_name"
kubectl delete pv "$pv_name"
if [ $? -eq 0 ]; then
log "✓ 成功删除 PV: $pv_name"
else
error "✗ 删除 PV 失败: $pv_name"
fi
else
log "取消删除 PV: $pv_name"
fi
else
error "无效的选择,请输入 1-${pv_count} 之间的数字,或 a/r/b/q"
fi
;;
esac
done
}
# 显示并选择 PVC
select_and_delete_pvc() {
local pvcs=()
local pvc_count=0
# 获取 PVC 列表到数组
while IFS= read -r line; do
if [ -n "$line" ]; then
pvcs+=("$line")
fi
done < <(get_pvc_list)
pvc_count=${#pvcs[@]}
if [ $pvc_count -eq 0 ]; then
warn "没有找到 PVC"
return
fi
echo -e "\n${PURPLE}=== 可用的 PVC 列表 (所有命名空间) ===${NC}"
printf "%-3s %-15s %-20s %-10s %-12s %-15s %s\n" "ID" "NAMESPACE" "NAME" "STATUS" "VOLUME" "CAPACITY" "ACCESS"
echo "------------------------------------------------------------------------------------------------"
for i in "${!pvcs[@]}"; do
IFS=' ' read -r namespace name status volume capacity access_mode <<< "${pvcs[$i]}"
printf "%-3d %-15s %-20s %-10s %-12s %-15s %s\n" \
$((i+1)) "$namespace" "$name" "$status" "$volume" "$capacity" "$access_mode"
done
while true; do
echo
echo -e "${YELLOW}选择操作:${NC}"
echo " 1-${pvc_count} : 删除对应的 PVC"
echo " a : 删除所有 PVC"
echo " r : 刷新列表"
echo " b : 返回上级菜单"
echo " q : 退出程序"
read -r -p "请输入选择: " choice
case "$choice" in
[qQ])
log "退出程序"
exit 0
;;
[bB])
return
;;
[rR])
select_and_delete_pvc
return
;;
[aA])
warn "即将删除所有 PVC!此操作不可逆!"
read -r -p "确认删除所有 PVC? (输入 'YES' 确认): " confirm
if [ "$confirm" = "YES" ]; then
for pvc_line in "${pvcs[@]}"; do
pvc_namespace=$(echo "$pvc_line" | awk '{print $1}')
pvc_name=$(echo "$pvc_line" | awk '{print $2}')
log "删除 PVC: $pvc_namespace/$pvc_name"
kubectl delete pvc "$pvc_name" -n "$pvc_namespace"
done
log "所有 PVC 删除操作已完成"
else
log "取消删除所有 PVC"
fi
;;
*)
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "$pvc_count" ]; then
pvc_line="${pvcs[$((choice-1))]}"
pvc_namespace=$(echo "$pvc_line" | awk '{print $1}')
pvc_name=$(echo "$pvc_line" | awk '{print $2}')
pvc_status=$(echo "$pvc_line" | awk '{print $3}")
pvc_volume=$(echo "$pvc_line" | awk '{print $4}")
echo -e "${YELLOW}选择的 PVC 信息:${NC}"
echo " 命名空间: $pvc_namespace"
echo " 名称: $pvc_name"
echo " 状态: $pvc_status"
echo " 关联 Volume: $pvc_volume"
echo " 完整信息: $pvc_line"
read -r -p "确认删除这个 PVC? (y/N): " confirm
if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
log "删除 PVC: $pvc_namespace/$pvc_name"
kubectl delete pvc "$pvc_name" -n "$pvc_namespace"
if [ $? -eq 0 ]; then
log "✓ 成功删除 PVC: $pvc_namespace/$pvc_name"
else
error "✗ 删除 PVC 失败: $pvc_namespace/$pvc_name"
fi
else
log "取消删除 PVC: $pvc_namespace/$pvc_name"
fi
else
error "无效的选择,请输入 1-${pvc_count} 之间的数字,或 a/r/b/q"
fi
;;
esac
done
}
# 主菜单
main_menu() {
while true; do
echo -e "\n${BLUE}=== Kubernetes PV/PVC 管理工具 ===${NC}"
echo -e "${CYAN}请选择操作类型:${NC}"
echo " 1. 管理 PV (PersistentVolume)"
echo " 2. 管理 PVC (PersistentVolumeClaim)"
echo " 3. 查看集群存储状态"
echo " q. 退出程序"
read -r -p "请输入选择 (1-3 或 q): " main_choice
case "$main_choice" in
1)
select_and_delete_pv
;;
2)
select_and_delete_pvc
;;
3)
echo -e "\n${GREEN}=== 集群存储状态 ===${NC}"
echo -e "${YELLOW}--- PV 状态 ---${NC}"
kubectl get pv 2>/dev/null || error "获取 PV 状态失败"
echo -e "\n${YELLOW}--- PVC 状态 (所有命名空间) ---${NC}"
kubectl get pvc --all-namespaces 2>/dev/null || error "获取 PVC 状态失败"
;;
[qQ])
log "退出程序"
exit 0
;;
*)
error "无效的选择,请输入 1-3 或 q"
;;
esac
done
}
# 脚本开始
check_kubectl
log "连接到 Kubernetes 集群成功"
main_menu
EOF
# 设置执行权限并运行
chmod +x k8s-pv-pvc-manager.sh
./k8s-pv-pvc-manager.sh