Kubernetes资源限制实战:Requests/Limits、监控与Dashboard

发布时间:2026/10/21 18:33:39
Kubernetes资源限制实战:Requests/Limits、监控与Dashboard 1. 为什么要给容器戴上“紧箍咒”——资源限制的核心逻辑先说个我早期踩过的坑某个微服务改造项目上线后集群里多个节点的负载频繁拉满但排查了一圈发现业务量并没有暴涨。后来用docker stats一看原来是一个跑批任务的服务把所在节点的内存吃掉了将近 80%导致同节点的其他容器疯狂 GC整个节点雪崩。当时团队还没有任何资源限额的约束大家各写各的 deployment谁都没意识到一个小容器能拖垮整个宿主机。这个案例其实已经很能说明问题了Kubernetes 本身只是“调度器”不会替你做资源分配层面的防御。你给它多少 Pod它就往节点上塞多少 Pod至于每个 Pod 能不能吃满节点、会不会影响邻居K8s 不管。所以容器资源限制不是“最佳实践”层面的加分项而是从第一天起就该做的事。1.1 三种资源分配维度的取舍逻辑Kubernetes 调度时主要看三类资源可压缩资源比如 CPU。CPU 是时间片调度的限制不生效时最多就是变卡但进程通常不会丢。不可压缩资源比如内存。内存超限会直接触发 OOM Kill容器被杀业务中断。临时存储比如 emptyDir、系统日志目录。打满会触发驱逐Eviction整个节点都会出问题。这三个维度里内存限制是最优先要做的其次是 CPU再其次是存储。很多人一上来就几百行 YAML 精心设计但实际生产里最务实的思路是先把内存限制做对再逐步补 CPU 和存储。1.2 Requests 和 Limits这两者到底是什么关系资源限制本质上就两个字段requests请求和limits上限。我见过不少新手把这两个概念搞混想当然以为 requests 就是要给这么多资源limits 是最高能用多少。听上去没毛病但落到调度器、节点运维层面两者的语义分工完全不同。requests是调度依据。调度器选节点时看一眼节点上所有 Pod 的 requests 总和剩余资源如果小于新 Pod 的 requests这个节点就不被考虑。所以 requests 相当于“预占席位”。limits是运行上限。容器实际使用中最多能用到多少资源由 limits 决定。CPU 超过 limit 会被节流throttled内存超过 limit 直接被杀。这里有个关键点Kubernetes 的 QoS服务质量等级就是按 requests 和 limits 的匹配关系划分的。QoS 等级条件实际效果Guaranteedrequests 和 limits 相等CPU 和内存都相等不会被驱逐优先级最高最稳妥Burstablerequests 小于 limits或只设置了部分 requests有机会抢资源但节点压力大时可能被驱逐BestEffort没设置 requests 和 limits系统压力大时最先被驱逐基本裸奔生产环境里的建议是把核心业务尽量做成 Guaranteed非核心业务用 BurstableBestEffort 除非极其边缘的场景比如本地开发测试否则别碰。1.3 CPU 和内存的单位换算别在细节上翻车CPU 单位是“核”cores可以写成整数或小数比如0.5、1、2也支持微核写法500m500 millicores。1 核等于 1000m250m就是四分之一个核。需要注意的点是CPU 的 requests/limits 设置得过碎比如十几毫核会显著增加调度开销因为调度器每个节点都要做资源预留计算。实际中一个容器 CPU 请求低于 100m 的场景很少见除非真的很轻量。内存单位是字节bytesK8s 里常见的有Mi、Gi、M、G。Mi 是二进制换算1Mi 1024Ki 1048576 字节M 是十进制1M 1000k 1000000 字节。绝大多数镜像和 JVM 参数都按 Mi/Gi 来算所以我日常写配置一律用 Mi/Gi避免十进制的 M/G 与各语言运行时的不一致导致误差。举一个典型的 Java 应用配置resources: requests: cpu: 500m memory: 1Gi limits: cpu: 2 memory: 2Gi这里 requests 的 memory 是 1Gi但 Java 的堆内存参数如果设成-Xmx2g加上 Metaspace、线程栈等开销物理内存占用会超过 2Gi直接 OOM。这类问题不是 K8s 配置本身的 bug而是业务侧对运行时内存画像不清楚。所以配置 limits 前花点时间摸清楚应用的基础内存占用比反复调整 YAML 更有价值。2. 实操给 Pod 配上真正可靠的资源限额理论聊完得落实到部署文件里。资源限制写在 Pod 模板的containers段下每个容器单独设置不能全局统一设置。这个粒度是刻意的同一 Pod 内不同容器面对的场景不同Sidecar比如日志采集、网络代理的消耗通常远小于主业务容器混合设置成同样的规格反而浪费。2.1 编写带资源限制的基础配置先看一个标准的 nginx 示例。假设我们有一个模拟项目 X 的网关服务主要工作是转发请求内存需求不大apiVersion: apps/v1 kind: Deployment metadata: name: gateway-service spec: replicas: 3 selector: matchLabels: app: gateway template: metadata: labels: app: gateway spec: containers: - name: gateway-container image: nginx:1.25 ports: - containerPort: 80 resources: requests: cpu: 200m memory: 256Mi limits: cpu: 500m memory: 512Mi这段配置的含义是调度器按照每个副本 200m CPU、256Mi 内存来寻找合适节点允许 3 个副本共存。运行时 CPU 最高能用到 500m超过就会被内核节流不至于拖垮整机内存最高 512Mi超出直接杀掉。写这段配置时有一点很多人容易忽略replicas: 3意味着总 requests 会被调度器考虑到节点容量总和里。如果集群里某节点只剩 400m CPU 可调度那第三个副本就会持续 Pending直到有空间腾出来。实际操作中调整副本数之前最好先算一遍当前集群的容量余量而不是盲目加副本。2.2 给命名空间套上统一的资源规则前面说的是“手动一个一个配置”但团队人多的时候总有人漏配或者配错。水平参差不齐就会导致不受控的资源竞争进入生产集群。这时候需要两个集群层面的“兜底机制”LimitRange和ResourceQuota。LimitRange的作用是给命名空间内的 Pod 设置默认值或最小/最大值。比如在名为develop的命名空间里这样配置apiVersion: v1 kind: LimitRange metadata: name: dev-limits namespace: develop spec: limits: - default: cpu: 500m memory: 512Mi defaultRequest: cpu: 200m memory: 256Mi max: cpu: 4 memory: 4Gi min: cpu: 50m memory: 128Mi type: Container这个 LimitRange 的含义是如果某个 container 没写 resources那么自动注入默认的 requests 和 limits即使写了也不能超过 max 或低于 min否则创建 Pod 的 API 请求会被拒绝。ResourceQuota则是从命名空间总量上限制。比如apiVersion: v1 kind: ResourceQuota metadata: name: dev-quota namespace: develop spec: hard: requests.cpu: 8 requests.memory: 16Gi limits.cpu: 16 limits.memory: 32Gi这两个机制组合起来基本能保证“少写一个字段系统自动兜底”。我在某公司搭测试环境时就是靠这套组合把十几个开发共用集群的稳定度提上来的在此之前几乎每周都有一次“某个测试环境把整集群搞崩”的事件。3. 监控没有可视化数据限制就是拍脑袋只做限制不做监控等于蒙着眼睛开车。你没法知道当前限制设置是否合理也没法预判热点 Pod 的峰值。经常遇到的情况是 limits 设了 500m CPU业务高峰期 CPU 一直被节流接口延迟飙升但没人告诉你是资源受限了。如果不借助监控排查这类问题会花掉大把时间。3.1 Metrics-Server 是监控链路的第一环Kubernetes 本身不内置监控采集组件常见的轻量级方案是部署metrics-server。它从每个节点的 kubelet 上周期性拉取指标聚合后通过 Metrics API 提供给上层使用。部署方式很简单用官方给出的components.yaml文件直接 applykubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml生产环境需要注意两点第一如果集群是自建的而不是托管的那么 kubelet 默认的 TLS 证书可能不包含节点的内网 IP。metrics-server 拉取时会报证书校验失败。快速解决方案是在部署文件中给 metrics-server 加一个启动参数args: - --kubelet-insecure-tls但这种方法有点一刀切只建议在内部的、无敏感数据的环境用。稍微正规一点的做法是给 kubelet 签发包含节点 IP 的证书不过那要动集群配置成本高很多。第二如果节点使用 cgroup v2而 metrics-server 版本太老会出现内存指标缺失或与实际不符。建议直接使用最新 release 版本旧版本对 cgroup v2 的支持不够完善。3.2 kubectl top 的日常用法与数据解读metrics-server 部署成功后最直接的消费方式就是kubectl top。# 查看所有节点的资源消耗 kubectl top nodes # 查看某个命名空间下所有 Pod 的实时资源使用 kubectl top pods -n develop # 查看 Pod 内各容器的资源使用 kubectl top pod gateway-service-8c5648d5f-2kx7m --containers -n develop输出大概是这样的NAME CPU(cores) MEMORY(bytes) gateway-service-8c5648d5f-2kx7m 128m 340Mi auth-service-7d6686b449-l2d9c 45m 210Mi注意这里的数字是“实时值”不是“平均值”。top 命令适合快速排查不适合做趋势分析。如果想判断一个 Pod 在业务高峰期是否触顶需要在重点时间段多次执行或者直接依赖 Prometheus 这类时序监控系统。实操中我有个习惯每次上线新服务先在测试环境连续观察 3 天kubectl top pods记录业务高峰期的资源消耗再反推 limits 的合理值。等上了生产再频繁调参就慢了。3.3 HPA 自动扩缩容是如何依赖资源指标的资源限制不仅是为了防止个别容器失控它还能配合自动扩缩容实现“按负载调副本数”。HorizontalPodAutoscalerHPA可以监控 Pod 的平均 CPU 使用率超过目标阈值就增加副本数。一个典型的配置apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: gateway-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: gateway-service minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70这个 HPA 的意思是当所有副本的 CPU 平均使用率达到 requests 中 CPU 的 70% 时开始扩容使用率低于 50% 时开始缩容。HPA 会轮询 metrics-server 的指标默认的同步周期是 15 秒。使用 HPA 有个先决条件Pod 必须设置 CPU requests而且这个值要尽量贴近真实需求。如果 requests 设得很低比如 100m但实际需要 300mHPA 检测到平均利用率很容易超过 70%副本数会疯狂暴涨。如果 requests 设得很高比如 2 核但实际只要 0.5 核那平均利用率永远到不了 70%HPA 形同虚设。所以 HPA 调参数的功夫很大一部分其实花在了“搞清楚 Pod 实际需要多少资源”上。4. Dashboard 部署让集群从命令行走向可视化kubectl用得再顺也没法在开会时端着终端展示资源曲线。可视化面板的价值不在于替代命令行而是让运维、开发、管理层之间有一个共同的、直观的认知窗口。Kubernetes 官方提供的Dashboard就是一个部署简单、开箱即用的方案。4.1 部署 Dashboard官方版本部署命令同样非常直接kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml这条命令会创建kubernetes-dashboard命名空间以及对应的 Deployment、Service、ServiceAccount 等资源。这里有个细节值得注意recommended.yaml里创建的 Service 默认类型是ClusterIP意味着只能在集群内部访问。外部访问常见的有三种方式方式一kubectl proxy代理。本地执行kubectl proxy后浏览器访问http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/。优点是零改动缺点是只适用于个人临时调试。方式二把 Service 改成 NodePort。在 Service 上添加type: NodePort并指定端口然后通过任意节点 IP 加端口访问。这是团队内部常用的方式最省事。方式三Ingress。通过域名和证书接入网关。适合生产环境或团队已具备稳定 Ingress Controller 的情况。我个人的建议是先 NodePort 用起来等团队习惯了 Dashboard 操作再逐步迁移到 Ingress。4.2 登录凭证Token 的获取和权限模型新部署的 Dashboard 默认没有任何可用账号需要我们自己创建管理员账号并拿到 Token。先创建管理员 ServiceAccountapiVersion: v1 kind: ServiceAccount metadata: name: admin-user namespace: kubernetes-dashboard再绑定 cluster-admin 角色apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-user roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: admin-user namespace: kubernetes-dashboard然后获取 Tokenkubectl -n kubernetes-dashboard create token admin-user拿到的一长串 Token 就可以在 Dashboard 登录页的“令牌”选项里粘贴登录了。这里必须强调一个安全问题cluster-admin是集群最高权限相当于整个 K8s 环境的 root。如果你把 Token 发给开发人员他们顺手就能删掉任意命名空间的资源。团队成员多的时候更好的做法是根据职责创建只读账号或限定命名空间的账号。生产经验告诉我一个团队至少配三种角色只读审计、命名空间管理员、集群管理员。只读角色用viewClusterRole 绑定命名空间管理员给指定命名空间绑定adminRole真正需要全局权限的人再单独分配 cluster-admin。这样可以有效控制“谁能做什么”即使发错凭证爆炸半径也有限。4.3 Dashboard 界面上的高频操作路径登录进去之后Dashboard 默认会展示命名空间列表、节点状态、工作负载等总览信息。日常用得最多的几个场景查看节点水位点击“节点”菜单能看到每个节点的 CPU、内存实际用量和可分配量。这个视图比kubectl top node直观很多适合汇报工作或排查节点倾斜问题。查看工作负载状态在“工作负载”下能看到每个 Deployment 的期望副本数和实际副本数扩张或缩容只需点一下编辑修改副本数后保存即可。这种操作对不熟悉命令行的同事非常友好。查看日志进入某个 Pod 详情页可以直接看到当前容器日志省去了kubectl logs -f的命令敲击。执行命令Pod 详情页有“执行”按钮可以打开一个终端会话进入容器。这和kubectl exec -it是同样的能力。注意如果容器里没有/bin/sh比如纯二进制镜像这个按钮会报错不是 Dashboard 的问题。4.4 安全加固建议Dashboard 默认比较“开放”部署到生产前至少要过这几道关第一开启登录访问控制之前避免直接把 Dashboard 暴露到公网。就算用 NodePort防火墙层面也要限制来源 IP 白名单。第二给 Dashboard 的 Service 加上更细的网络策略只允许内网网段或特定办公网段访问。第三如果团队有统一认证体系比如 OIDCDashboard 也支持接入这样就不用手动分发 Token账号生命周期管理也顺带解决了。5. 常见问题与排查实录把资源限制、监控、Dashboard 三者打通以后日常维护中还是会有各种“怪毛病”。下面这些是我在几个不同的项目环境里真实排查过的问题不一定每次都能在网上搜到现成答案但排查思路是可以复用的。5.1 资源限制配置了却不生效症状YAML 里明明写了 limits但kubectl describe pod看不到对应的字段或者容器实际使用远超 limit 却没被杀、没被节流。排查流程先确认修改的 Deployment 是否真的滚动更新了。kubectl rollout status deployment/xxx看看返回结果。再看 Pod 本身kubectl get pod pod-name -o yaml | grep -A5 resources如果资源字段是空的说明用的镜像模板有问题。最常见的原因是修改了旧版本的 Deployment 对象而实际使用的 Helm Chart 或 Kustomize 模板里又覆盖了 resources 字段。注意kubectl describe输出里的 QoS Class 是BestEffort还是Guaranteed。如果是 BestEffort说明 requests 和 limits 都没写对或者只写了 requests、没写 limits导致 QoS 降级。还有一种很隐蔽的情况镜像内部应用自己限制了资源。比如 Java 应用通过-XX:MaxRAMPercentage50.0把堆内存限制在物理内存的 50%这时容器内部的进程不可能超出这个自设上限K8s limits 再高也只是冷冰冰的数字。资源限制不是“自动展示成容器内可用量”应用自己不感知 K8s limits 的话还是要靠运行时的参数来控制。5.2 kubectl top 拿不到数据一直提示 Metrics API 不可用症状error: metrics.k8s.io/v1beta1: the server is currently unable to handle the request排查流程查看 metrics-server Pod 是否 Runningkubectl -n kube-system get pods | grep metrics-server查看日志kubectl -n kube-system logs deployment/metrics-server --tail20如果日志里全是 x509 证书相关报错按我们前面说的要么加--kubelet-insecure-tls启动参数要么给 kubelet 做证书改造。大量自建集群都是这个原因导致的。还要注意 metrics-server 的资源占用。它本身也是 Pod如果节点上可用内存太少metrics-server 会被 OOM Kill表现就是“时好时坏重启后恢复一会儿又挂”。给它设置一个合理的 limits 很有必要默认模板的 requests 通常不高但 limits 很多时候没有被显式设置。5.3 Dashboard 白屏或无法加载数据症状Chrome 打开 Dashboard 地址页面能出来框架但大量图表区域是空白或者报错轮询接口超时。排查流程确认 Dashboard 的 Pod 状态kubectl -n kubernetes-dashboard get pods如果 Pod 一直 CrashLoopBackOff多半是镜像版本太旧和当前集群 API Server 版本不兼容。升级 Dashboard 版本即可。如果 Pod 正常但页面拿不到数据先看是不是网络策略拦截了 Dashboard 访问 API Server 的流量。自建集群里经常有 CNI 网络策略默认拒绝需要显式放行 kubernetes-dashboard 命名空间到 kube-system 的流量。另外Dashboard 登录后的默认超时时间是 15 分钟默认配置如果长时间不操作Token 会过期。刷新页面再用 Token 重新登录即可。5.4 HPA 扩缩容“反应迟钝”症状CPU 明明已经飙到 90% 了HPA 等了五六分钟才扩容。排查思路这不是 bug。HPA 的设计就是“避免抖动”默认的--horizontal-pod-autoscaler-sync-period是 15 秒但计算副本数、等待 Deployment 滚动更新、以及 metrics-server 的采集周期叠加整体从指标超标到副本就绪通常会有几分钟的延迟。如果业务流量是秒级剧增单靠 HPA 反应太慢需要结合cluster-autoscaler节点级扩展和 HPA 的behavior字段来控制扩容速率。给 HPA 增加扩容速率的示例behavior: scaleUp: stabilizationWindowSeconds: 0 policies: - type: Pods value: 4 periodSeconds: 60 selectPolicy: Max这个配置的意思是每分钟最多新增 4 个副本没有冷却期。5.5 一个综合排查案例某跨平台系统上线时的整体资源问题有一次在某公司部署一个跨平台系统涉及 API 网关、业务服务、Redis 和对象存储等多个组件。上线前我们统一设置了 requests 和 limits但压测时发现网关服务频繁超时日志里大量 Connection reset。一开始怀疑是 K8s 资源限制的问题但kubectl top pods显示网关 CPU 才 300m 左右远没到 limit。后来用docker inspect看进程的实际状态发现网关进程自己开了大量线程文件描述符爆了根本不是 K8s 层面的问题。把容器的ulimits调高后问题消失。这个案例说明容器资源限制和监控解决的是“资源维度”的问题而进程内部的文件句柄、线程数、连接数等指标K8s 层面的监控未必能直接暴露。运维视角要更宽不能只盯着 CPU 和内存。6. 最后分享一点实际体会把资源限制、监控、Dashboard 三个东西一起落地是我在维护 Kubernetes 集群时觉得“最值”的一组投入。限制让每个容器有边界监控让边界有反馈Dashboard 让反馈被团队看到。单独做任何一个都容易变成一次性工程而组合起来就形成了一套可以自我迭代的闭环先根据监控数据反推资源限制的合理性再借助 Dashboard 的直观面板让团队其他人也参与到容量规划里来。在这个过程中我犯过最大的错误是一开始把 requests 和 limits 设得偏高。想想是因为“怕容器不够用”的心态结果集群里每个 Pod 都预留了大量资源实际利用率不到 30%。这不仅浪费成本也让 HPA 的触发阈值形同虚设。后来学乖了先用监控跑数据基于历史峰值乘以一个 1.2 的安全系数来定 limits再按“真实用量的一半”左右来设 requests给调度留出灵活空间。这套方法未必适用于所有场景尤其是那种流量波动极大的服务需要更细的容量规划。但对于绝大多数常规业务先搞定限制、监控、面板再把 HPA 接入稳定的集群底座基本就成形了。每一步都不难难的是把这三件事作为一个整体持续推进。如果你正在搭自家的 Kubernetes 环境不妨就按这个顺序一项项来过程中多记录几次线上波峰的数据之后的资源规划会轻松很多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询