K8s 系列 | 第 30 天:K8s 生产环境踩坑实录:性能调优、故障排查与最佳实践


tags: [Kubernetes, K8s系列, DevOps, 生产运维, 性能调优, 故障排查, 最佳实践, 踩坑实录]

K8s 系列 | 第 30 天:K8s 生产环境踩坑实录:性能调优、故障排查与最佳实践

第 30/30 天

引言

经过前面 29 天的学习,我们从 K8s 的基础概念到集群搭建、存储网络、调度策略、监控日志、安全备份,一路走到了多集群管理。今天是 30 天 K8s 系列的收官之作,我们将分享生产环境中真实踩过的坑一线调优经验

Kubernetes 在生产环境中运行稳定吗?答案是:只要配置得当,它非常稳定;但如果忽视细节,它也能让你彻夜难眠。本文整理了我在生产环境运维中遇到的最常见问题、性能瓶颈和对应的解决方案,希望能帮助你少走弯路。


一、kubelet 与节点层面的常见坑

1.1 节点压力驱逐(Node Pressure Eviction)

在生产环境中最常见的问题之一就是节点被 kubelet 驱逐 Pod。当磁盘、内存或 PID 资源不足时,kubelet 会触发节点压力驱逐:

# kubelet 配置示例 - 驱逐阈值调整
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
  memory.available: "500Mi"
  nodefs.available: "10%"
  nodefs.inodesFree: "5%"
  imagefs.available: "15%"
evictionSoft:
  memory.available: "1Gi"
  nodefs.available: "15%"
  imagefs.inodesFree: "10%"
  imagefs.available: "20%"
evictionSoftGracePeriod:
  memory.available: "1m30s"
  nodefs.available: "2m"
  imagefs.available: "2m"
evictionMaxPodGracePeriod: 120

踩坑实录:我们曾遇到一个集群因为日志未做轮转,/var/log 目录被写满,导致 nodefs 达到驱逐阈值,kubelet 开始驱逐系统组件 Pod(如 calico-nodekube-proxy),引发雪崩式故障。

解决方案:配置 logrotate + 设置合理的驱逐阈值 + 监控磁盘使用率报警。

1.2 容器运行时镜像 GC 问题

containerd 默认不会清理旧的镜像层,长期运行后磁盘占用会持续增长:

# 查看 containerd 镜像 GC 配置
cat /etc/containerd/config.toml | grep -A 10 "[plugins."io.containerd.gc.v1.scheduler"]"

# 手动清理未使用的镜像
crictl rmi --prune

# 定期清理:crontab 中添加
# 0 3 * * * /usr/bin/crictl rmi --prune

二、控制平面(Control Plane)的隐患

2.1 etcd 性能瓶颈

etcd 是整个集群的”心脏”,它的性能直接决定集群的响应速度。etcd 对磁盘 IOPS 极为敏感:

# 推荐的生产环境 etcd 配置
# /etc/etcd/etcd.conf
ETCD_NAME=master-1
ETCD_DATA_DIR=/var/lib/etcd
ETCD_LISTEN_CLIENT_URLS=https://0.0.0.0:2379
ETCD_LISTEN_PEER_URLS=https://0.0.0.0:2380

# 性能优化参数
--quota-backend-bytes=8589934592  # 8GB 存储配额
--auto-compaction-mode=periodic
--auto-compaction-retention=1h    # 每 1 小时压缩一次历史版本
--snapshot-count=10000             # 每 10000 次变更触发快照
# 检查 etcd 性能
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 
  --cacert=/etc/kubernetes/pki/etcd/ca.crt 
  --cert=/etc/kubernetes/pki/etcd/server.crt 
  --key=/etc/kubernetes/pki/etcd/server.key 
  check perf

# 查看 etcd 数据库大小
ETCDCTL_API=3 etcdctl endpoint status --write-out=table

2.2 API Server 过载

当集群规模较大(1000+ Pod)或频繁创建/删除资源时,API Server 可能成为瓶颈:

