一、问题引入:ACK集群节点资源分配不均的常见表现与影响

很多用阿里云容器服务ACK(Alibaba Cloud Container Service for Kubernetes)做业务部署的开发者,都会遇到一个头疼的问题:集群里的节点资源分配得乱七八糟。有的节点CPU、内存被占得满满当当,连新的Pod都调度不上去;有的节点却空空荡荡,大部分资源都闲置着。这种不均不仅会浪费云服务器的成本,还会让业务的稳定性大打折扣——资源满负载的节点容易出现Pod被驱逐、服务响应慢甚至崩溃的情况,而闲置节点的资源又完全没用到。

举个实际的场景:某电商公司的ACK集群里,部署了核心的订单服务、支付服务和非核心的日志采集服务。默认情况下,K8s的调度器是按照“尽量平均”的原则分配Pod,但订单服务的Pod本身CPU占用高,日志服务的Pod数量多。运行一段时间后,发现大部分订单服务的Pod都挤在了前10台节点上,这些节点的CPU使用率都超过了90%,而后面20台节点的CPU使用率只有20%左右。每到大促的时候,前10台节点就会频繁出现Pod被驱逐的情况,导致订单处理中断,而闲置节点却帮不上忙。

1.1 资源分配不均的核心原因

默认的K8s调度策略,只考虑节点的整体资源剩余,不会区分业务的类型、Pod的重要性,也不会考虑节点的硬件配置差异。比如有的节点是高CPU的计算型实例,有的是高内存的内存型实例,如果把高内存需求的业务调度到高CPU节点上,就会导致CPU闲置、内存不够的情况。另外,很多开发者不会主动配置调度规则,都是让调度器“自由发挥”,时间一长,资源分配的偏差就会越来越大。

二、核心优化思路:节点亲和性与Pod拓扑分布

要解决ACK集群的资源分配不均问题,核心就是给K8s的调度器加“规则”,让它按照我们的需求来分配Pod。最常用的两个规则就是节点亲和性和Pod拓扑分布约束,这两个规则配合使用,就能实现资源的精准分配。

2.1 节点亲和性:让Pod“找对节点”

节点亲和性的作用,就是给Pod指定“喜欢”或者“不喜欢”的节点。比如我们可以让高CPU的业务Pod只调度到高CPU的节点上,让高内存的业务Pod只调度到高内存的节点上,这样就能避免不同类型的业务抢资源,也能充分利用不同节点的硬件优势。

节点亲和性分为两种:硬亲和性和软亲和性。硬亲和性是必须满足的规则,不满足就调度不上去;软亲和性是尽量满足的规则,没有合适的节点也能调度。

示例:节点亲和性配置

技术栈:Kubernetes YAML(ACK集群兼容标准K8s配置) 我们先给节点打标签,给高CPU的节点打instance-type=ecs.g7.8xlarge(阿里云通用计算型实例)的标签,给高内存的节点打instance-type=ecs.r7.8xlarge(阿里云内存型实例)的标签。然后给订单服务的Pod配置硬亲和性,让它只调度到高CPU节点上;给日志服务的Pod配置软亲和性,尽量调度到高内存节点上。

# 先给节点打标签的命令,需要先获取节点名称
# 给高CPU节点打标签
kubectl label nodes <高CPU节点名称> instance-type=ecs.g7.8xlarge
# 给高内存节点打标签
kubectl label nodes <高内存节点名称> instance-type=ecs.r7.8xlarge

# 订单服务的Deployment配置(硬亲和性)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 5
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
      - name: order-container
        image: registry.cn-hangzhou.aliyuncs.com/my-images/order:v1.0
        resources:
          requests:
            cpu: "4"
            memory: "8Gi"
          limits:
            cpu: "8"
            memory: "16Gi"
      # 节点亲和性配置
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution: # 硬亲和性
            nodeSelectorTerms:
            - matchExpressions:
              - key: instance-type
                operator: In # 匹配标签值在指定列表内
                values: ["ecs.g7.8xlarge"] # 只调度到高CPU节点

# 日志服务的Deployment配置(软亲和性)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: log-service
spec:
  replicas: 10
  selector:
    matchLabels:
      app: log-service
  template:
    metadata:
      labels:
        app: log-service
    spec:
      containers:
      - name: log-container
        image: registry.cn-hangzhou.aliyuncs.com/my-images/log:v1.0
        resources:
          requests:
            cpu: "1"
            memory: "4Gi"
          limits:
            cpu: "2"
            memory: "8Gi"
      # 节点亲和性配置
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution: # 软亲和性
          - weight: 100 # 权重越高越优先满足
            preference:
              matchExpressions:
              - key: instance-type
                operator: In
                values: ["ecs.r7.8xlarge"] # 尽量调度到高内存节点

2.2 Pod拓扑分布约束:让Pod“均匀分布”

节点亲和性解决了“找对节点”的问题,但同一个类型的节点上,可能还会出现Pod扎堆的情况。比如高CPU节点有10台,订单服务的5个Pod可能都调度到了其中2台节点上,导致这2台节点资源满负载,其他8台闲置。这时候就需要用到Pod拓扑分布约束,让同类型的Pod均匀分布在不同的节点、可用区甚至地域上。

Pod拓扑分布约束的核心是设置“最大允许偏差”,也就是同一个拓扑域(比如节点、可用区)上的Pod数量,最多比平均数量多几个。比如我们设置maxSkew: 1,就是要求每个节点上的同类型Pod数量,最多只比最少的那个节点多1个。

