Kubernetes集群升级后服务中断?排查修复实战指南

loong
2026-01-19 / 0 评论 / 17 阅读 / 正在检测是否收录...

Kubernetes集群升级后服务中断?排查修复实战指南

凌晨3点,运维值班群突然炸了锅。"线上集群升级后,核心业务服务全部不可用!"这样的场景,相信每个运维工程师都不想遇到。但现实总是残酷的——我见过太多团队因为集群升级而深夜通宵,也见过太多因为处理不当导致事故升级的情况。

今天分享一套我总结的实战方法论,帮助你快速定位和解决Kubernetes升级后的服务中断问题。

为什么升级总是伴随着"惊喜"?

在深入排查之前,我们先理解问题的根源。Kubernetes升级不只是kubelet、kube-apiserver这些组件版本的更新,更重要的是:

API行为变化:新版本可能弃用某些API或改变默认行为
网络插件兼容性:CNI插件可能需要跟随升级
存储驱动适配:CSI驱动与Kubernetes版本的匹配关系
证书签发机制变化:证书自动轮转的逻辑调整

这些变化往往是静默的,只有在特定负载下才会暴露。

排查路线图:从症状到根源

第一步:快速状态评估(5分钟内完成)

# 集群健康度检查
kubectl get nodes -o wide
kubectl get cs  # 组件状态
kubectl cluster-info

# 核心资源状态
kubectl get pods --all-namespaces | grep -v Running
kubectl get svc --all-namespaces
kubectl get deploy --all-namespaces

# 资源配额和限制
kubectl top nodes
kubectl top pods --all-namespaces

关键观察点:

  • 节点状态:NotReady还是Ready但有其他问题?
  • Pod状态:Pending、CrashLoopBackOff还是其他?
  • 服务变化:有哪些服务IP或端口发生变化?

第二步:日志深度挖掘

# 集群组件日志检查
sudo journalctl -u kubelet --since "1 hour ago"
sudo journalctl -u kube-apiserver --since "1 hour ago"

# 问题Pod日志
kubectl logs <pod-name> -n <namespace> --previous
kubectl describe pod <pod-name> -n <namespace>

# 事件流追踪
kubectl get events --sort-by='.lastTimestamp' --all-namespaces

经验分享:升级后最常见的问题是etcd leader选举、网络插件配置和cgroup驱动不匹配导致的容器启动失败。

第三步:网络连通性验证

# 核心DNS解析
kubectl run dnstest --image=busybox --rm -it -- nslookup kubernetes.default

# 跨节点连通性
kubectl run networktest --image=nicolaka/netshoot --rm -it -- \
  sh -c "ping -c 3 <其他节点IP> && curl -m 5 http://<服务IP>:80"

# Service和Endpoint验证
kubectl get svc,ep -n <namespace>

典型故障模式与修复方案

故障模式1:Pod一直Pending

症状:kubectl get pods显示状态为Pending

排查思路:

kubectl describe pod <pod-name> -n <namespace>

重点查看Events字段,常见原因:

  • 节点压力:Resource quota超限
  • Taint冲突:节点被打污点但Pod没有相应容忍度
  • 亲和性规则:Pod调度失败

修复方案:

# 临时解决方案
kubectl patch pod <pod-name> -n <namespace> -p '{
  "spec": {
    "tolerations": [{
      "key": "node.kubernetes.io/not-ready",
      "operator": "Exists",
      "effect": "NoExecute",
      "tolerationSeconds": 300
    }]
  }
}'

# 根因解决
# 1. 调整资源限制或增加节点
# 2. 修改Deployment的tolerations配置
# 3. 检查并调整调度策略

故障模式2:容器反复Crash

症状:CrashLoopBackOff状态,容器不断重启

排查步骤:

# 查看容器启动失败日志
kubectl logs <pod-name> -n <namespace> --previous --tail=100

# 容器内健康检查
kubectl exec -it <pod-name> -n <namespace> -- /bin/sh

# 检查依赖服务
kubectl get svc -n <namespace>
kubectl get endpoints -n <namespace>

常见原因及修复:

1. 配置文件版本不兼容

# 问题:Pod使用过时的ConfigMap/Secret
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    env:
    - name: CONFIG_PATH
      valueFrom:
        configMapKeyRef:
          name: app-config-v1  # 这个版本可能不存在了
          key: config.yaml

# 修复:更新到正确的配置版本
kubectl set env deployment/<deployment-name> \
  CONFIG_FROM_CONFIGMAP=app-config-v2

2. 镜像拉取失败

# 检查镜像拉取策略
kubectl describe pod <pod-name> | grep -A5 "Events"

# 修复镜像仓库配置
kubectl patch deployment <deployment-name> \
  -p '{"spec":{"template":{"spec":{"containers":[{"name":"app","imagePullPolicy":"Always"}]}}}'

# 或配置镜像仓库凭证
kubectl create secret docker-registry regcred \
  --docker-server=<your-registry-server> \
  --docker-username=<your-username> \
  --docker-password=<your-password> \
  --docker-email=<your-email>

3. 权限问题(RBAC变化)

# 检查ServiceAccount权限
kubectl auth can-i get pods --as=system:serviceaccount:<namespace>:<sa-name>

# 修复缺失的RBAC权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: <namespace>
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]

故障模式3:Service访问异常

症状:外部能访问,但应用间通信失败,或LoadBalancer IP变化

排查重点:

# Service配置检查
kubectl get svc <service-name> -n <namespace> -o yaml

# Endpoint健康状态
kubectl get endpoints <service-name> -n <namespace>

# CoreDNS解析测试
kubectl run dnstest --image=busybox --rm -it -- \
  nslookup <service-name>.<namespace>.svc.cluster.local

