#!/bin/bash set -euo pipefail # 颜色输出函数 red_echo() { echo -e "\033[31m$1\033[0m"; } green_echo() { echo -e "\033[32m$1\033[0m"; } yellow_echo() { echo -e "\033[33m$1\033[0m"; } # 强制清理函数 force_clean() { local cmd=$* if eval "$cmd"; then green_echo " 执行成功: $cmd" else yellow_echo " 执行失败,但继续: $cmd" fi } # ======================== # 核心函数:彻底清理tunl0接口 # ======================== clean_tunl0() { green_echo " 专项清理tunl0接口..." # 尝试1:常规停用+删除 force_clean "ip link set tunl0 down || true" force_clean "ip link delete tunl0 || true" # 尝试2:卸载ipip模块后删除(tunl0依赖的内核模块) force_clean "rmmod ipip || true" force_clean "ip link delete tunl0 || true" # 尝试3:检查是否有残留的tunl0配置并删除 if ip link show tunl0 &> /dev/null; then force_clean "ip link set tunl0 nomaster || true" # 移除主接口关联 force_clean "ip link delete tunl0 || true" fi } # ======================== # 第一阶段:强制清理核心组件 # ======================== cleanup_core() { green_echo "===== 第一阶段:清理核心K8s组件 =====" # 1. 强制终止所有相关进程 green_echo "1. 强制终止K8s/容器相关进程..." force_clean "ps -aux | grep -E 'kube|etcd|containerd|docker|cni|flannel|tunl0' | grep -v grep | awk '{print \$2}' | xargs -r kill -9" force_clean "pkill -f 'kube|etcd|containerd|docker|cni|flannel' || true" # 2. 强制清理网络资源(优先处理tunl0) green_echo "2. 强制清理网络资源..." clean_tunl0 # 调用专项清理函数 # 清理其他网络接口 force_clean "ip link set flannel.1 down || true" force_clean "ip link delete flannel.1 || true" force_clean "ip link set cni0 down || true" force_clean "ip link delete cni0 || true" force_clean "ip link set docker0 down || true" force_clean "ip link delete docker0 || true" # 清理路由和规则 force_clean "ip route flush proto bird || true" force_clean "rm -rf /var/lib/cni/* /etc/cni/net.d/* || true" force_clean "iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X || true" force_clean "ip6tables -F && ip6tables -t nat -F && ip6tables -t mangle -F && ip6tables -X || true" force_clean "ipvsadm --clear || true" # 3. 强制重置K8s配置 green_echo "3. 强制重置K8s配置..." force_clean "kubeadm reset -f --cri-socket unix:///var/run/containerd/containerd.sock || true" force_clean "kubeadm reset -f --cri-socket unix:///var/run/docker.sock || true" force_clean "rm -f /etc/kubernetes/flannel/* || true" } # ======================== # 第二阶段:彻底卸载软件包 # ======================== cleanup_packages() { green_echo "\n===== 第二阶段:彻底卸载软件包 =====" force_clean "apt-get purge -y kubelet kubectl kubeadm kubernetes-cni cri-tools --allow-change-held-packages || true" force_clean "dpkg -P kubelet kubectl kubeadm kubernetes-cni cri-tools || true" force_clean "apt-get purge -y containerd containerd.io docker-ce docker-ce-cli docker-buildx-plugin docker-compose-plugin --allow-change-held-packages || true" force_clean "dpkg -P containerd containerd.io docker-ce docker-ce-cli || true" force_clean "apt-get autoremove -y --purge || true" force_clean "apt-get autoclean || true" } # ======================== # 第三阶段:删除所有相关目录 # ======================== cleanup_directories() { green_echo "\n===== 第三阶段:删除所有相关目录 =====" # 卸载containerd挂载点 green_echo " 卸载containerd挂载点..." local containerd_mounts=$(mount | grep "containerd" | awk '{print $3}') if [ -n "$containerd_mounts" ]; then force_clean "echo '$containerd_mounts' | xargs -I {} umount -l {} || true" else green_echo " 无containerd挂载点需要卸载" fi # 卸载SHM挂载点 green_echo " 卸载共享内存(SHM)挂载点..." local shm_mounts=$(mount | grep "tmpfs.*type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k,mode=700)" | awk '{print $3}') if [ -n "$shm_mounts" ]; then force_clean "echo '$shm_mounts' | xargs -I {} umount -l {} || true" else green_echo " 无SHM挂载点需要卸载" fi # 处理活跃的SHM文件(临时关闭set -e避免退出) green_echo " 删除所有活跃的SHM文件..." set +e for pid in $(ls -1 /proc/ 2>/dev/null | grep -E '^[0-9]+$' || true); do if [ -f "/proc/$pid/mounts" ] && [ -r "/proc/$pid/mounts" ]; then grep "shm" "/proc/$pid/mounts" 2>/dev/null | grep "containerd" 2>/dev/null | while read -r line; do shm_path=$(echo "$line" | awk '{print $2}') if [ -n "$shm_path" ] && [ -d "$shm_path" ]; then umount -l "$shm_path" 2>/dev/null || true echo " 已尝试卸载SHM: $shm_path" fi done fi done set -e # 清理/k8sdata目录内容 green_echo " 清理/k8sdata目录内容..." force_clean "rm -rf /k8sdata/* /k8sdata/.* || true" # 清理containerd残留目录 green_echo " 清理containerd容器残留..." force_clean "rm -rf /run/containerd/io.containerd.grpc.v1.cri/sandboxes/* || true" force_clean "rm -rf /run/containerd/io.containerd.runtime.v2.task/k8s.io/* || true" # 清理其他目录 local dirs=( /etc/kubernetes /var/lib/kubelet /var/lib/kubernetes /var/lib/etcd /var/lib/kube-proxy /var/lib/kubeadm /var/lib/cni /var/lib/containerd /var/lib/docker /run/containerd /run/docker /etc/containerd /etc/docker /usr/local/bin/kube* /usr/local/bin/etcd* $HOME/.kube /root/.kube /var/lib/flannel /etc/flannel ) for dir in "${dirs[@]}"; do force_clean "rm -rf $dir || true" if [ -d "$dir" ]; then force_clean "rm -rf --one-file-system $dir || true" fi done } # ======================== # 第四阶段:清理系统服务 # ======================== cleanup_services() { green_echo "\n===== 第四阶段:清理系统服务 =====" local services=(kubelet kube-apiserver kube-controller-manager kube-scheduler kube-proxy etcd containerd docker) for service in "${services[@]}"; do force_clean "systemctl stop $service || true" force_clean "systemctl disable $service || true" force_clean "rm -f /lib/systemd/system/$service.service || true" force_clean "rm -f /etc/systemd/system/$service.service || true" force_clean "rm -f /etc/systemd/system/multi-user.target.wants/$service.service || true" done force_clean "systemctl daemon-reload || true" force_clean "systemctl reset-failed || true" } # ======================== # 最终验证:确保无残留(含tunl0专项检查) # ======================== verify_perfect_cleanup() { green_echo "\n===== 最终验证:确保无残留 =====" local is_perfect=true # 1. 验证进程无残留 green_echo "1. 验证进程无残留..." local processes=$(ps -aux | grep -E 'kube|etcd|containerd|docker|cni|flannel' | grep -v grep) if [ -n "$processes" ]; then red_echo " ❌ 发现残留进程:" echo "$processes" is_perfect=false else green_echo " ✔️ 无残留进程" fi # 2. 验证命令无残留 green_echo "2. 验证命令无残留..." local cmds=(kubectl kubelet containerd docker etcd kubeadm) for cmd in "${cmds[@]}"; do sleep 1 # 确保命令列表更新 if command -v "$cmd" &> /dev/null; then red_echo " ❌ 命令 $cmd 仍存在" is_perfect=false fi done if [ "$is_perfect" = true ]; then green_echo " ✔️ 无残留命令" fi # 3. 验证目录无残留 green_echo "3. 验证目录无残留..." # 验证/k8sdata是否为空 if [ "$(ls -A /k8sdata 2>/dev/null)" ]; then red_echo " ❌ 目录/k8sdata不为空" is_perfect=false else green_echo " ✔️ 目录/k8sdata为空" fi # 验证containerd目录是否为空 if [ "$(ls -A /run/containerd/io.containerd.grpc.v1.cri/sandboxes 2>/dev/null)" ]; then red_echo " ❌ containerd沙箱目录不为空" is_perfect=false else green_echo " ✔️ containerd沙箱目录为空" fi if [ "$(ls -A /run/containerd/io.containerd.runtime.v2.task/k8s.io 2>/dev/null)" ]; then red_echo " ❌ containerd运行时目录不为空" is_perfect=false else green_echo " ✔️ containerd运行时目录为空" fi # 验证其他目录 local other_dirs=( /etc/kubernetes /var/lib/kubelet /var/lib/etcd /var/lib/cni /var/lib/containerd /var/lib/docker $HOME/.kube /var/lib/flannel ) for dir in "${other_dirs[@]}"; do if [ -d "$dir" ] || [ -f "$dir" ]; then red_echo " ❌ 目录/文件 $dir 仍存在" is_perfect=false fi done # 4. 验证网络无残留(核心:tunl0专项检查+最后清理) green_echo "4. 验证网络无残留..." # 最后一次尝试清理tunl0(防止验证前重新出现) clean_tunl0 # 检查残留接口 local interfaces=$(ip link show | grep -E 'cni0|flannel.1|docker0|tunl0' | awk '{print $2}' | sed 's/://') if [ -n "$interfaces" ]; then red_echo " ❌ 发现残留网络接口: $interfaces (如果是tunl0请忽略)" is_perfect=false else green_echo " ✔️ 无残留网络接口" fi # 最终结果 if [ "$is_perfect" = true ]; then green_echo "\n🎉 完美清理!系统中已无任何K8s相关残留,可安全重新安装。" else red_echo "\n❌ 清理不彻底!以上残留项需手动处理。" exit 1 fi } # ======================== # 执行完整流程 # ======================== green_echo "===== 开始K8s彻底清理流程 =====" cleanup_core cleanup_packages cleanup_directories cleanup_services sleep 20 # 延长等待,确保内核释放所有资源 verify_perfect_cleanup