Kubernetes生产环境的十个配置陷阱:从资源限制到探针配置的避坑手册
如果说Kubernetes是一架精密的航空发动机,那么配置就是控制面板上密密麻麻的旋钮。拧错任何一个,轻则效率下降,重则空中停车。本文梳理十个高频K8s配置陷阱,每一个都有明确的现象-根因-修复路径。
一、K8s配置陷阱的分类框架
从故障模式出发,K8s配置陷阱可以分为四类:
- 资源管理类:与CPU/内存的分配和限制相关(陷阱一、二)
- 可用性类:影响Pod调度、驱逐和自愈的配置(陷阱三、四、五)
- 安全类:涉及凭证管理和访问控制(陷阱六、七)
- 运维类:影响日常操作和问题排查的配置(陷阱八、九、十)
二、十个配置陷阱逐一剖析
陷阱一:未设置资源requests和limits
现象:节点内存压力大时,未设置requests的Pod被随机驱逐,甚至驱逐了核心服务。
根因:K8s调度器依赖requests做调度决策,kubelet依赖requests和limits做驱逐决策。未设置requests的Pod被赋予BestEffort QoS等级,在资源紧张时最先被杀死。
修复:
- 所有Pod必须设置
resources.requests和resources.limits - 通过LimitRange在namespace级别强制默认值
- 对核心服务设置
priorityClassName: high-priority配合requests使用
验证命令:
# 找出未设置requests的Pod kubectl get pods --all-namespaces -o json | \ jq '.items[] | select(.spec.containers[].resources.requests == null) | .metadata.name'陷阱二:limits等于requests的误用
现象:服务在低负载时CPU使用率仅5%,但HPA无法缩容——原因是requests设得和limits一样高。
根因:将limits=requests是为了获得Guaranteed QoS,但牺牲了资源弹性。在负载波动场景下,这意味着所有Pod都预留了峰值资源,集群利用率极低。
修复:
- 区分稳态和峰值:requests设为P50用量,limits设为P95用量
- 对批处理任务使用Guaranteed(limits=requests),对在线服务使用Burstable
- 监控实际用量,每季度调整requests/limits至合理的"实际用量×1.3"
陷阱三:探针过于激进导致重启风暴
现象:应用启动需要60秒,但startupProbe或livenessProbe的initialDelaySeconds只设了10秒。Pod陷入CrashLoopBackOff。
根因:探针检查在应用就绪前开始执行,livenessProbe连续失败导致容器被杀死。在滚动更新时,新旧Pod都无法服务,造成短暂全站不可用。
修复:
- 使用
startupProbe(K8s 1.16+),将其failureThreshold * periodSeconds设为略大于应用最大启动时间 livenessProbe的initialDelaySeconds设为0,让startupProbe先接管readinessProbe的failureThreshold至少设为3,避免临时抖动导致Pod被摘流
# 推荐的探针配置 startupProbe: httpGet: path: /health port: 8080 periodSeconds: 10 failureThreshold: 12 # 最多等120秒 livenessProbe: httpGet: path: /health port: 8080 periodSeconds: 15 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 8080 periodSeconds: 5 failureThreshold: 3陷阱四:探针过于宽松导致流量打到未就绪Pod
现象:Pod启动后立刻接收流量,但应用内部的连接池、缓存尚未初始化完成,首批请求大量超时。
根因:readinessProbe未配置,或探针端点只检查端口是否监听,不检查内部依赖就绪状态。
修复:
- readinessProbe端点必须验证关键依赖就绪(数据库连接池、Redis连接、配置加载完成)
- 使用
/ready端点实现深度健康检查,而非简单的/health - 结合
podReadinessGates实现更细粒度的流量控制
陷阱五:未配置PodDisruptionBudget
现象:运维人员执行kubectl drain,K8s同时驱逐了某服务的所有Pod,导致服务短暂不可用。
根因:没有PDB约束,K8s允许同时驱逐任意数量的Pod。在节点维护、集群升级时,核心服务可能被"一波带走"。
修复:
- 所有多副本服务必须配置PDB
- 关键服务设置
maxUnavailable: 1或minAvailable: N-1(N为副本总数) - 定期检查PDB生效状态:
kubectl get pdb -A
陷阱六:Secret以明文形式存在
现象:Secret通过环境变量注入,在应用日志、错误堆栈、调试信息中意外泄露。
根因:环境变量方式注入的Secret可被应用内任何代码读取,且在core dump、/proc/{pid}/environ中可见。
修复:
- Secret优先使用Volume挂载,而非环境变量注入
- 启用
encryption at rest:在etcd中对Secret加密存储 - 集成外部密钥管理(Vault、AWS Secrets Manager),通过CSI驱动挂载
- 使用OPA/Kyverno策略禁止将Secret作为环境变量
陷阱七:所有资源部署在default namespace
现象:生产、测试、开发环境共享同一集群时,资源全在default namespace下,误操作风险极大。
根因:default namespace缺乏隔离性,一条错误的kubectl delete可能同时影响所有环境。
修复:
- 强制使用命名空间隔离:
prod、staging、dev至少三个独立namespace - 使用RBAC限制每个namespace的访问权限
- 通过NetworkPolicy限制跨namespace流量
- 使用ResourceQuota为每个namespace设置资源上限
陷阱八:日志未集中收集
现象:Pod被驱逐或重启后,之前的日志随容器销毁而丢失,问题排查只能"盲人摸象"。
根因:依赖kubectl logs做问题排查,但容器销毁后日志不可追溯。节点磁盘满时,kubelet也会主动清理日志。
修复:
- 部署日志采集Agent(Fluentd/Fluent Bit/Vector)以DaemonSet形式运行
- 将stdout/stderr集中发送到Loki/Elasticsearch
- 关键业务日志同时写一份到持久化存储
- 设置日志保留策略:至少保留7天,核心服务保留30天
陷阱九:标签体系混乱
现象:同一应用的不同环境Pod使用不同标签命名,或者标签名随意拼写(如app和application混用)。
根因:Service的selector依赖标签匹配,标签混乱导致流量路由错误——曾经出现过生产流量被路由到测试Pod的事故。
修复:
- 强制使用标准化标签集:
app.kubernetes.io/name、app.kubernetes.io/instance、app.kubernetes.io/version、app.kubernetes.io/environment - 使用Kyverno策略在准入时校验标签完整性
- 标签变更纳入GitOps流程,禁止手动修改
陷阱十:集群资源配额未设置
现象:某个团队的测试Job申请了集群全部剩余资源,导致生产Pod无法调度。
根因:namespace级别未设置ResourceQuota,任何namespace理论上可以消耗集群全部资源。
修复:
- 每个namespace设置ResourceQuota,限制总CPU、内存、PVC数量
- 设置LimitRange为Pod设置默认requests/limits
- 结合PriorityClass确保生产Pod在资源紧张时优先调度
三、配置审计自动化
对于已有集群,建议建立配置审计流水线,定期扫描以下项目:
| 审计项 | 检查规则 | 严重级别 |
|---|---|---|
| 无requests/limits | resources.requests 或 resources.limits 为空 | 严重 |
| 无PDB | replicas > 1 且无关联PDB | 警告 |
| Secret为环境变量 | secretKeyRef 存在 | 警告 |
| 探针缺失 | livenessProbe 或 readinessProbe 未配置 | 警告 |
| 使用default namespace | namespace = "default" | 信息 |
可用工具:kube-score、polaris、popeye。建议集成到CI/CD中,对新增部署做准入检查。
四、配置治理的演进路径
K8s配置治理不是一次性工程,而是一个持续演进的过程:
第一阶段(有就行):为所有Pod设置基本的requests/limits和探针。
第二阶段(配得对):基于实际监控数据调整资源配置,优化探针参数。
第三阶段(管得住):通过OPA/Gatekeeper/Kyverno策略强制配置规范,违规部署自动拒绝。
第四阶段(自优化):结合VPA(Vertical Pod Autoscaler)自动调整requests/limits,探针参数基于启动时间历史数据自动推荐。
五、总结
K8s的配置陷阱有一个共性:默认值在生产环境中几乎总是错的。K8s的设计哲学是"给用户最大灵活性",这意味着它不会替你做任何安全假设。没有资源限制?可以。没有探针?可以。没有PDB?都可以。但生产环境不能这样。
建议每个K8s集群至少部署kube-score或polaris做一次全量配置扫描,你会发现比自己预想的更多问题。配置治理是一项投入小、回报大的工程实践——花一天时间修配置,可能省下一周的故障排查时间。