修复策略:

1. Service类型变化

# 如果Service类型从NodePort变为ClusterIP
kubectl patch svc <service-name> -n <namespace> \
  -p '{"spec":{"type":"NodePort","ports":[{"nodePort":30080,"port":80,"protocol":"TCP","targetPort":8080}]}}'

2. 网络策略阻止流量

# 检查NetworkPolicy
kubectl get networkpolicy -n <namespace>
kubectl describe networkpolicy <policy-name> -n <namespace>

# 临时禁用测试
kubectl delete networkpolicy <policy-name> -n <namespace>

3. SessionAffinity配置问题

# 如果应用需要无状态访问,关闭SessionAffinity
kubectl patch svc <service-name> -n <namespace> \
  -p '{"spec":{"sessionAffinity":"None"}}'

快速恢复流程(适用于紧急情况)

当业务影响较大时,我们需要有一个"救命"的快速恢复策略:

1. 降级到安全版本(最后手段)

# 备份当前配置
kubectl get all -A -o yaml > pre-rollback-backup.yaml

# 如果是Helm部署的Charts
helm rollback <release-name> <previous-revision>

# 如果是原生Kubernetes资源
kubectl apply -f pre-rollback-backup.yaml

2. 逐个命名空间隔离恢复

# 选择性恢复关键命名空间
kubectl get namespace | grep -E "(prod|critical|default)"

# 对每个关键命名空间执行恢复
for ns in prod critical default; do
  echo "恢复命名空间: $ns"
  kubectl get all -n $ns -o yaml | kubectl apply -f -
done

3. 创建临时救火部署

# 快速创建最小可用版本
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: emergency-deployment
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: emergency
  template:
    metadata:
      labels:
        app: emergency
    spec:
      containers:
      - name: emergency-app
        image: nginx:alpine
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
  name: emergency-service
  namespace: default
spec:
  type: NodePort
  selector:
    app: emergency
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080
EOF

预防措施:让升级不再是噩梦

经验告诉我们,预防永远比事后补救更有效:

升级前准备清单

1. 环境和版本兼容性测试

# 创建测试集群模拟生产环境
kind create cluster --config test-cluster.yaml

# 关键配置验证
kubectl version --short
crictl info | grep -A10 "containerd"

# 网络插件兼容性
kubectl get pods -n kube-system | grep -E "coredns|flannel|weave|calico"

2. 资源配置和依赖关系梳理

# 导出所有资源配置
kubectl get all,configmap,secret,pvc,ingress,networkpolicy -A -o yaml > backup-pre-upgrade.yaml

# 关键指标基线记录
kubectl top nodes > node-metrics-baseline.txt
kubectl top pods --all-namespaces > pod-metrics-baseline.txt

# RBAC权限清单
kubectl auth can-i --list --as=system:serviceaccount > rbac-baseline.txt

监控告警策略

# 升级期间加强监控
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: upgrade-monitoring
spec:
  groups:
  - name: k8s.upgrade
    rules:
    - alert: PodCrashLooping
      expr: rate(kube_pod_container_status_restarts_total[10m]) > 0.1
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "Pod {{ $labels.pod }} is crash looping"
    
    - alert: NodeNotReady
      expr: kube_node_status_condition{condition="Ready",status="true"} == 0
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "Node {{ $labels.node }} is not ready"

升级流程标准化

1. 分阶段灰度升级

# 先升级一个节点进行测试
kubectl drain <node-name> --ignore-daemonsets
# 手动升级这个节点的kubelet
# 验证Pod迁移和集群功能正常
kubectl uncordon <node-name>

# 确认无问题后,按批次升级其他节点
for node in $(kubectl get nodes -o name | cut -d'/' -f2 | grep -v <test-node>); do
  echo "升级节点: $node"
  kubectl drain $node --ignore-daemonsets
  # 执行升级操作
  kubectl uncordon $node
done

2. 配置管理自动化

#!/bin/bash
# 升级前配置检查脚本
check_configs() {
  echo "检查配置文件兼容性..."
  
  # 检查API弃用警告
  kubectl apply --dry-run=server -f configs/ 2>&1 | grep -i deprecat
  
  # 检查资源配额
  kubectl get quota -A -o wide
  
  # 检查HPA配置
  kubectl get hpa -A -o yaml
}

# 升级后验证脚本
post_upgrade_validation() {
  echo "升级后验证..."
  
  # 核心功能测试
  kubectl run test-pod --image=busybox --rm -it --restart=Never -- wget -qO- http://kubernetes.default
  
  # 网络连通性测试
  kubectl run network-test --image=nicolaka/netshoot --rm -it -- sh -c "ping -c 3 google.com && nslookup kubernetes.default"
}

经验总结与建议

经过多次集群升级的实战,我总结出几个关键要点:

  1. 升级窗口选择很重要:避开业务高峰期,选择系统负载相对较低的时间段
  2. 回滚方案必须提前准备:不是每次升级都能顺利回滚,但必须有这个预案
  3. 监控告警要实时:升级期间必须有人值守,不能升级后就不管了
  4. 沟通机制要顺畅:升级团队、运维团队、业务团队之间要保持密切沟通
  5. 文档记录要完整:问题解决过程要详细记录,为后续升级积累经验

最重要的是,不要被集群升级吓到。虽然每次升级都伴随着风险,但只要方法得当、准备充分,这些风险都是可控的。

记住:在生产环境中,技术问题往往不是最难的,最难的是在压力下保持冷静、按照既定流程操作。

希望这份实战指南能帮助你在下次面对集群升级时,不再慌张胸慌乱。最后,如果你有其他Kubernetes运维问题,或者想分享你的实战经验,欢迎在评论区交流讨论。

0