# k8s 运维与故障排查


## 导读

生产级集群的稳定性依赖成熟的运维手段与可复用的排障路径。本文覆盖集群升级策略、etcd 与 Velero 备份恢复、分层故障诊断方法论，以及 kubectl debug 临时容器等实战技能。掌握这些内容，能在事故发生时快速定位根因并恢复服务。

## 核心概念

集群升级有三条主流路径。滚动升级通过 kubeadm 逐个节点升级控制面与工作节点，期间业务 Pod 被驱逐再重建，适合大多数场景。蓝绿升级额外维护一套备用集群，流量整体切换，回滚快但资源成本翻倍。金丝雀升级把新版本先导入少量节点或少量流量，观察指标无异常后再全量推进，适合对稳定性要求极高的核心业务。三种策略的选择本质上是停机容忍度与资源成本的权衡。

备份恢复是运维的最后一道防线。etcd 存储了集群所有状态数据，其快照是集群级恢复的基础。Velero 则面向工作负载，能把命名空间、Deployment、ConfigMap 等资源 YAML 连同 PV 卷快照一并备份到对象存储。两者互补：etcd 备份应对控制面灾难，Velero 备份应对应用级误删与迁移。

故障诊断需要分层方法。Kubernetes 的声明式模型让问题往往层层传递：Pod 异常可能源于调度失败，调度失败可能源于节点资源不足，节点资源不足可能源于上层资源配额。从 Pod 状态出发，逐层上溯到节点与集群，能避免在错误层级打转。下图把常见 Pod 异常状态拆成四条诊断分支。

```mermaid
flowchart TD
    A[Pod 异常] --> B{判断状态}
    B -->|Pending| C[kubectl describe pod<br/>查看 Events]
    C --> C1{资源不足}
    C1 -->|是| C2[节点 CPU、内存或配额耗尽<br/>扩容节点或调整 request]
    C1 -->|否| C3[调度策略冲突<br/>检查 nodeSelector、亲和性、taint]
    B -->|CrashLoopBackOff| D[kubectl logs --previous<br/>查看退出日志]
    D --> D1{启动失败原因}
    D1 -->|应用异常| D2[检查配置、依赖、启动参数]
    D1 -->|健康检查失败| D3[livenessProbe 误判<br/>调大 initialDelaySeconds]
    B -->|ImagePullBackOff| E[kubectl describe pod<br/>查看拉取错误]
    E --> E1{镜像问题}
    E1 -->|镜像不存在或 Tag 错误| E2[核对镜像名与仓库]
    E1 -->|认证失败| E3[检查 imagePullSecrets]
    E1 -->|网络受限| E4[节点到仓库连通性与限速]
    B -->|Evicted| F[kubectl describe node<br/>查看节点压力]
    F --> F1{压力来源}
    F1 -->|磁盘压力| F2[清理镜像与日志<br/>检查 emptyDir 滥用]
    F1 -->|内存压力| F3[排查内存泄漏 Pod<br/>调整 LimitRange]
```

图 1 给出了从 Pod 异常出发的四分支决策树。Pending 指向调度层问题，根因集中在资源与调度策略；CrashLoopBackOff 指向应用层问题，需查看前一次容器退出日志；ImagePullBackOff 指向镜像与仓库层问题；Evicted 指向节点压力，根因多为磁盘或内存耗尽。每条分支都对应一组确定的诊断命令与根因方向，避免无目的试探。

## 图解原理

Velero 由服务端 Controller 与客户端 CLI 组成，是 Kubernetes 原生的备份恢复工具。它的核心思路是把集群状态拆成两类产物分别持久化：一类是 Kubernetes 资源的 YAML 清单，另一类是持久卷的快照。两类产物都写入对象存储，恢复时再从对象存储反向拉取重建。下图刻画了这条双向数据流的完整路径。

```mermaid
graph LR
    V[Velero Controller] -->|调用云厂商快照 API| S[卷快照 CSI Snapshot]
    V -->|导出资源 YAML| Y[Kubernetes 资源清单]
    S --> O[(对象存储<br/>S3 与 OSS)]
    Y --> O
    O -->|恢复时拉取快照元数据| R1[重建 PV 卷数据]
    O -->|恢复时拉取资源 YAML| R2[重建 K8s 资源]
    R1 --> T[目标集群恢复完成]
    R2 --> T
```

图 2 展示了 Velero 备份与恢复的双向数据流。备份阶段资源 YAML 与卷快照并行写入对象存储，互不依赖，任一失败不影响另一份产物。恢复阶段两者从对象存储反向拉取，资源重建与卷数据恢复同步推进。这种分离设计让跨集群迁移成为可能，只要目标集群能访问同一对象存储桶并安装对应 CSI 驱动，即可恢复完整工作负载。

kubectl debug 通过注入临时容器（Ephemeral Container）到目标 Pod 实现排障。临时容器不修改 Pod 规约、不参与重启循环，而是共享目标 Pod 的网络与存储命名空间，特别适合诊断 distroless 或精简镜像里无法安装排障工具的场景。容器退出后自动清理，不留痕迹。

