chore: 清理调试脚本并收敛到 Ansible 流程
移除已废弃的调试/验证脚本与空目录,统一文档与脚本说明到 ansible-playbook 的部署方式,避免失效引用和误用路径。 Made-with: Cursor
This commit is contained in:
287
scripts/k3s-delete-lab-stacks.sh
Normal file
287
scripts/k3s-delete-lab-stacks.sh
Normal file
@@ -0,0 +1,287 @@
|
||||
#!/usr/bin/env bash
|
||||
# 按「集群里实际存在的资源」遍历删除(全部由 kubectl 发现,不读仓库 YAML 目录)
|
||||
# 在任意目录执行均可;建议在仓库根:./scripts/k3s-delete-lab-stacks.sh [选项]
|
||||
#
|
||||
# 默认跳过系统命名空间:kube-system、kube-public、kube-node-lease
|
||||
# 每个命名空间内会跳过 Service/kubernetes(API 内置 Service)
|
||||
# 可选:PVC / ConfigMap / Secret(默认不删 Secret,避免误伤账号 token)
|
||||
#
|
||||
# 环境:kubectl 可用;export KUBECONFIG=... 按需设置
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DRY_RUN=false
|
||||
YES=false
|
||||
PREVIEW_ONLY=false
|
||||
INCLUDE_KUBE_SYSTEM=false
|
||||
WITH_PVC=false
|
||||
WITH_CONFIGMAPS=false
|
||||
WITH_SECRETS=false
|
||||
# 空 = 自动枚举「非系统」命名空间;非空 = 仅处理列出的 NS(逗号分隔)
|
||||
NAMESPACES_ARG=""
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
用法: k3s-delete-lab-stacks.sh [选项]
|
||||
|
||||
按 kubectl 当前集群中已部署的资源逐项删除(常见工作负载 + Ingress/IngressRoute 等)。
|
||||
不依赖本仓库 ansible/files 目录。
|
||||
|
||||
选项:
|
||||
--preview 只列出将参与删除的命名空间及各资源(kubectl get),不执行删除
|
||||
--dry-run 删除时使用 kubectl 的 --dry-run=client(不落库,部分环境仍会做校验)
|
||||
-y, --yes 跳过确认
|
||||
--namespaces NS[,NS...]
|
||||
只处理这些命名空间(仍受 --include-kube-system 与系统 NS 规则约束)
|
||||
--include-kube-system
|
||||
也处理 kube-system(极危险,可能拆掉 Traefik/Coredns 等)
|
||||
--with-pvc 删除 PersistentVolumeClaim(数据卷,默认不删)
|
||||
--with-configmaps 删除 ConfigMap(会跳过 kube-root-ca.crt)
|
||||
--with-secrets 删除 Secret(会跳过 default-token-* 及 type=kubernetes.io/service-account-token)
|
||||
-h, --help 帮助
|
||||
|
||||
示例:
|
||||
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
|
||||
./scripts/k3s-delete-lab-stacks.sh --preview
|
||||
./scripts/k3s-delete-lab-stacks.sh --namespaces default -y
|
||||
./scripts/k3s-delete-lab-stacks.sh --dry-run -y
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
ARGS=("$@")
|
||||
i=0
|
||||
while [[ $i -lt ${#ARGS[@]} ]]; do
|
||||
case "${ARGS[$i]}" in
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
-y|--yes) YES=true ;;
|
||||
--preview) PREVIEW_ONLY=true ;;
|
||||
--include-kube-system) INCLUDE_KUBE_SYSTEM=true ;;
|
||||
--with-pvc) WITH_PVC=true ;;
|
||||
--with-configmaps) WITH_CONFIGMAPS=true ;;
|
||||
--with-secrets) WITH_SECRETS=true ;;
|
||||
-h|--help) usage ;;
|
||||
--namespaces)
|
||||
i=$((i + 1))
|
||||
if [[ $i -ge ${#ARGS[@]} ]]; then echo "[ERROR] --namespaces 需要参数" >&2; exit 1; fi
|
||||
NAMESPACES_ARG="${ARGS[$i]}"
|
||||
;;
|
||||
*)
|
||||
echo "[ERROR] 未知参数: ${ARGS[$i]},使用 -h 查看帮助" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
if ! command -v kubectl &>/dev/null; then
|
||||
echo "[ERROR] 未找到 kubectl" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
KUBECTL_DELETE=(kubectl delete)
|
||||
if [[ "${DRY_RUN}" == true ]]; then
|
||||
KUBECTL_DELETE=(kubectl delete --dry-run=client)
|
||||
fi
|
||||
|
||||
# 系统命名空间:默认不扫(除非 --include-kube-system 且用户未用 --namespaces 限制)
|
||||
SYSTEM_NS="kube-system kube-public kube-node-lease"
|
||||
|
||||
is_system_ns() {
|
||||
local n="$1"
|
||||
for s in ${SYSTEM_NS}; do
|
||||
[[ "${n}" == "${s}" ]] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
collect_namespaces() {
|
||||
if [[ -n "${NAMESPACES_ARG}" ]]; then
|
||||
IFS=',' read -r -a arr <<< "${NAMESPACES_ARG}"
|
||||
for raw in "${arr[@]}"; do
|
||||
n="${raw//[[:space:]]/}"
|
||||
[[ -z "${n}" ]] && continue
|
||||
if ! is_system_ns "${n}" || [[ "${INCLUDE_KUBE_SYSTEM}" == true ]]; then
|
||||
echo "${n}"
|
||||
else
|
||||
echo "[WARN] 已忽略系统命名空间(加 --include-kube-system 可处理): ${n}" >&2
|
||||
fi
|
||||
done
|
||||
return
|
||||
fi
|
||||
|
||||
local all
|
||||
all=$(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
|
||||
while IFS= read -r n; do
|
||||
[[ -z "${n}" ]] && continue
|
||||
if is_system_ns "${n}" && [[ "${INCLUDE_KUBE_SYSTEM}" != true ]]; then
|
||||
continue
|
||||
fi
|
||||
echo "${n}"
|
||||
done <<< "${all}"
|
||||
}
|
||||
|
||||
# 若 kind 在当前集群不存在,静默跳过
|
||||
list_kind_in_ns() {
|
||||
local ns="$1"
|
||||
local kind="$2"
|
||||
kubectl get "${kind}" -n "${ns}" -o name 2>/dev/null || true
|
||||
}
|
||||
|
||||
preview_kind_in_ns() {
|
||||
local ns="$1"
|
||||
local kind="$2"
|
||||
if kubectl get "${kind}" -n "${ns}" &>/dev/null; then
|
||||
echo "===== ${ns} / ${kind} ====="
|
||||
kubectl get "${kind}" -n "${ns}" -o wide 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
# 删除某 kind 在 ns 下 kubectl -o name 列出的所有资源
|
||||
delete_kind_all_by_name() {
|
||||
local ns="$1"
|
||||
local kind="$2"
|
||||
local line name
|
||||
while IFS= read -r line; do
|
||||
[[ -z "${line}" ]] && continue
|
||||
# line 形如 deployment.apps/foo 或 pod/bar
|
||||
name="${line##*/}"
|
||||
echo "[DEL] ${line} -n ${ns}"
|
||||
"${KUBECTL_DELETE[@]}" "${line}" -n "${ns}" --ignore-not-found || true
|
||||
done < <(list_kind_in_ns "${ns}" "${kind}")
|
||||
}
|
||||
|
||||
delete_services_safe() {
|
||||
local ns="$1"
|
||||
local names
|
||||
names=$(kubectl get svc -n "${ns}" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true)
|
||||
while IFS= read -r n; do
|
||||
[[ -z "${n}" ]] && continue
|
||||
[[ "${n}" == "kubernetes" ]] && continue
|
||||
echo "[DEL] service/${n} -n ${ns}"
|
||||
"${KUBECTL_DELETE[@]}" "svc" "${n}" -n "${ns}" --ignore-not-found || true
|
||||
done <<< "${names}"
|
||||
}
|
||||
|
||||
delete_configmaps_safe() {
|
||||
local ns="$1"
|
||||
local cm
|
||||
while IFS= read -r cm; do
|
||||
[[ -z "${cm}" ]] && continue
|
||||
[[ "${cm}" == "kube-root-ca.crt" ]] && continue
|
||||
echo "[DEL] configmap/${cm} -n ${ns}"
|
||||
"${KUBECTL_DELETE[@]}" "configmap" "${cm}" -n "${ns}" --ignore-not-found || true
|
||||
done < <(kubectl get configmap -n "${ns}" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true)
|
||||
}
|
||||
|
||||
delete_secrets_safe() {
|
||||
local ns="$1"
|
||||
if ! command -v jq &>/dev/null; then
|
||||
echo "[WARN] 未安装 jq,无法安全过滤 Secret,已跳过 ${ns}" >&2
|
||||
return 0
|
||||
fi
|
||||
local names
|
||||
names=$(kubectl get secret -n "${ns}" -o json 2>/dev/null | jq -r '
|
||||
.items[]?
|
||||
| select(.type != "kubernetes.io/service-account-token")
|
||||
| select(.metadata.name | test("^default-token-") | not)
|
||||
| .metadata.name' 2>/dev/null || true)
|
||||
while IFS= read -r sn; do
|
||||
[[ -z "${sn}" ]] && continue
|
||||
echo "[DEL] secret/${sn} -n ${ns}"
|
||||
"${KUBECTL_DELETE[@]}" "secret" "${sn}" -n "${ns}" --ignore-not-found || true
|
||||
done <<< "${names}"
|
||||
}
|
||||
|
||||
# Traefik / 扩展:不存在则 list 为空
|
||||
TRAEFIK_KINDS=(ingressroute middleware tlsoption traefikservice serverstransport)
|
||||
|
||||
process_namespace() {
|
||||
local ns="$1"
|
||||
|
||||
if [[ "${PREVIEW_ONLY}" == true ]]; then
|
||||
echo "######## 命名空间: ${ns} ########"
|
||||
for k in cronjob job deployment statefulset daemonset ingress networkpolicy horizontalpodautoscaler hpa \
|
||||
"${TRAEFIK_KINDS[@]}" service pvc configmap secret; do
|
||||
preview_kind_in_ns "${ns}" "${k}" || true
|
||||
done
|
||||
return
|
||||
fi
|
||||
|
||||
echo "######## 删除: ${ns} ########"
|
||||
|
||||
# 1) 定时与任务
|
||||
delete_kind_all_by_name "${ns}" "cronjob"
|
||||
delete_kind_all_by_name "${ns}" "job"
|
||||
|
||||
# 2) 路由(Traefik CRD 可能未装,list 为空)
|
||||
delete_kind_all_by_name "${ns}" "ingress"
|
||||
for tk in "${TRAEFIK_KINDS[@]}"; do
|
||||
delete_kind_all_by_name "${ns}" "${tk}"
|
||||
done
|
||||
|
||||
# 3) 工作负载
|
||||
delete_kind_all_by_name "${ns}" "deployment"
|
||||
delete_kind_all_by_name "${ns}" "statefulset"
|
||||
delete_kind_all_by_name "${ns}" "daemonset"
|
||||
delete_kind_all_by_name "${ns}" "replicaset"
|
||||
|
||||
# 4) 其它常见附属(不主动删 Pod:由上层负载级联回收,避免误伤系统静态 Pod)
|
||||
delete_kind_all_by_name "${ns}" "networkpolicy"
|
||||
delete_kind_all_by_name "${ns}" "horizontalpodautoscaler"
|
||||
delete_kind_all_by_name "${ns}" "hpa"
|
||||
|
||||
# 5) Service(保留 kubernetes)
|
||||
delete_services_safe "${ns}"
|
||||
|
||||
# 6) PVC
|
||||
if [[ "${WITH_PVC}" == true ]]; then
|
||||
delete_kind_all_by_name "${ns}" "persistentvolumeclaim"
|
||||
fi
|
||||
|
||||
if [[ "${WITH_CONFIGMAPS}" == true ]]; then
|
||||
delete_configmaps_safe "${ns}"
|
||||
fi
|
||||
|
||||
if [[ "${WITH_SECRETS}" == true ]]; then
|
||||
if command -v jq &>/dev/null; then
|
||||
delete_secrets_safe "${ns}"
|
||||
else
|
||||
echo "[WARN] ${ns}: 未安装 jq,已跳过 --with-secrets" >&2
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
NS_LIST=()
|
||||
while IFS= read -r _ns; do
|
||||
[[ -z "${_ns}" ]] && continue
|
||||
NS_LIST+=("${_ns}")
|
||||
done < <(collect_namespaces | sort -u)
|
||||
|
||||
if [[ ${#NS_LIST[@]} -eq 0 ]]; then
|
||||
echo "[ERROR] 没有可处理的命名空间(检查 --namespaces / 集群连接)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "${KUBECONFIG:-}" ]]; then
|
||||
echo "[INFO] KUBECONFIG=${KUBECONFIG}"
|
||||
else
|
||||
echo "[INFO] KUBECONFIG 未设置,使用 kubectl 默认配置"
|
||||
fi
|
||||
echo "[INFO] 命名空间 (${#NS_LIST[@]}): ${NS_LIST[*]}"
|
||||
echo "[INFO] include-kube-system=${INCLUDE_KUBE_SYSTEM} with-pvc=${WITH_PVC} with-configmaps=${WITH_CONFIGMAPS} with-secrets=${WITH_SECRETS} preview=${PREVIEW_ONLY} dry-run=${DRY_RUN}"
|
||||
|
||||
if [[ "${PREVIEW_ONLY}" != true && "${YES}" != true && "${DRY_RUN}" != true ]]; then
|
||||
echo "[WARN] 将按上述命名空间删除工作负载与路由等资源(见脚本内顺序)。输入 yes 继续:"
|
||||
read -r confirm
|
||||
if [[ "${confirm}" != "yes" ]]; then
|
||||
echo "已取消"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
for ns in "${NS_LIST[@]}"; do
|
||||
process_namespace "${ns}"
|
||||
done
|
||||
|
||||
echo "[DONE] 完成"
|
||||
Reference in New Issue
Block a user