Kubernetes 基础体系 · 第 70/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
Kubernetes 成本治理:资源归属、闲置、Spot、存储网络和优化边界
Kubernetes 成本治理不是简单地把节点数量乘以云厂商单价。集群账单通常由计算、持久化存储、网络流量、负载均衡、日志与监控、控制面,以及云厂商附加服务共同构成;Kubernetes 只直接管理其中一部分资源。
成本治理的核心问题可以形式化为:
而组织真正需要回答的是:
- 这笔成本属于哪个团队、应用、环境或租户?
- 付费资源中有多少没有产生有效业务价值?
- 哪些工作负载可以使用更便宜但更不稳定的资源?
- 优化后是否损害了可用性、性能、数据持久性或故障恢复能力?
- 成本降低是否只是把费用从一个项目转移到了另一个项目?
因此,Kubernetes 成本治理应当同时处理资源归属、容量配置、闲置识别、弹性资源、存储、网络和优化边界。
一、先建立成本模型:Kubernetes 对账单做了什么、没有做什么
1. 计算成本的基本单位是节点,而不是 Pod
云厂商通常按照虚拟机、裸金属节点或托管节点池计费。Kubernetes 调度 Pod 时关心的是节点的可分配资源,而不是云账单中的单个 Pod。
设一个节点的小时成本为 ,节点池中有 个节点,则计算成本近似为:
Pod 只是占用了节点上的 CPU、内存、临时存储等资源。除非使用了能够根据 Pod 或命名空间做成本分摊的外部系统,否则云厂商账单不会天然知道某个 Pod 应该承担多少钱。
因此必须区分两个概念:
- 账单成本:云平台实际收费的节点、磁盘、流量和服务费用。
- 分摊成本:根据资源使用、资源申请量或业务规则,把账单映射到团队和应用的内部成本。
例如,两个命名空间分别运行在同一个节点上:
节点实际成本:1.00 美元/小时
team-a 的 Pod request:2 vCPU,4 GiB
team-b 的 Pod request:1 vCPU,4 GiB
节点 allocatable:4 vCPU,8 GiB
如果按照 request 分摊,则:
team-a:约 66.7%
team-b:约 33.3%
但如果 team-a 的 CPU 实际利用率远低于 team-b,按照 request 分摊仍然可能不符合某些财务或容量分析目的。成本分摊规则必须先明确:是按预留容量、实际使用量、峰值占用,还是按固定的共享服务规则。
2. 成本归属至少需要三层信息
一套可审计的归属模型通常包含:
组织归属:团队、部门、成本中心
技术归属:集群、节点池、命名空间、工作负载
业务归属:服务、产品、环境、租户
Kubernetes 原生对象中的常见归属字段包括:
metadata.labels:用于查询、聚合和选择;metadata.annotations:适合保存描述性信息,不适合承担高频筛选和严格枚举;namespace:提供逻辑隔离,但不是天然的财务成本中心;- 节点标签:表示区域、架构、节点池、容量类型等;
- 节点污点:控制哪些 Pod 可以被调度到节点上。
一个工作负载可以使用如下标签:
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout
namespace: retail-prod
labels:
app.kubernetes.io/name: checkout
app.kubernetes.io/instance: checkout-prod
app.kubernetes.io/component: api
app.kubernetes.io/part-of: retail
cost-center: cc-042
owner-team: payments
environment: production
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: checkout
app.kubernetes.io/instance: checkout-prod
template:
metadata:
labels:
app.kubernetes.io/name: checkout
app.kubernetes.io/instance: checkout-prod
cost-center: cc-042
owner-team: payments
environment: production
spec:
containers:
- name: app
image: example/checkout:1.2.3
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
这里有一个容易遗漏的细节:成本工具经常从 Pod 标签 读取归属,而不是只读取 Deployment 标签。Deployment 的标签和 Pod 模板标签必须同时维护,否则控制器对象看似有归属,实际运行的 Pod 可能无法被正确分摊。
3. 共享成本不能强行归给某个业务
以下资源往往属于共享成本:
- 控制面或托管控制平面;
- CoreDNS、网络插件、Ingress Controller;
- 日志、监控、追踪采集器;
- 节点操作系统和系统 DaemonSet;
- NAT Gateway、跨可用区流量、公共负载均衡器;
- 集群级别的安全、备份和镜像仓库服务。
可以采用两种方式:
- 单独保留共享成本池:报表中列为平台成本;
- 按规则分摊:例如按各团队 request 占比、Pod 数、节点使用量或收入比例分摊。
第二种方式必须记录公式。例如某共享成本池为 3000 元,团队 request 成本占比为 40%,则分摊金额为:
如果没有固定规则,不同月份可能使用不同口径,报表将失去可比性。
二、Request、利用率与闲置:便宜的资源不等于未使用的资源
1. Kubernetes 调度主要依据 Request
容器资源配置中:
requests表示调度器为该容器预留的资源需求;limits表示容器可使用的上限;- 没有设置 request 时,可能继承
LimitRange的默认值,也可能成为没有明确请求的容器; ResourceQuota可以限制命名空间总量,但不会自动让资源配置合理。
一个 Pod 的 CPU request 是其所有容器 CPU request 的总和;内存同理。一个节点能调度多少 Pod,取决于节点的 allocatable,而不是云主机标称的全部 CPU 和内存。
若节点有:
capacity:8 vCPU,32 GiB
系统和 kubelet 保留后 allocatable:7.5 vCPU,29 GiB
已调度 Pod requests:6 vCPU,20 GiB
那么调度视角下的剩余容量为:
CPU:1.5 vCPU
内存:9 GiB
即使操作系统监控显示当前只用了 3 vCPU,调度器也不会因此自动把更多需要 2 vCPU request 的 Pod 放入该节点。因为调度以 request 为主要约束,而不是实时利用率。
2. 闲置至少有三种含义
“闲置”不能只看 CPU 百分比,至少应区分:
资源申请闲置
Pod request 远高于实际使用量:
例如:
内存 request:4 GiB
P95 实际使用:1 GiB
则按 P95 计算的内存申请闲置率为:
这意味着调度容量被大量预留,但不代表可以直接把 request 改成 1 GiB。还必须检查 OOM、缓存突增、JVM 堆外内存、内核页缓存和故障切换后的流量。
节点装箱闲置
节点已购买,但由于 Pod request 碎片化或约束无法装满。例如总剩余 CPU 足够,但每个节点都没有单独容纳新 Pod 所需 request 的连续容量,这属于碎片,不是简单的总量不足。
业务闲置
应用本身没有有效流量或没有产生业务价值,例如:
- 已停止使用的预生产环境;
- 过期的临时环境;
- 已下线但 Deployment 仍保留;
- 低流量服务长期运行固定副本数;
- 批处理任务完成后留下的节点池或 PVC。
三者的处理方式不同。调低 request 解决的是第一类,重排节点或调整调度约束解决第二类,缩容或删除资源解决第三类。
3. 一个完整的 Request 优化算例
假设一个服务运行 10 个副本:
每个 Pod CPU request:1000m
每个 Pod 内存 request:2 GiB
实际 CPU P95:350m
实际内存 P99:1.1 GiB
原始申请量:
CPU:10 × 1000m = 10 vCPU
内存:10 × 2 GiB = 20 GiB
如果经过压测确认故障场景下单 Pod 的 CPU 不超过 600m、内存不超过 1.5 GiB,可以将配置调整为:
requests:
cpu: "600m"
memory: "1536Mi"
limits:
cpu: "1"
memory: "2Gi"
新的申请量:
CPU:10 × 600m = 6 vCPU
内存:10 × 1.5 GiB = 15 GiB
仅从调度容量看,释放了:
CPU:4 vCPU
内存:5 GiB
但这个结论成立需要满足几个条件:
- P95 或 P99 数据覆盖完整业务周期,而不是只覆盖低峰;
- 发生单实例故障后,剩余副本能够承接流量;
- CPU limit 触发节流不会破坏延迟目标;
- 内存峰值不会导致 OOMKilled;
- 滚动更新期间的额外副本仍可调度;
- HPA 的目标指标没有因为 request 改变而失真。
HPA 使用 CPU 利用率时,通常按“实际 CPU 使用量 / CPU request”计算相对利用率。request 从 1000m 调到 600m 后,同样使用 300m CPU,利用率会从 30% 变成 50%。因此调整 request 可能改变 HPA 扩缩容行为,而不是只改变成本。
4. limits 不是“越大越安全”
CPU limit 可能导致容器在节点有空闲时仍受到 CFS 限流;内存 limit 超出后可能触发 OOM。对于延迟敏感服务,CPU limit 的设置需要通过压测确认,而不是机械地设为 request 的固定倍数。
相反,完全不设置内存 limit 也有风险:单个失控容器可能消耗节点大量内存,最终触发节点级内存压力和驱逐。QoS Class、内存回收和驱逐行为会影响故障表现,因此成本优化不能脱离稳定性验证。
可以使用以下命令检查实际资源配置:
kubectl get pods -A \
-o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,CPU_REQ:.spec.containers[*].resources.requests.cpu,MEM_REQ:.spec.containers[*].resources.requests.memory'
这只能看到配置的 request,不能代表实际利用率。实际利用率通常来自 Metrics Server、Prometheus 或云厂商监控;kubectl top 依赖 Metrics API,且适合观察当前或近期数据,不适合作为长期成本核算的唯一数据源:
kubectl top pod -A
kubectl top node
生产环境应保存至少数周的 request、实际使用量、重启、OOM、驱逐、延迟和业务流量数据,再进行调整。
三、资源归属的实现:从标签到可审计的分摊
1. Namespace 是边界,不是完整的成本模型
Namespace 可以提供:
- 资源命名隔离;
ResourceQuota配额;LimitRange默认资源配置;- RBAC 授权范围;
- NetworkPolicy 等策略作用域。
但 Namespace 不天然对应团队或账单。一个团队可以使用多个 Namespace,一个 Namespace 也可能包含多个服务。更重要的是,Namespace 中的共享组件和跨命名空间资源需要额外处理。
可以为生产命名空间设置资源总额:
apiVersion: v1
kind: ResourceQuota
metadata:
name: retail-prod-quota
namespace: retail-prod
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
pods: "200"
这会拒绝超出配额的新对象,但不会自动缩小现有 Pod,也不会判断申请量是否合理。配额是治理边界,不是优化算法。
LimitRange 可以防止团队提交完全没有资源配置的容器:
apiVersion: v1
kind: LimitRange
metadata:
name: default-container-resources
namespace: retail-prod
spec:
limits:
- type: Container
defaultRequest:
cpu: "100m"
memory: "128Mi"
default:
cpu: "500m"
memory: "512Mi"
max:
cpu: "4"
memory: "8Gi"
默认值只是兜底。若默认值远高于大多数应用实际需求,会把“缺少配置”转化为系统性的容量浪费,因此应定期统计默认值的命中率和后续实际使用量。
2. 标签治理需要防止伪归属
只允许任意用户填写 cost-center,会产生以下问题:
- 团队名称拼写不一致;
- 业务删除后标签仍保留;
- 一个团队把成本标记到另一个成本中心;
- Deployment 有标签,Pod 模板没有;
- Job、CronJob、StatefulSet 使用了不同字段。
常见的控制手段是:
- 使用组织规定的标签键和值;
- 通过 Admission Webhook 或策略引擎拒绝缺少归属信息的对象;
- 将归属从 Namespace、Git 仓库或服务目录自动注入;
- 对标签值建立版本化字典;
- 把无法归属的资源单独列为“未分配成本”。
Webhook、Gatekeeper 或 Kyverno 等工具不是 Kubernetes 核心 API,具体能力和兼容性取决于实现。治理系统本身也会产生运行成本和误拦截风险,必须提供审计模式、豁免机制和恢复路径。
四、节点自动扩缩容与成本:缩容的前提是可驱逐、可重调度
节点自动扩缩容通常包含以下数据流:
flowchart LR
A[工作负载提交或负载变化] --> B[调度器]
B -->|无可用节点| C[Pending Pod]
C --> D[集群扩容组件]
D --> E[节点组或云 API]
E --> F[新节点加入集群]
F --> B
G[低利用率或空节点] --> D
D --> H[驱逐可迁移 Pod]
H --> I[节点排空]
I --> J[删除节点或缩小节点组]
关键点是:扩容通常由 Pending Pod 的 requests 和调度约束 触发,而不是由节点 CPU 利用率直接触发。缩容则需要证明节点上的 Pod 能被驱逐并在其他节点重新调度。
一个节点看起来只有 10% CPU 使用率,但如果上面的 Pod 具有以下任意约束,仍可能无法缩容:
PodDisruptionBudget不允许继续驱逐;local卷或节点本地数据不能迁移;- Pod 使用了不可重建的本地状态;
- 反亲和性导致其他节点放不下;
- 拓扑约束限制了目标区域;
- DaemonSet 或系统 Pod 处理逻辑不支持;
- Pod 的 request 过大,其他节点没有足够 allocatable;
- 使用了本地临时存储,驱逐会造成数据丢失。
因此,“删除一个节点能省一台机器费用”只有在下面条件成立时才成立:
这个条件不仅包含 CPU 和内存,还包含架构、区域、污点、卷拓扑、端口、GPU、拓扑分布和安全策略。
节点自动扩缩容组件并非 Kubernetes 核心统一实现。Cluster Autoscaler、云厂商托管扩缩容器、Karpenter 等工具在节点发现、实例选择、替换策略和缩容判定上存在差异。使用时应以对应项目和云厂商文档为准,不应假定某个组件的参数能迁移到另一个组件。
五、Spot:通过接受中断换取更低的计算价格
1. Spot 的经济模型
Spot、抢占式实例或可中断实例的共同特征是:云厂商可以在容量不足、价格策略变化或维护事件发生时回收实例。名称和回收通知机制因云厂商不同,但本质都是以较低价格换取较弱的供应稳定性。
如果普通实例每小时成本为 ,Spot 实例成本为 ,中断概率为 ,一次中断造成的恢复和业务损失期望成本为 ,则粗略期望成本为:
只有当:
时,Spot 才具有经济意义。
这里的 不仅是重新创建节点的费用,还包括:
- 请求失败和重试;
- 缓存重新预热;
- 批处理延迟;
- 数据恢复;
- 人工介入;
- SLO 违约;
- 多可用区流量或跨区域恢复成本。
因此,Spot 并不是“便宜节点池”,而是“把容量可靠性风险交给应用和调度系统处理”。
2. Spot 工作负载的必要条件
适合放到 Spot 上的工作负载通常具备:
- 可水平扩展;
- Pod 可以被重建;
- 状态保存在外部可靠存储;
- 能容忍短暂容量减少;
- 具有合理的超时、重试和幂等设计;
- 可以接受批处理延迟或吞吐波动。
不适合直接使用 Spot 的典型工作负载包括:
- 单副本且不可中断的核心服务;
- 将节点本地磁盘作为唯一数据副本的系统;
- 需要稳定低延迟的关键路径;
- 无法处理重复执行的任务;
- 依赖固定节点身份或本地缓存内容的应用。
3. 用污点和容忍度隔离 Spot 节点
可以给 Spot 节点添加污点,例如:
capacity-type=spot:NoSchedule
只有声明容忍度的 Pod 才能调度到这些节点:
spec:
tolerations:
- key: capacity-type
operator: Equal
value: spot
effect: NoSchedule
仅有 toleration 不代表 Pod 一定会去 Spot 节点,它只是允许调度。还需要节点标签和节点亲和性表达偏好或要求:
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: capacity-type
operator: In
values:
- spot
- weight: 20
preference:
matchExpressions:
- key: capacity-type
operator: In
values:
- on-demand
这里使用 preferredDuringSchedulingIgnoredDuringExecution,表示优先选择 Spot,但没有 Spot 时仍可选择按需节点。若使用 requiredDuringSchedulingIgnoredDuringExecution,Spot 容量不足时 Pod 会保持 Pending,成本可能反而增加,因为业务无法运行。
更稳妥的部署通常是混合容量:
按需节点:承载最低可用副本和系统组件
Spot 节点:承载可中断的额外副本、批任务和弹性吞吐
还必须设置多样化的实例类型、区域或节点组选择条件,避免所有副本集中在一个可能同时失效的容量池中。具体字段和标签名称由云厂商或节点管理工具定义,不能假定 capacity-type=spot 在所有环境中自动存在。
4. 中断处理不是 PDB 的替代品
云厂商通常提供实例回收通知,应用可以通过节点生命周期组件、守护进程或云厂商集成执行:
- 接收中断通知;
- 对节点执行
cordon,禁止新 Pod 调度; - 执行
drain,驱逐可迁移 Pod; - 等待替代 Pod 在其他节点启动;
- 最终删除节点。
PodDisruptionBudget 只约束某些自愿中断场景下的并发驱逐数量,例如节点排空;它不能阻止云厂商强制回收 Spot 节点,也不能保证 Pod 在中断前完成迁移。
例如:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: checkout-pdb
namespace: retail-prod
spec:
minAvailable: 2
selector:
matchLabels:
app.kubernetes.io/name: checkout
对于 3 个副本,minAvailable: 2 通常允许一次自愿中断一个副本。若所有 3 个副本都在同一个 Spot 节点上,节点被强制回收时仍可能同时丢失全部副本。PDB 必须结合副本数、拓扑分布、节点池混合和应用启动时间评估。
六、存储成本:PVC 不是唯一费用,容量也不是唯一指标
1. PersistentVolumeClaim 的生命周期和费用生命周期可能不同
PVC 是用户对存储的请求,PV 是实际绑定的持久卷,StorageClass 描述动态供应策略。一个常见流程是:
提交 PVC
↓
StorageClass 动态供应
↓
创建 PV 和云磁盘
↓
Pod 挂载 PVC
↓
删除 Pod
↓
PVC/PV 是否删除,取决于回收策略和工作流
查看 PVC、PV 和 StorageClass:
kubectl get pvc -A
kubectl get pv
kubectl get storageclass
kubectl describe pvc -n retail-prod checkout-data
需要特别检查:
- StorageClass 的
reclaimPolicy是Delete还是Retain; - 是否启用了
allowVolumeExpansion; - 卷是否绑定到特定拓扑区域;
- 卷类型的 IOPS、吞吐和快照费用;
- 删除 Namespace 后 PVC 是否按预期清理;
- 备份和快照是否有独立保留周期。
Delete 通常意味着删除 PV 时删除底层卷,但具体行为由 provisioner 实现;Retain 则通常保留底层卷,便于恢复,却容易形成孤儿资源。生产系统不能只根据 Kubernetes 对象是否存在判断云磁盘是否还在,应同时核对云平台资源。
2. 存储优化要同时看容量、性能和恢复要求
一个 1 TiB 卷即使只使用 100 GiB,也可能按 1 TiB 计费。可以考虑缩小卷,但缩容通常不如扩容简单,很多云盘和文件系统不支持原地缩容。安全流程一般是:
- 确认应用支持停机或在线迁移;
- 做可验证的备份;
- 创建目标容量的新卷;
- 复制数据并校验;
- 修改挂载关系;
- 观察读写错误和延迟;
- 在保留窗口后删除旧卷。
不要直接修改 PVC 期望通过把容量写小来实现缩容;Kubernetes 通常只支持扩容,具体能力还取决于 StorageClass、CSI 驱动和底层存储。
存储成本还包括:
- 快照数量和保留周期;
- 备份跨区域复制;
- 高 IOPS 或高吞吐配置;
- 多副本、纠删码或复制因子;
- 日志和临时文件;
- 对象存储请求与生命周期费用。
删除快照前应确认没有备份链依赖。某些云厂商的增量快照删除语义并不等同于简单删除一个独立文件,实际费用和恢复能力应以厂商实现为准。
3. StatefulSet 缩容不会自动删除所有数据
StatefulSet 的 Pod 和 PVC 生命周期需要分开理解。缩小副本数通常会减少 Pod,但历史 PVC 是否删除取决于 Kubernetes 版本、StatefulSet PVC 保留策略和具体配置;不能把缩容当成数据清理操作。
删除 StatefulSet、PVC 或 PV 前,应先回答:
数据是否仍需恢复?
是否有经过演练的备份?
是否还有其他 Pod 使用该卷?
底层云资源是否会被删除?
删除后是否能阻止自动重建?
成本治理不能以不可逆的数据删除作为默认手段。
七、网络成本:流量路径比流量总量更重要
Kubernetes 网络成本经常来自 Kubernetes 之外。常见收费路径包括:
Pod → 公网出口/NAT Gateway → 外部服务
Pod → 跨可用区节点 → Service 或数据库
集群 → 跨区域对象存储或备份
Ingress/LoadBalancer → 跨区域后端
日志采集 → 跨区域日志服务
同样是 1 TB 流量,经过不同路径可能产生完全不同的费用。尤其需要区分:
- 入站流量和出站流量;
- 同可用区和跨可用区流量;
- 同区域不同服务之间的流量;
- 公网出口、NAT、负载均衡和专线流量;
- 重试流量和业务有效流量。
例如,一个服务部署在区域 A,数据库主节点在区域 B。即使应用和数据库都在同一云区域,只要跨可用区传输按流量收费,就可能产生双向费用。将副本均匀分布到多个可用区有助于可用性,但也可能增加跨区访问费用。优化目标不是“永远避免跨区”,而是比较:
应用层还应避免无意义的放大流量:
- 大对象通过服务间同步而不是对象存储共享;
- 无限重试导致流量倍增;
- Sidecar 或代理重复传输日志;
- 将本可本地聚合的数据频繁发送到中心服务;
- 每个请求都跨区访问固定依赖。
网络优化必须结合延迟、故障域、数据合规和一致性要求。为了省跨区流量而把数据库和所有副本放进单一可用区,可能把可用性风险提高到不可接受的程度。
八、自动扩缩容如何影响成本和容量
1. HPA、VPA 和节点扩缩容解决不同问题
- HPA 调整工作负载副本数;
- VPA 调整 Pod 的资源建议或资源配置,VPA 不是 Kubernetes 核心内置控制器,通常作为独立项目安装;
- Cluster Autoscaler 或云厂商节点扩缩容器 调整节点数量;
- 节点选型工具 根据 Pod 约束选择实例类型和节点池。
HPA 的副本变化可能带来节点扩容,VPA 的 request 变化也可能让原本可装箱的 Pod 变成 Pending,继而触发节点扩容。几套控制器同时修改容量时,容易产生振荡:
利用率上升
→ HPA 增加副本
→ 节点扩容
→ 利用率下降
→ HPA 缩容
→ 节点缩容
→ 剩余 Pod 利用率再次升高
成本分析必须观察控制器动作的时间窗口,而不能只比较两个时刻的节点数。
2. Request 过大和过小都会增加成本
设节点总可分配资源为 ,所有 Pod request 总和为 ,粗略装箱利用率为:
当 过大时,节点需要提前扩容,产生申请闲置;当 过小时,调度器会把过多 Pod 放入节点,可能产生 CPU 争抢、内存压力和延迟恶化。
更可靠的优化目标不是最大化 ,而是在满足故障余量和性能约束的前提下最小化节点数量:
约束包括:
这也是为什么“把节点 CPU 使用率提升到 90%”不能作为通用目标。高利用率可能降低成本,但会减少故障余量、增加调度碎片和扩容响应压力。
九、优化边界:什么时候不应继续压低成本
成本优化存在明确边界,至少包括以下几类。
1. 可用性边界
如果服务要求跨可用区部署、保留故障余量或维持最低副本数,就不能把所有节点压缩到单个故障域。节省的节点费用必须与故障损失比较,而不是单看月账单。
2. 性能边界
CPU request 降低后,容器可能更容易被调度,但 CPU 争抢和 limit 节流可能使尾延迟上升。内存 request 降低后,节点可能因内存压力驱逐 Pod。优化前后应同时比较吞吐、错误率、P95/P99 延迟、OOM 和重启次数。
3. 数据边界
删除 PVC、快照、备份或降低副本数会影响恢复点目标(RPO)和恢复时间目标(RTO)。如果恢复演练没有成功,账面上省下的存储费用不能视为真实节省。
4. 运维边界
过度分散节点类型、Spot 池和调度规则会增加故障排查复杂度。一个每月节省少量费用、但需要大量人工处理 Pending Pod 和中断事件的方案,可能没有正向收益。
5. 组织边界
把共享成本全部摊给业务团队,会造成错误的优化激励;把共享成本全部留给平台团队,又可能让业务看不到资源浪费。成本模型应能区分:
可控成本:团队通过代码和配置可以改变
共享成本:多个团队共同产生
不可控成本:合同、控制面或最低购买承诺决定
十、一个可执行的治理闭环
成本治理应当形成“测量—归属—验证—变更—回滚”的闭环,而不是一次性清理。
第一步:建立资源和账单的对应关系
至少收集:
集群、节点池、节点类型和区域
Namespace、Pod、工作负载及归属标签
CPU/内存 request 与实际利用率
节点 allocatable 和系统开销
PVC、PV、StorageClass、快照和备份
网络出入方向、可用区和公网出口
HPA、PDB、拓扑约束和调度失败事件
云账单按节点池、磁盘、流量和服务分类,Kubernetes 指标按 Pod 和 Namespace 分类。两者需要通过节点标签、节点池元数据、时间窗口和分摊规则进行关联。
第二步:先找“无争议浪费”
优先检查:
kubectl get deploy,statefulset,daemonset,job,cronjob -A
kubectl get pods -A --field-selector=status.phase=Pending
kubectl get pvc -A
kubectl get nodes --show-labels
kubectl get events -A --sort-by=.lastTimestamp
适合优先处理的对象包括:
- 已无业务流量的临时环境;
- 完成但未清理的 Job;
- 长期未使用的 PVC 和快照;
- 明确过大的 request;
- 空载但无法缩容的节点;
- 失败重试造成的网络和计算放大。
“无争议”意味着删除或调整前能确认所有者、保留期限和恢复路径。
第三步:用变更前后的证据验证
每次调整都应记录:
变更对象和提交版本
变更前 request、节点数和账单基线
变更后的利用率、延迟、错误率和重启
HPA/扩缩容事件
Spot 中断和 Pod 重调度结果
PVC、快照和网络费用变化
例如将内存 request 从 2 GiB 调到 1.5 GiB 后,不能只看到节点数下降,还要观察至少一个完整业务高峰周期,并确认:
- 没有新增 OOMKilled;
- 没有出现内存压力驱逐;
- P99 延迟未越过目标;
- 滚动更新和节点故障演练仍能完成;
- HPA 未出现异常振荡。
第四步:保留恢复路径
恢复路径包括:
- 保留旧 Deployment 版本和资源配置;
- 通过 Git 管理 YAML,避免直接修改后无法追溯;
- 对 PVC 和云磁盘建立删除保护窗口;
- 对 Spot 工作负载保留按需容量;
- 对节点缩容设置可观察的排空过程;
- 对策略拦截保留审计和豁免机制。
成本治理的成功标准不是某一天节点数最少,而是在满足业务约束的前提下,让每一单位付费资源都能被解释、归属、验证,并且在风险出现时可以恢复。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Kubernetes 容量规划:Request、利用率、碎片、故障余量和压测
- 下一篇:Kubernetes 工作负载排障:Pending、CrashLoop、ImagePull、OOM 和 Probe
- 延伸:Kubernetes 节点自动扩缩容:Pending Pod、Node Group、缩容和成本
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论