CTO Plus技术服务栈

Kubernetes生产环境避坑指南:3年运维踩过的50个坑

总结在生产环境运行Kubernetes三年来的实战经验,涵盖Pod驱逐、OOMKilled排查、CNI网络故障、etcd性能优化、RBAC权限管理、HPA自动扩缩容陷阱等高频问题及其解决方案。

SteveRocket
北京,中国
2 min read

前言

Kubernetes 已经成为云原生时代的”操作系统”,但把 K8s 跑在生产环境和在 Minikube 上跑个 Demo 完全是两回事。这篇文章整理了我们在生产环境运维 K8s 集群三年多以来踩过的坑和对应的解决方案。

一、Pod 调度与生命周期

坑 1:Pod 频繁被驱逐(Evicted)

现象:Pod 状态变成 Evicted,服务不可用。

根因:节点资源(磁盘/内存)不足触发了 Kubelet 的驱逐机制。

# ❌ 没有设置 resource limit,容易触发驱逐
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    image: myapp:v1
    # 缺少 resources 配置!

# ✅ 正确配置
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    image: myapp:v1
    resources:
      requests:
        memory: "256Mi"
        cpu: "100m"
      limits:
        memory: "512Mi"  # limit 不要设太大,防止 OOM
        cpu: "500m"

关键经验

  • requestslimits 必须设置,缺一不可
  • limits.memory 不要超过节点内存的 50%
  • 配置 podAntiAffinity 避免同一个服务的 Pod 全挤在一台机器上
  • 设置 priorityClassName 保护核心服务不被驱逐

坑 2:OOMKilled 但日志里什么都看不到

现象:Pod 突然重启,状态显示 OOMKilled,但应用日志里没有任何异常。

根因:JVM / Node.js 的堆外内存没有被 limits 覆盖到。

# Java 应用的 JVM 参数必须和 K8s limits 对齐
env:
- name: JAVA_OPTS
  value: "-Xms256m -Xmx384m -XX:MaxDirectMemorySize=64m"
  # Xmx + MaxDirectMemorySize + Metaspace < limits.memory

排查方法

# 查看 Pod 上次退出的原因
kubectl describe pod <pod-name> | grep -A5 "Last State"

# 查看节点级别的 OOM 事件
kubectl get events --field-selector reason=OOMKilling

# 使用 metrics-server 查看实际内存使用
kubectl top pod <pod-name> --containers

坑 3:Readiness Probe 配错导致流量打到未就绪的 Pod

# ❌ 探针路径返回 200 不代表应用真正就绪
readinessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10

# ✅ 应该检查应用核心依赖(数据库连接、缓存连接等)
readinessProbe:
  httpGet:
    path: /health/ready  # 这个端点检查 DB + Redis + MQ 连接
    port: 8080
  initialDelaySeconds: 30  # 给应用足够的启动时间
  periodSeconds: 10
  failureThreshold: 3      # 连续失败3次才标记为未就绪
  successThreshold: 1

二、网络(CNI)问题

坑 4:Pod 间网络不通,时好时坏

现象:A Pod 访问 B Pod 的 Service 偶尔超时。

排查链路

# 1. 检查 Service 的 Endpoints 是否正确
kubectl get endpoints <service-name>

# 2. 检查 kube-proxy 是否正常运行
kubectl -n kube-system get pods -l k8s-app=kube-proxy

# 3. 从另一个 Pod 测试连通性
kubectl run test-pod --rm -it --image=busybox -- sh
wget -O- http://<service-name>:<port>/health

# 4. 检查 iptables 规则(最常见的坑)
iptables -t nat -L KUBE-SERVICES -n | grep <service-name>

常见原因

  • conntrack 表满了:sysctl net.netfilter.nf_conntrack_max
  • DNS 解析延迟:ndots:5 导致多余的 DNS 查询
  • CNI 插件版本不兼容

坑 5:DNS 解析慢导致服务超时