# 检查 API Server 的请求延迟
kubectl get --raw /metrics | grep -E "apiserver_request_duration_seconds_(bucket|sum)"

# 检查资源计数
kubectl get --raw /metrics | grep etcd_request_duration_seconds_sum

# 调优 kube-apiserver(在静态 Pod 配置中添加)
# --max-requests-inflight=2000
# --max-mutating-requests-inflight=1000
# --watch-cache=true
# --watch-cache-sizes=1000

踩坑实录:在一个大规模 CI/CD 集群中,持续集成的频繁资源创建导致 API Server 的 watch 连接数飙升到 50000+,最终 API Server OOM。解决方案是使用 API Server 的 --watch-cache-sizes 参数和合理设置 --max-requests-inflight


三、网络层面的常见问题

3.1 DNS 解析超时(CoreDNS 瓶颈)

CoreDNS 是 K8s 中极为关键的组件,配置不当会导致大量服务发现失败:

# CoreDNS ConfigMap 优化配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
            lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf {
            max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }
# 检查 CoreDNS Pod 的 DNS 查询 QPS
kubectl exec -n kube-system deploy/coredns -- 
  /coredns -plugins | grep -i cache

# 扩容 CoreDNS(缓解 DNS 查询风暴)
kubectl scale deployment/coredns -n kube-system --replicas=3

# 启用 autoscaler
kubectl apply -f https://raw.githubusercontent.com/kubernetes/.../cluster-proportional-autoscaler.yaml

3.2 节点端口耗尽

Kubernetes 中 Service 的 NodePort 和 HostPort 会占用节点端口,同时大量 Pod 发出的连接也会占用临时端口(ephemeral port):

# 检查临时端口范围
cat /proc/sys/net/ipv4/ip_local_port_range
# 默认输出:32768 60999 (约 28000+ 个端口)

# 扩大临时端口范围
echo "10240 65535" > /proc/sys/net/ipv4/ip_local_port_range

# 检查端口使用情况
ss -tpn | wc -l

四、存储层面的常见坑

4.1 PV 回收策略导致数据丢失

一个最常见的存储问题是 PV 回收策略的设置:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: production-pv
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain  # 避免使用 Delete!
  storageClassName: standard
  hostPath:
    path: /data/production

踩坑实录:某次误删除 PVC 后,由于 PV 的 persistentVolumeReclaimPolicy 设置为 Delete,底层存储直接被删除,所有数据丢失。生产环境应始终设置为 Retain

4.2 StatefulSet 中 Volume 挂载的顺序依赖

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  podManagementPolicy: OrderedReady  # 串行启动
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        storageClassName: ssd
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 50Gi

使用 OrderedReady 确保 Pod 按顺序启动,避免分布式数据库(如 PostgreSQL 主从、Cassandra)出现竞争条件。


五、性能调优最佳实践

5.1 资源请求与限制的合理设置

# 生产部署的资源设置示例
apiVersion: apps/v1
kind: Deployment
metadata:
  name: production-app
spec:
  template:
    spec:
      containers:
        - name: app
          image: myapp:1.0
          resources:
            requests:
              cpu: "500m"
              memory: "512Mi"
            limits:
              cpu: "1000m"      # 不超过 request 的 2 倍
              memory: "1Gi"     # 设置硬限制防止 OOM
          # 关键:启用 liveness + readiness probe
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5

黄金法则
– CPU request = 稳态使用量;limit ≤ request × 2
– Memory request = 稳态使用量;limit = request × 1.2~1.5
– 切勿设置 CPU limit = request(会导致 CPU 节流)
– 切勿不设置 memory limit(可能导致节点 OOM)

5.2 Pod 反亲和性避免资源争抢

apiVersion: apps/v1
kind: Deployment
metadata:
  name: compute-intensive
spec:
  replicas: 4
  template:
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - compute-intensive
                topologyKey: kubernetes.io/hostname

5.3 集群节点自动扩缩

