正在加载,请稍候…

Kubernetes 资源优化:VPA/HPA 配置、资源请求与限制、节点亲和性与成本优化

优化 Kubernetes 资源使用以降低云成本并提升性能:配置 HPA 和 VPA 实现动态扩缩容,设置准确的资源请求与限制,利用节点亲和性和竞价实例,并实施

Kubernetes 资源优化:VPA/HPA 配置、资源请求与限制、节点亲和性与成本优化

Kubernetes 资源优化:VPA/HPA 配置、资源请求与限制、节点亲和性与成本优化

Kubernetes 集群因过度配置资源和意外云账单而臭名昭著。研究表明,平均每个 Kubernetes 集群浪费了 50-70% 的分配计算资源。本指南涵盖了从正确的请求和限制到动态自动缩放和智能节点放置的系统性资源优化。

资源请求与限制

Kubernetes 资源优化:VPA/HPA 配置、资源请求与限制、节点亲和性与成本优化 插图

理解区别

  • Request(请求):Kubernetes 为调度预留的量;保证可用
  • Limit(限制):容器可以使用的最大值;超过 CPU 会被节流,超过内存会导致 OOM 杀死
resources:
  requests:
    memory: "256Mi"
    cpu: "250m"    # 0.25 vCPU
  limits:
    memory: "512Mi"
    cpu: "1000m"   # 1 vCPU

找到合适的值

从观察实际使用情况开始:

# 命名空间中所有 Pod 的当前使用情况
kubectl top pods -n production --sort-by=cpu

# 随时间变化的资源使用情况(需要 metrics-server)
kubectl top pods -n production --containers

# 通过 PromQL 查看历史使用情况
# 容器 7 天内的平均 CPU 使用率
avg_over_time(
  rate(container_cpu_usage_seconds_total{
    container="api",
    namespace="production"
  }[5m])[7d:]
)

# P95 内存使用量
quantile_over_time(0.95,
  container_memory_working_set_bytes{
    container="api",
    namespace="production"
  }[7d]
)

根据历史数据设置请求

# 经验法则:
# CPU 请求 = P50 使用量 * 1.2
# 内存请求 = P95 使用量 * 1.1
# CPU 限制 = P99 使用量 * 1.5(或移除 CPU 限制以避免节流)
# 内存限制 = P99 使用量 * 1.5(OOM 风险与浪费之间的权衡)

CPU 节流问题

将 CPU 限制设置得太低会导致即使节点有空闲资源也会发生节流:

# 检查 CPU 节流率
kubectl exec -n production deploy/api -- cat /sys/fs/cgroup/cpu/cpu.stat

# PromQL:节流率
rate(container_cpu_cfs_throttled_seconds_total{container="api"}[5m])
/ rate(container_cpu_cfs_periods_total{container="api"}[5m])

许多团队对延迟敏感的工作负载完全移除 CPU 限制,依靠 HPA 进行扩展而不是节流。

水平 Pod 自动缩放器(HPA)

HPA 根据指标调整 Pod 副本的数量。

基于 CPU 的 HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60   # 目标 CPU 利用率 60%
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Pods
          value: 4
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300   # 等待 5 分钟再缩容
      policies:
        - type: Percent
          value: 10
          periodSeconds: 60

Kubernetes 资源优化:VPA/HPA 配置、资源请求与限制、节点亲和性与成本优化 插图

自定义指标 HPA

根据请求率、队列深度或任何 Prometheus 指标进行缩放:

metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "1000"   # 每个 Pod 目标 1000 RPS

  - type: External
    external:
      metric:
        name: sqs_queue_depth
        selector:
          matchLabels:
            queue: order-processing
      target:
        type: AverageValue
        averageValue: "50"    # 每个 Pod 50 条消息

安装 Prometheus Adapter 以将 Prometheus 指标暴露给 HPA:

# prometheus-adapter values.yaml
rules:
  custom:
    - seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
      resources:
        overrides:
          namespace: { resource: "namespace" }
          pod: { resource: "pod" }
      name:
        matches: "^(.*)_total
quot; as: "${1}_per_second" metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'

垂直 Pod 自动缩放器(VPA)

VPA 根据观察到的使用情况自动调整资源请求。

# 安装 VPA
kubectl apply -f https://github.com/kubernetes/autoscaler/releases/latest/download/vertical-pod-autoscaler.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: api-vpa
  namespace: production
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  updatePolicy:
    updateMode: "Auto"   # Auto, Recreate, Initial, 或 Off
  resourcePolicy:
    containerPolicies:
      - containerName: api
        minAllowed:
          cpu: 100m
          memory: 128Mi
        maxAllowed:
          cpu: 4
          memory: 4Gi
        controlledResources: ["cpu", "memory"]
        controlledValues: RequestsAndLimits

