1. 为什么我们需要主动缩容?

在Kubernetes集群运维中,节点扩容和缩容就像呼吸一样自然。想象一下这样的场景:

  • 业务低谷期:电商平台在双十一结束后,服务器负载骤降
  • 硬件更换:老旧的物理机需要退役更换为云服务器
  • 资源优化:发现某些节点长期处于低利用率状态

此时若任由闲置节点空转,每小时可能浪费数百元云成本。但直接删除节点就像突然拔掉正在输液的针头——可能引发服务中断、数据丢失等问题。

2. 关键前置检查清单(必做!)

掏出你的终端,在开始手术前先做好五项检查:

# 查看所有节点状态(技术栈:kubectl)
kubectl get nodes -o wide

# 检查目标节点上的Pod分布(替换<node-name>为实际节点名)
kubectl get pods --all-namespaces -o wide --field-selector spec.nodeName=<node-name>

# 验证节点是否可调度(重点看SchedulingDisabled状态)
kubectl describe node <node-name> | grep -i taints

# 检查本地存储卷(特别是使用hostPath的应用)
kubectl get pv --all-namespaces | grep <node-name>

# 确认Kubelet状态(在目标节点执行)
systemctl status kubelet

3. 核心操作六部曲详解

3.1 给节点打上免打扰标签

就像给即将搬走的邻居门上贴封条:

kubectl cordon <node-name>

此时新Pod不会再被调度到此节点,但现有Pod继续运行。

3.2 优雅驱逐现有Pod

用专业的排水(drain)操作替代粗暴删除:

kubectl drain <node-name> \
  --ignore-daemonsets \       # 忽略守护进程集(如监控Agent)
  --delete-emptydir-data \    # 清理临时数据卷
  --timeout=5m                # 避免卡死

注意! 如果看到报错:"error: unable to drain node... due to pod with local storage",需要手动处理有状态服务。

3.3 处理顽固Pod的特殊案例

当遇到有本地存储的有状态服务时,推荐分步处理:

# 临时缩容StatefulSet(技术栈:Kubernetes原生API)
kubectl scale statefulset my-redis --replicas=0

# 手动删除残余Pod(谨慎操作!)
kubectl delete pod redis-node-0 --grace-period=60

# 等待所有PVC确认解除绑定(必须步骤)
watch kubectl get pvc -n <namespace>

3.4 正式删除节点

确认节点无残留Pod后执行:

kubectl delete node <node-name>

3.5 清理残留资源三要素

很多工程师会漏掉这些隐藏角落:

# 孤儿endpoint清理
kubectl get endpoints | grep <node-ip>
kubectl delete endpoints <orphan-endpoint-name>

# 残留网络接口检查(在集群网络插件所在节点执行)
ip link show | grep cali  # Calico插件示例
calicoctl delete node <node-name>  # 网络配置清理

# Kubelet残留配置清理(在目标节点执行)
sudo kubeadm reset
sudo rm -rf /etc/cni/net.d/

3.6 反向验证操作

必须通过四项验证才算真正成功:

# 查看节点列表是否已消失
kubectl get nodes

# 检查CoreDNS端点更新情况
kubectl -n kube-system get endpoints kube-dns

# 验证集群自动缩放器状态(如果启用了Cluster Autoscaler)
kubectl -n kube-system logs deployment/cluster-autoscaler | grep "Removed node"

# 残留资源最后通牒检查
kubectl get all --all-namespaces -o wide | grep <node-name>

4. 必知的五个隐藏技巧

4.1 负载均衡器的秘密握手

在AWS环境删除节点时,记得检查目标组:

aws elbv2 describe-target-health \
  --target-group-arn <your-tg-arn> \
  --query 'TargetHealthDescriptions[?Target.Id==`<instance-id>`]'

4.2 节点优雅退出的时间玄学

通过调整控制器管理器的参数实现平滑过渡:

# 修改kube-controller-manager启动参数
- --terminated-pod-gc-threshold=50
- --pod-eviction-timeout=2m0s

4.3 节点临终关怀服务

创建PreStop钩子确保服务优雅终止:

lifecycle:
  preStop:
    exec:
      command: 
        - /bin/sh
        - -c 
        - "curl -X POST http://127.0.0.1:8080/drain || true"

4.4 节点标签的精确制导

通过标签选择器批量操作:

kubectl drain -l node-type=spot-instance

4.5 云供应商特殊处理

AWS节点需要额外清理ENI:

aws ec2 describe-network-interfaces \
  --filters Name=attachment.instance-id,Values=<instance-id>

5. 典型故障的黄金救援方案

5.1 僵尸节点复活事件

当节点状态显示NotReady但API Server仍保留记录时:

kubectl get node <node-name> -o json | jq '.spec.taints'
kubectl patch node <node-name> -p '{"spec":{"taints":[]}}'

5.2 永远驱逐不走的Pod

强制删除的终极手段:

kubectl delete pod <pod-name> --grace-period=0 --force

5.3 存储卷的生死缠绵

当PV持续处于Terminating状态:

kubectl patch pv <pv-name> -p '{"metadata":{"finalizers":null}}'

6. 关联技术深度解析

6.1 PodDisruptionBudget的守护结界

为关键服务设置驱逐保护:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

6.2 节点亲和性的双刃剑

错误配置的亲和性可能导致Pod无法迁移:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: ssd
          operator: In
          values:
          - "true"

7. 实践中的坑与黄金法则

7.1 三大经典翻车现场

  • 未检查DaemonSet导致Pod无限重建
  • 漏删本地存储引发的数据泄漏
  • LoadBalancer未及时更新导致的流量黑洞

7.2 操作黄金时间窗

建议选择业务低峰期的维护窗口:

  • 周二凌晨2-4点(避开周一服务重启高峰)
  • 季度结算周期结束后
  • 重大促销活动前2周

8. 完整实战案例演示

假设我们有一个3节点的集群要移除其中1个worker节点:

# 先标记节点不可调度
kubectl cordon worker-node03

# 驱逐所有Pod(带详细参数解释)
kubectl drain worker-node03 \
  --ignore-daemonsets \       # 绕过监控Agent等DaemonSet
  --delete-emptydir-data \    # 清除临时数据卷
  --grace-period=120 \        # 给Pod额外2分钟准备时间
  --timeout=10m               # 设置超时阈值

# 确认驱逐完成
watch kubectl get pods -o wide

# 执行节点删除
kubectl delete node worker-node03

# 云平台资源清理(以AWS为例)
aws ec2 terminate-instances --instance-ids i-0abcdef1234567890

# 验证负载均衡器健康检查(以ALB为例)
aws elbv2 describe-target-health --target-group-arn arn:aws:elasticloadbalancing...

# 最终状态确认
kubectl get nodes
kubectl get pods --all-namespaces -o wide

9. 技术方案选型对比

当面对不同规模集群时,采用不同策略:

场景 小规模集群(<20节点) 中大型集群 超大规模集群
操作方式 手动逐台操作 脚本批量处理 自动化调度系统
风险控制 人工二次确认 金丝雀发布模式 细胞级隔离策略
清理周期 立即清理 定时垃圾回收 智能预测回收
典型工具链 kubectl Ansible+Shell 自研运维平台
耗时预估 10分钟/节点 5分钟/节点 <1分钟/节点