## 动手实操

运维操作的落地依赖准确的命令序列。以下按 etcd 备份、Velero 工作负载备份、分层诊断与临时容器四个场景给出可复用的命令模板，所有命令均基于 kubectl 与对应工具 CLI。生产环境执行前应在测试集群验证参数与权限。

### etcd 快照备份与恢复

```bash
# 在任一 etcd 节点执行快照
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snapshot.db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/etcd/ca.crt \
  --cert=/etc/etcd/server.crt \
  --key=/etc/etcd/server.key

# 验证快照完整性
ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-snapshot.db -w table

# 灾难恢复：停止 etcd 后用快照恢复成员数据目录
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot.db \
  --data-dir=/var/lib/etcd-restored
```

恢复后需将该目录指向 etcd 静态 Pod 配置并重启 kubelet，控制面才会基于恢复的数据重新拉起。整个恢复过程要求先停止所有 etcd 成员，属于停机操作，因此 etcd 快照恢复只用于灾难场景。日常运维的细粒度恢复应交给 Velero 按命名空间粒度处理。

### Velero 安装与备份

```bash
# 安装 Velero CLI 后，连接 S3 兼容对象存储部署服务端
velero install \
  --provider aws \
  --bucket k8s-backup \
  --backup-location-config region=us-east-1 \
  --snapshot-location-config region=us-east-1 \
  --secret-file credentials-velero

# 按命名空间创建备份，包含卷快照
velero backup create app-backup --include-namespaces production --snapshot-volumes

# 查看备份状态与详情
velero backup describe app-backup --details

# 恢复到目标集群
velero restore create --from-backup app-backup
```

备份创建后用 describe 查看 Hooks 与卷快照状态，确认 VolumeSnapshots 资源已生成。跨集群恢复时目标集群需安装相同 CSI 驱动并配置同一对象存储访问凭证。恢复默认按备份顺序重建资源，可通过 --restore-volumes 控制是否恢复卷数据。

### 分层故障诊断命令

```bash
# Pod 层：查看状态、事件与日志
kubectl get pod <pod-name> -n <ns>
kubectl describe pod <pod-name> -n <ns>
kubectl logs <pod-name> -n <ns> --previous --tail=100

# 节点层：查看资源占用与压力状况
kubectl describe node <node-name>
kubectl get --raw /api/v1/nodes/<node-name>/proxy/stats/summary

# 集群层：检查关键组件与节点就绪状态
kubectl get cs
kubectl get nodes -o wide
```

describe 的 Events 段按时间倒序排列，最近的调度与拉取事件排在最前。节点 stats 接口能拿到容器运行时的实时资源占用，比 kubectl top 更细。控制面组件异常时优先查 kube-apiserver 与 kube-scheduler 的日志。

### kubectl debug 临时容器

```bash
# 给运行中的 Pod 注入带排障工具的临时容器
kubectl debug -it <pod-name> --image=busybox:1.36 --target=<container-name> -n <ns>

# 在节点上创建调试 Pod，共享节点命名空间
kubectl debug node/<node-name> -it --image=ubuntu:22.04
```

--target 指定共享进程命名空间的容器，便于在不进入目标容器内部的前提下诊断其进程。节点级调试会创建特权 Pod 挂载节点文件系统，适合排查 CNI、容器运行时等节点级问题。临时容器不写入 Pod 规约，退出后随 Pod 生命周期清理，不污染声明式配置。

## 常见问题与避坑

升级阶段，滚动升级期间若 PodDisruptionBudget 设置过严，节点驱逐会卡住导致升级停滞；金丝雀升级若缺乏自动指标门控，异常版本可能被手动全量铺开。升级前应核对 PDB 与节点冗余度，保证每个工作负载在驱逐后仍有足够副本承接流量。**控制面升级前必须完成 etcd 快照**，这是升级失败的唯一回滚依据。

备份环节，**Velero 默认不备份不带 PV 的临时数据**，依赖 emptyDir 的有状态应用恢复后会丢数据。etcd 快照恢复属于集群级操作，会覆盖现有全部状态，破坏性远超想象。生产环境应在隔离的恢复集群验证快照完整性后再切流，切勿直接在原集群覆盖恢复。

排障阶段，CrashLoopBackOff 的日志需用 --previous 参数读取上一次退出容器，直接 kubectl logs 往往只能看到空输出。Pending 状态要优先看 describe 的 Events 段而非日志，调度拒绝原因都记录在那里。**节点 Evicted 的 Pod 不会自动删除**，堆积后会干扰调度判断，需定期清理 pod-phase=Failed 的残留对象。

## 小结与进阶

运维的本质是用可复用的流程把不确定性收敛为确定操作——升级走灰度、备份分两层、排障按状态分支逐层定位，Kubernetes 的复杂度终将被这套纪律驯服。

