4. 容器化成本基础:Docker与Kubernetes资源模型

在云原生环境中,成本控制的第一道防线并非事后账单分析,而是资源申明。Docker 与 Kubernetes 的资源模型定义了容器如何“请求”和“限制”计算资源,直接决定了调度决策、节点密度以及最终的账单金额。理解这一模型,是 FinOps 从“被动看账”转向“主动控费”的起点。

4.1 Docker 资源约束:容器的“沙箱配额”

Docker 通过 Linux cgroups 对容器进行资源隔离。在 docker run 或 Compose 文件中,可以设置 CPU 和内存的软硬限制:

参数 类型 含义 成本影响
--memory 硬限制 容器最多使用的内存量,超过则 OOM Kill 防止单容器内存泄漏撑爆节点,避免节点扩容成本
--memory-reservation 软限制 当宿主机内存竞争时,优先回收至此值以下 允许超卖,提升节点装箱率,降低单位 Pod 成本
--cpus 硬限制 容器可使用的 CPU 核心数(如 1.5) 限制 CPU 突发,避免干扰同节点其他容器
--cpu-shares 软权重 CPU 时间片分配权重(默认 1024) 空闲时可用全部 CPU,竞争时按比例分配
FinOps 提示: 生产环境务必同时设置 --memory 和 --memory-reservation。仅设置硬限制会导致节点内存碎片化;仅设置软限制则无法阻止单容器失控。两者配合可在“稳定性”与“装箱率”之间取得平衡。

4.2 Kubernetes 资源模型:Requests 与 Limits

Kubernetes 将 Docker 的底层约束抽象为两个核心字段——requests 和 limits,分别对应“调度保证”和“运行时上限”。

apiVersion: v1
kind: Pod
metadata:
  name: finops-demo
spec:
  containers:
  - name: app
    image: my-app:1.0
    resources:
      requests:
        memory: "256Mi"
        cpu: "500m"        # 0.5 核
      limits:
        memory: "512Mi"
        cpu: "1000m"       # 1 核

4.2.1 Requests:调度决策的“承诺”

  • 调度依据: kube-scheduler 只检查 requests,确保节点剩余资源 ≥ Pod 的 requests 总和。
  • 成本绑定: 云厂商(如 EKS、AKS、GKE)的节点计费按实际运行时长,但内部成本分摊通常按 requests 比例计算。一个 Pod 即使空闲,也会按 requests 值占用成本。
  • 超卖风险: 若所有 Pod 的 requests 总和超过节点容量,但实际使用量较低,节点仍可正常运行;一旦突发,则触发 CPU 节流或 OOM。

4.2.2 Limits:运行时“防爆墙”

  • CPU Limits: 通过 CFS 配额控制,超过时强制节流(throttle),不会 OOM,但延迟会飙升。
  • Memory Limits: 硬限制,超过即触发 OOM Kill,容器被重启。
  • 成本陷阱: 设置过高的 limits 而不调整 requests,会导致节点资源碎片化,降低装箱效率。
常见误区: 很多团队将 requests 和 limits 设为相同值(即“Guaranteed” QoS 类)。虽然稳定性最高,但会浪费大量资源。FinOps 最佳实践是:requests 设为 P50 或 P75 分位值,limits 设为 P99 或 P99.9 分位值,允许适度突发。

4.3 CPU 与内存的计价方式

在公有云 Kubernetes 环境中,CPU 和内存的计价并非直接按“Pod 规格 × 单价”,而是通过节点成本分摊间接计算。以下是三种主流计价模型:

计价模型 计算方式 适用场景 FinOps 关注点
节点级分摊 节点总成本 ÷ 节点可分配资源 × Pod requests 混合负载、节点共享 需排除系统组件(kube-system)的资源占用
预留实例/节省计划 预付折扣价 + 按需价 × 超出部分 稳态工作负载 预留覆盖率 ≥ 80% 时成本最优
Spot 实例 按实时竞价,通常为按需价的 60%~90% 折扣 无状态、容错、批处理 需配合 PodDisruptionBudget 和节点中断处理

4.3.1 单位成本换算示例