重要:VPA 和 HPA 不能同时管理 CPU/内存。使用 VPA 进行应用程序的资源大小调整,使用 HPA 进行副本数量缩放。对于需要两者的工作负载,将 VPA 设置为仅推荐模式(updateMode: "Off")并手动应用推荐。

节点亲和性与拓扑

节点亲和性

spec:
  affinity:
    nodeAffinity:
      # 硬性要求:必须在此节点类型上运行
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: node.kubernetes.io/instance-type
                operator: In
                values:
                  - m5.xlarge
                  - m5.2xlarge

      # 软偏好:优先选择 us-east-1a 区域的节点
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          preference:
            matchExpressions:
              - key: topology.kubernetes.io/zone
                operator: In
                values:
                  - us-east-1a

Pod 反亲和性实现高可用

spec:
  affinity:
    podAntiAffinity:
      # 硬性:两个副本不能在同一节点上
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchLabels:
              app: api
          topologyKey: kubernetes.io/hostname

      # 软性:优先跨可用区分布
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 50
          podAffinityTerm:
            labelSelector:
              matchLabels:
                app: api
            topologyKey: topology.kubernetes.io/zone

拓扑分布约束(较新方法)

spec:
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: topology.kubernetes.io/zone
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels:
          app: api
    - maxSkew: 1
      topologyKey: kubernetes.io/hostname
      whenUnsatisfiable: ScheduleAnyway
      labelSelector:
        matchLabels:
          app: api

Kubernetes 资源优化:VPA/HPA 配置、资源请求与限制、节点亲和性与成本优化 插图

竞价/可抢占实例以降低成本

在竞价实例上运行无状态工作负载可节省 60-90% 的费用:

# Karpenter NodePool 用于竞价实例
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: spot-workers
spec:
  template:
    metadata:
      labels:
        capacity-type: spot
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot"]
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: node.kubernetes.io/instance-type
          operator: In
          values: ["m5.xlarge", "m5.2xlarge", "m4.xlarge", "m4.2xlarge"]
      nodeClassRef:
        apiVersion: karpenter.k8s.aws/v1
        kind: EC2NodeClass
        name: default
  disruption:
    consolidationPolicy: WhenUnderutilized
    consolidateAfter: 30s

优雅处理竞价实例终止

# 处理 2 分钟终止通知
spec:
  terminationGracePeriodSeconds: 120
  containers:
    - name: api
      lifecycle:
        preStop:
          exec:
            command: ["/bin/sh", "-c", "sleep 10"]  # 排空连接

资源配额和 LimitRange

每个命名空间的 ResourceQuota

apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    requests.cpu: "100"
    requests.memory: "200Gi"
    limits.cpu: "200"
    limits.memory: "400Gi"
    pods: "200"
    services.loadbalancers: "5"
    persistentvolumeclaims: "20"

默认请求的 LimitRange

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: production
spec:
  limits:
    - type: Container
      default:          # 如果未指定,默认限制
        cpu: 500m
        memory: 256Mi
      defaultRequest:   # 如果未指定,默认请求
        cpu: 100m
        memory: 128Mi
      max:              # 允许的最大值
        cpu: 4
        memory: 8Gi
      min:              # 要求的最小值
        cpu: 50m
        memory: 64Mi

使用 Kubecost 进行成本监控

helm install kubecost cost-analyzer \
  --repo https://kubecost.github.io/cost-analyzer/ \
  --namespace kubecost \
  --create-namespace \
  --set kubecostToken="your-token"

关键 Kubecost 查询:

  • 每个命名空间的成本
  • 每个团队标签的成本
  • 空闲/浪费的资源
  • 节省建议

Cluster Autoscaler 与 Karpenter 对比

特性 Cluster Autoscaler Karpenter
缩放触发 节点组利用率 待处理 Pod
新节点速度 2-5 分钟 30-60 秒
实例选择 预定义节点组 动态装箱
竞价支持 通过节点组 原生支持

对于新集群,推荐使用 Karpenter,因为它具有更快的缩放和更好的装箱能力。

结论

Kubernetes 资源优化是一个持续的过程。从测量实际使用情况和设置准确的请求开始。添加 HPA 以处理流量峰值而无需过度配置。使用 VPA 建议来调整容器大小。通过拓扑约束跨可用区分布工作负载。在竞价实例上运行无状态工作负载以大幅节省成本。通过系统优化,团队通常可以将 Kubernetes 成本降低 40-60%,同时不牺牲可靠性。