# 在 Pod spec 中配置 DNS 策略
spec:
  dnsConfig:
    options:
    - name: ndots
      value: "2"  # 减少不必要的域名补全尝试
    - name: single-request-reopen
  dnsPolicy: ClusterFirst

三、存储

坑 6:PVC 挂载不上,Pod 一直 Pending

排查

# 1. 检查 PV 状态
kubectl get pv

# 2. 检查 PVC 绑定状态
kubectl describe pvc <pvc-name>

# 3. 检查 CSI Driver Pod 是否正常
kubectl -n kube-system get pods | grep csi

# 4. 检查节点是否有卷的挂载限制(AWS EBS 单节点最多挂 39 个卷)
kubectl describe node <node-name> | grep -A5 "Allocated resources"

四、etcd 性能

坑 7:etcd 磁盘 IO 飙升导致集群响应变慢

# etcd 性能自查
# 1. 检查 etcd 磁盘是否有延迟
etcdctl endpoint health --cluster

# 2. 检查 etcd 数据库大小(超过 2GB 会触发告警)
etcdctl endpoint status --cluster -w table | grep DB SIZE

# 3. 碎片整理
etcdctl defrag --cluster

# 4. 查看哪些 key 占用最多空间
etcdctl get / --prefix --keys-only | wc -l

优化建议

  • etcd 必须使用 SSD,不能放在共享存储上
  • 定期执行 defrag(建议每周一次)
  • 设置 --auto-compaction-retention=1 自动压缩历史版本
  • --quota-backend-bytes=8589934592(8GB)限制数据库大小

五、HPA 自动扩缩容

坑 8:HPA 频繁扩缩导致”抖动”

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300  # 缩容前等待5分钟
      policies:
      - type: Percent
        value: 10    # 每次最多缩 10%
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0    # 扩容不等待
      policies:
      - type: Percent
        value: 100   # 每次最多扩 100%
        periodSeconds: 15
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70  # 不要设太低,60-80 是合理范围

六、安全

坑 9:RBAC 权限过大导致安全隐患

# 审计谁有什么权限
kubectl auth can-i --list --as=system:serviceaccount:default:my-sa

# 检查是否有 ClusterAdmin 权限的 ServiceAccount
kubectl get clusterrolebindings -o json | \
  jq '.items[] | select(.subjects[]?.kind=="ServiceAccount") | .metadata.name'

原则

  • 永远不要给 default ServiceAccount 绑 ClusterRole
  • 每个微服务使用独立的 ServiceAccount
  • 定期审计 RBAC 权限

七、升级与维护

坑 10:K8s 版本升级后 Pod 启动失败

升级前必须检查

# 1. 检查即将被废弃的 API 版本
kubectl get --raw /metrics | grep apiserver_requested_deprecated_apis

# 2. 使用 pluto 检查集群中的废弃 API 使用情况
pluto detect-helm -o wide

# 3. 确认所有 CRD 与新版本兼容
kubectl get crd -o yaml | grep -A2 "apiVersion"

监控检查清单

生产环境 K8s 集群每天必看的指标:

指标健康范围告警阈值
节点 CPU 使用率< 70%> 85%
节点内存使用率< 75%> 90%
Pod 重启次数0> 3/天
etcd 磁盘使用< 60%> 80%
etcd DB Size< 4GB> 6GB
API Server 延迟 P99< 1s> 3s
集群事件中 Warning< 10/小时> 50/小时

总结

K8s 运维的核心不是会敲 kubectl,而是理解它每一层的工作原理:

  1. 资源管理是基础:request/limit 配不好,后面全是坑
  2. 网络是最大的变量:CNI 选型、DNS 配置、Service Mesh 引入都要慎重
  3. 存储要提前规划:CSI Driver 兼容性、备份策略、跨 AZ 可用性
  4. 安全不能后补:RBAC、Network Policy、Pod Security 从一开始就规划好
  5. 升级要谨慎:先在测试集群验证,再看生产

记住一句话:Kubernetes is a platform for building platforms, not an end-user product.

Comments

Link copied to clipboard!