假设一个 m6i.large 节点(2 vCPU, 8 GiB)按需价格为 $0.096/小时,可分配资源扣除系统开销后约为 1.8 vCPU 和 7 GiB 内存:

  • CPU 单价: $0.096 ÷ 1.8 = $0.0533 / vCPU·小时
  • 内存单价: $0.096 ÷ 7 = $0.0137 / GiB·小时
  • Pod 成本: 若 Pod requests 为 0.5 vCPU + 1 GiB,则每小时成本 = 0.5×0.0533 + 1×0.0137 = $0.0404
关键公式: Pod 成本 = (CPU requests × CPU 单价) + (Memory requests × 内存单价)。
注意:limits 不直接参与成本计算,但过高的 limits 会降低节点可调度 Pod 数量,间接推高单 Pod 成本。

4.4 资源模型与成本控制的关系图

下图展示了从 Docker 容器约束到 Kubernetes 资源申明,再到云厂商计费模型的完整链路:

容器化资源模型与成本链路 容器化资源模型与成本控制链路 Docker 约束 --memory / --cpus cgroups 硬限制 + 软权重 K8s 资源模型 requests / limits 调度保证 + 运行时上限 计价方式 节点分摊 / 预留 / Spot 按 requests 比例计费 成本控制关键点 requests 设置过高 → 浪费预留资源 | requests 设置过低 → 调度失败或性能抖动 垂直压测 通过压测确定 P50/P99 资源使用基线 动态调整 VPA / HPA 自动调整 requests 与副本数 成本可视化 Kubecost / OpenCost 按 namespace/team 分摊 核心原则:requests 决定调度与成本,limits 决定稳定性与突发能力

4.5 资源模型对比:QoS 类与成本权衡

Kubernetes 根据 requests 和 limits 的配置关系,将 Pod 分为三种 QoS 类,直接影响调度优先级和成本效率:

QoS 类 配置条件 调度保证 成本效率 推荐场景
Guaranteed requests == limits(CPU 和内存均相等) 最高,不会被 OOM 或驱逐 低(资源预留多,装箱率低) 关键数据库、有状态中间件
Burstable requests < limits(至少一个资源不等) 中等,资源充足时可突发 高(允许超卖,装箱率高) 微服务、Web 应用、批处理
BestEffort 未设置 requests 和 limits 最低,资源不足时优先被驱逐 极高(完全超卖) 非生产、可中断任务
FinOps 建议: 生产集群中,80% 以上的 Pod 应配置为 Burstable。Guaranteed 仅用于延迟敏感型服务;BestEffort 仅用于开发/测试环境或 Spot 实例上的批处理作业。通过合理配置 QoS 类,可在不牺牲稳定性的前提下将集群装箱率提升 30%~50%。

4.6 资源请求与限制的常见陷阱

  1. 陷阱一:requests 设置过高 —— 导致节点资源浪费,单 Pod 成本虚高。例如将 requests 设为 2 vCPU 但实际使用仅 0.3 vCPU,成本被放大 6 倍。
  2. 陷阱二:limits 设置过低 —— 导致 CPU 频繁节流,应用延迟增加,进而触发 HPA 扩容,产生额外 Pod 成本。
  3. 陷阱三:忽略内存的不可压缩性 —— CPU 可以节流,内存超过 limits 直接 OOM。内存 requests 应基于实际使用量 + 安全缓冲(通常 20%~30%)。
  4. 陷阱四:所有容器使用相同资源规格 —— 不同微服务的资源特征差异巨大,应逐服务进行压测和调优。
成本事故案例: 某团队将所有微服务的 requests 设置为 1 vCPU / 2 GiB,limits 设置为 2 vCPU / 4 GiB。实际压测发现 70% 的服务 CPU 使用率低于 0.2 vCPU。调整后,集群节点数从 50 台降至 32 台,月度成本下降 36%。
教训: 没有经过压测的资源申明,本质上是“拍脑袋”的成本浪费。

4.7 本章小结

容器化成本的基础在于理解资源申明(requests/limits)与计价模型之间的映射关系。Docker 提供底层约束能力,Kubernetes 将其抽象为调度和运行时策略,而云厂商的计费最终落在节点级分摊上。FinOps 工程师的核心任务,就是通过持续压测、动态调整和成本可视化,找到每个工作负载的“资源甜点”——既满足性能 SLO,又最大化节点装箱率,从而降低单位业务成本。