08 k8s 维护脚本
k8s 维护脚本¶
1.0 内存使用率维护¶
实现: 当内存使用率超过95% 时,则将节点禁止调度。当节点重新恢复至85% 时,则取消禁止调度。
监控数据来源: kubectl top node
禁止调度和取消禁止调度命令: kubectl cordon
和 kubectl uncordon
#!/bin/bash
# k8s-node-auto-cordon.sh
# 根据 kubectl top node 的内存使用率自动 cordon/uncordon 节点
#
# 规则:
# - 内存使用率 > CORDON_THRESHOLD 时,对节点执行 cordon
# - 内存使用率 < UNCORDON_THRESHOLD 时,对由本脚本 cordon 的节点执行 uncordon
#
# 用法:
# chmod +x k8s-node-auto-cordon.sh
# ./k8s-node-auto-cordon.sh
#
# 本脚本为一次性检查,建议通过 crontab 定时执行,例如每分钟执行一次:
# * * * * * /path/to/k8s-node-auto-cordon.sh
set -u
# ---------- 可配置参数 ----------
readonly CORDON_THRESHOLD=${CORDON_THRESHOLD:-95} # 触发 cordon 的内存使用率阈值
readonly UNCORDON_THRESHOLD=${UNCORDON_THRESHOLD:-85} # 触发 uncordon 的内存使用率阈值
readonly LOG_FILE=${LOG_FILE:-/var/log/k8s-node-auto-cordon.log}
readonly LABEL_KEY="node-autocordon-memory-pressure" # 用于标记由本脚本 cordon 的节点
# ---------- 日志 ----------
function log() {
local level="$1"
shift
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [${level}] $*" | tee -a "$LOG_FILE"
}
function log_info() {
log "INFO" "$@"
}
function log_warn() {
log "WARN" "$@"
}
function log_error() {
log "ERROR" "$@"
}
# ---------- 前置检查 ----------
function check_prerequisites() {
if ! command -v kubectl &>/dev/null; then
log_error "未找到 kubectl,请确保 kubectl 已安装并在 PATH 中"
exit 1
fi
if ! kubectl version &>/dev/null; then
log_error "无法连接 Kubernetes 集群,请检查 kubeconfig"
exit 1
fi
}
# ---------- 数值比较工具(支持小数) ----------
function num_gt() {
awk -v a="$1" -v b="$2" 'BEGIN { exit !(a > b) }'
}
function num_lt() {
awk -v a="$1" -v b="$2" 'BEGIN { exit !(a < b) }'
}
# ---------- kubectl 状态查询 ----------
function get_node_memory_usage() {
local node="$1"
kubectl top node "$node" --no-headers 2>/dev/null | awk '{print $5}' | sed 's/%//'
}
function is_node_cordoned() {
local node="$1"
local unschedulable
unschedulable=$(kubectl get node "$node" -o jsonpath='{.spec.unschedulable}' 2>/dev/null)
[ "$unschedulable" = "true" ]
}
function is_cordoned_by_us() {
local node="$1"
local label_value
label_value=$(kubectl get node "$node" -o jsonpath="{.metadata.labels['$LABEL_KEY']}" 2>/dev/null)
[ "$label_value" = "true" ]
}
# ---------- cordon / uncordon 操作 ----------
function cordon_node() {
local node="$1"
local usage="$2"
log_warn "节点 $node 内存使用率为 ${usage}%,超过 ${CORDON_THRESHOLD}%,执行 cordon"
if ! kubectl label node "$node" "$LABEL_KEY=true" --overwrite &>/dev/null; then
log_error "无法为节点 $node 打标记标签"
return 1
fi
if ! kubectl cordon "$node" &>/dev/null; then
log_error "无法 cordon 节点 $node"
return 1
fi
log_info "节点 $node 已成功 cordon"
}
function uncordon_node() {
local node="$1"
local usage="$2"
log_info "节点 $node 内存使用率已降至 ${usage}%,低于 ${UNCORDON_THRESHOLD}%,执行 uncordon"
if ! kubectl uncordon "$node" &>/dev/null; then
log_error "无法 uncordon 节点 $node"
return 1
fi
if ! kubectl label node "$node" "$LABEL_KEY-" &>/dev/null; then
log_warn "无法移除节点 $node 的标记标签"
fi
log_info "节点 $node 已成功 uncordon"
}
# ---------- 节点检查 ----------
function check_all_nodes() {
local nodes
nodes=$(kubectl get nodes -o jsonpath='{.items[*].metadata.name}' 2>/dev/null)
if [ -z "$nodes" ]; then
log_error "获取节点列表失败"
return 1
fi
for node in $nodes; do
local usage
usage=$(get_node_memory_usage "$node")
if [ -z "$usage" ]; then
log_warn "无法获取节点 $node 的内存使用率(metrics-server 可能不可用)"
continue
fi
if ! [[ "$usage" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
log_warn "节点 $node 的内存使用率格式异常: '$usage'"
continue
fi
if num_gt "$usage" "$CORDON_THRESHOLD"; then
if ! is_node_cordoned "$node"; then
cordon_node "$node" "$usage"
fi
elif num_lt "$usage" "$UNCORDON_THRESHOLD"; then
if is_node_cordoned "$node" && is_cordoned_by_us "$node"; then
uncordon_node "$node" "$usage"
fi
fi
done
}
# ---------- 脚本入口 ----------
function main() {
check_prerequisites
check_all_nodes
}
main "$@"