# Cluster Autoscaler 配置
clusterAutoscaler:
  enabled: true
  autoDiscovery:
    clusterName: production-cluster
  maxNodesTotal: 50
  scaleDownUtilizationThreshold: 0.5
  scaleDownUnneededTime: "10m"
  scaleDownDelayAfterAdd: "10m"
  skipNodesWithLocalStorage: false
  skipNodesWithSystemPods: true

六、故障排查核心技巧

6.1 Pod 卡在 Pending 状态

# 排查 Pod 卡住的原因
kubectl describe pod <pod-name>
kubectl get events --field-selector involvedObject.name=<pod-name>

# 检查节点资源是否充足
kubectl describe node <node-name> | grep -A 5 "Allocated resources"

# 检查 PVC 是否绑定
kubectl get pvc
kubectl describe pvc <pvc-name>

6.2 Pod 频繁 CrashLoopBackOff

# 查看日志
kubectl logs <pod-name> --previous   # 查看上次崩溃的日志

# 检查 OOM kill
kubectl describe pod <pod-name> | grep -i "OOM|Exit"

# 检查容器启动命令是否正确
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].command}'

6.3 网络不通排查三板斧

# 1. 检查 Service 是否有 Endpoint
kubectl get endpoints <service-name>

# 2. 检查 DNS 解析
kubectl run -it --rm debug --image=alpine:latest -- sh
# 在容器内执行
# nslookup <service-name>
# cat /etc/resolv.conf

# 3. 检查集群网络插件状态
kubectl get pods -n kube-system | grep -E "calico|flannel|weave"

# 4. 直接测试连通性
kubectl exec -it <pod-name> -- curl -v http://<svc-name>:<port>

七、生产运维最佳实践清单

日常运维 checklist

  1. 备份是底线 — 每天备份 etcd 快照 + 定期用 Velero 备份 PV
  2. 资源配额无处不在 — 每个 Namespace 设置 ResourceQuota 和 LimitRange
  3. PodDisruptionBudget 不要忘 — 确保滚动更新时服务不中断
  4. 监控必须到位 — Alertmanager 配置好告警规则(节点不可达、Pod 重启、磁盘满)
  5. 安全审计开启 — 开启 Kubernetes 审计日志,保留至少 90 天
  6. 镜像版本锁定 — 永远使用 image:tag 而非 image:latest
  7. 资源声明是金标准 — 所有 Pod 必须设置 requestslimits

重要告警规则

# Prometheus 告警规则片段
groups:
  - name: kubernetes-alerts
    rules:
      - alert: KubeNodeNotReady
        expr: kube_node_status_condition{condition="Ready",status="true"} == 0
        for: 5m
        severity: critical
      - alert: KubePodCrashLooping
        expr: rate(kube_pod_container_status_restarts_total[5m]) > 1
        for: 5m
        severity: warning
      - alert: KubePersistentVolumeFillingUp
        expr: (
          kubelet_volume_stats_available_bytes / kubelet_volume_stats_capacity_bytes
        ) < 0.1
        for: 5m
        severity: critical
      - alert: KubeCPUThrottlingHigh
        expr: rate(container_cpu_cfs_throttled_seconds_total[5m]) > 0.3
        for: 5m
        severity: warning

总结

经过 30 天的学习,我们从零开始完整走过了 Kubernetes 的入门、存储网络、调度策略、监控日志、安全备份、集群管理,再到今天的生产踩坑实录。K8s 是一个庞大而精密的系统,真正掌握它需要在实践中不断摸索。

最后分享三条生产环境的铁律:

1. 所有操作先考虑失败的后果 — 写时注解、读时检查、删时三思

2. 监控先行 — 没有监控的集群就像蒙眼开车

3. 一切皆代码 — 集群配置、部署清单、备份策略都应该版本化管理

K8s 的学习没有终点,生产环境的挑战层出不穷。希望这个系列能成为你 K8s 之路上的可靠参考。


📖 系列目录

🎉 30 天 K8s 系列全部完成! 感谢一路相伴,祝你在 K8s 之路上一帆风顺!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容