示例:Pod拓扑分布约束配置

技术栈:Kubernetes YAML(ACK集群兼容标准K8s配置) 还是用刚才的订单服务和日志服务,给它们加上拓扑分布约束,让同服务的Pod均匀分布在不同的节点上。

# 订单服务的Deployment配置(增加拓扑分布约束)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 5
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
      - name: order-container
        image: registry.cn-hangzhou.aliyuncs.com/my-images/order:v1.0
        resources:
          requests:
            cpu: "4"
            memory: "8Gi"
          limits:
            cpu: "8"
            memory: "16Gi"
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: instance-type
                operator: In
                values: ["ecs.g7.8xlarge"]
      # Pod拓扑分布约束
      topologySpreadConstraints:
      - maxSkew: 1 # 同一个拓扑域上的Pod数量偏差不超过1
        topologyKey: kubernetes.io/hostname # 拓扑域是节点(按节点名称划分)
        whenUnsatisfiable: DoNotSchedule # 不满足规则就不调度,避免扎堆
        labelSelector:
          matchLabels:
            app: order-service # 约束的是标签为app=order-service的Pod

# 日志服务的Deployment配置(增加拓扑分布约束)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: log-service
spec:
  replicas: 10
  selector:
    matchLabels:
      app: log-service
  template:
    metadata:
      labels:
        app: log-service
    spec:
      containers:
      - name: log-container
        image: registry.cn-hangzhou.aliyuncs.com/my-images/log:v1.0
        resources:
          requests:
            cpu: "1"
            memory: "4Gi"
          limits:
            cpu: "2"
            memory: "8Gi"
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            preference:
              matchExpressions:
              - key: instance-type
                operator: In
                values: ["ecs.r7.8xlarge"]
      # Pod拓扑分布约束(按可用区划分)
      topologySpreadConstraints:
      - maxSkew: 2 # 同一个可用区上的Pod数量偏差不超过2
        topologyKey: topology.kubernetes.io/zone # 拓扑域是可用区
        whenUnsatisfiable: ScheduleAnyway # 尽量满足,不满足也调度
        labelSelector:
          matchLabels:
            app: log-service

三、应用场景、优缺点与注意事项

3.1 应用场景

节点亲和性和Pod拓扑分布约束的组合,适用于几乎所有需要优化ACK集群资源分配的场景:

  1. 业务分层部署:核心业务(如订单、支付)部署在高性能节点,非核心业务(如日志、监控)部署在普通节点;
  2. 硬件资源匹配:高CPU需求的业务部署在计算型节点,高内存需求的业务部署在内存型节点;
  3. 高可用部署:让业务Pod均匀分布在不同的可用区,避免单个可用区故障导致业务中断;
  4. 成本优化:通过合理分配资源,减少闲置节点的数量,降低云服务器的成本。

3.2 技术优缺点

节点亲和性的优缺点

优点:

  1. 配置简单,容易理解,只需要给节点打标签、给Pod配置规则即可;
  2. 可以灵活控制Pod的调度范围,适合不同类型业务的隔离部署;
  3. 硬亲和性可以保证业务的部署环境符合要求,软亲和性可以兼顾调度的灵活性。 缺点:
  4. 硬亲和性如果配置不当,会导致Pod调度失败(比如没有符合标签的节点);
  5. 软亲和性的权重如果设置不合理,可能会导致调度结果不符合预期;
  6. 只控制Pod的调度范围,不控制范围内的分布,容易出现同类型Pod扎堆的情况。

Pod拓扑分布约束的优缺点

优点:

  1. 可以保证同类型Pod的均匀分布,避免资源分配不均;
  2. 可以设置不同的拓扑域(节点、可用区、地域),满足不同的高可用需求;
  3. 可以通过whenUnsatisfiable参数控制调度策略,兼顾灵活性和可用性。 缺点:
  4. 配置相对复杂,需要理解拓扑域、最大偏差等概念;
  5. 如果maxSkew设置得过小,会导致Pod调度失败;
  6. 对于副本数较少的Pod,拓扑分布约束的效果不明显。

3.3 注意事项

  1. 节点标签的命名要规范,尽量使用有意义的名称,避免混淆;
  2. 硬亲和性配置后,要及时检查集群中是否有符合标签的节点,避免Pod无法调度;
  3. maxSkew的设置要根据副本数和节点数量来调整,一般设置为1或2即可;
  4. 拓扑分布约束的topologyKey要根据实际需求选择,比如要实现可用级别的高可用,就选择topology.kubernetes.io/zone
  5. 配置规则后,要及时验证调度结果,比如用kubectl get pod -o wide命令查看Pod的调度情况,确保符合预期;
  6. 对于重要的业务,建议同时使用节点亲和性和Pod拓扑分布约束,既保证部署环境符合要求,又保证资源的均匀分配。

四、文章总结

ACK集群节点资源分配不均是很多开发者都会遇到的问题,默认的调度策略往往无法满足复杂的业务需求。通过节点亲和性和Pod拓扑分布约束的组合使用,可以实现Pod的精准调度,让不同类型的业务部署在合适的节点上,同类型的业务均匀分布在不同的节点或可用区上,从而解决资源分配不均的问题,提高集群的稳定性和资源利用率。

在实际使用中,开发者可以根据自己的业务需求,灵活调整配置规则,比如给不同的业务设置不同的亲和性规则、调整maxSkew的大小、选择不同的拓扑域等。同时,要注意配置的验证和监控,及时调整规则,确保集群的资源分配始终处于合理的状态。