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-node、kube-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
- 备份是底线 — 每天备份 etcd 快照 + 定期用 Velero 备份 PV
- 资源配额无处不在 — 每个 Namespace 设置 ResourceQuota 和 LimitRange
- PodDisruptionBudget 不要忘 — 确保滚动更新时服务不中断
- 监控必须到位 — Alertmanager 配置好告警规则(节点不可达、Pod 重启、磁盘满)
- 安全审计开启 — 开启 Kubernetes 审计日志,保留至少 90 天
- 镜像版本锁定 — 永远使用
image:tag而非image:latest - 资源声明是金标准 — 所有 Pod 必须设置
requests和limits
重要告警规则
# 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 之路上的可靠参考。
📖 系列目录
- 第 1 天:Kubernetes 是什么?核心概念与架构全景解析
- 第 2 天:手把手搭建你的第一个 K8s 集群(kubeadm 实战)
- 第 3 天:Pod 详解:K8s 最小的调度单元与生命周期管理
- 第 4 天:Deployment 与 ReplicaSet:声明式应用管理
- 第 5 天:Service 与网络基础:ClusterIP、NodePort、LoadBalancer 详解
- 第 6 天:Namespace 与资源配额:多租户隔离基础
- 第 7 天:ConfigMap 与 Secret:配置管理最佳实践
- 第 8 天:Volume 与 PersistentVolume:存储抽象层的核心机制
- 第 9 天:StorageClass 与动态存储供给实战
- 第 10 天:StatefulSet:有状态应用的部署与管理
- 第 11 天:Ingress 与 Ingress Controller:外部流量接入全攻略
- 第 12 天:NetworkPolicy:K8s 网络安全策略与微隔离
- 第 13 天:Headless Service 与服务发现机制深度解析
- 第 14 天:CSI 存储插件与生产存储选型指南
- 第 15 天:污点与容忍度:掌控 Pod 调度
- 第 16 天:Node Affinity 与 Pod Affinity:精细化调度策略实战
- 第 17 天:HPA 水平自动扩缩:基于 CPU/内存/自定义指标的弹性伸缩
- 第 18 天:VPA 与 Cluster Autoscaler:资源与集群层的自动扩缩
- 第 19 天:Job 与 CronJob:批处理与定时任务实战
- 第 20 天:PriorityClass 与抢占式调度机制
- 第 21 天:资源配额与 LimitRange:多租户资源管控
- 第 22 天:监控体系搭建:Prometheus + Grafana + Kube-state-metrics
- 第 23 天:日志收集实战:EFK/ELK 栈在 K8s 中部署
- 第 24 天:RBAC 权限控制:ServiceAccount、Role、ClusterRole 深度解析
- 第 25 天:Helm Charts:包管理器使用与 Chart 开发实战
- 第 26 天:集群备份与恢复:etcd 快照 + Velero 方案
- 第 27 天:滚动更新与回滚策略:实现零停机发布
- 第 28 天:集群升级最佳实践:Control Plane 与 Node 升级步骤
- 第 29 天:多集群管理:Federation / Cluster API / 多集群服务网格
- 第 30 天:K8s 生产环境踩坑实录:性能调优、故障排查与最佳实践 ✅ ← 本篇(完结篇)
🎉 30 天 K8s 系列全部完成! 感谢一路相伴,祝你在 K8s 之路上一帆风顺!















暂无评论内容