Kubernetes Autoscaler

电商项目流量波峰波谷现象是非常常见的, 在闲时服务器资源利用率可能只有10%左右, 但是活动期间利用率会瞬间飙升导致集群压力变大甚至宕机, 传统的应对方案就是增加集群的硬件资源, 但这也会增加不少费用开销

本篇文章中用几段简单的k8s配置来展示公司的k8s集群是如何应对这种情况的, 在这之前我们需要了解几个k8s的概念

Pod是k8s中的工作单元, 可以理解为一个Pod就是一个应用实例, 对于同一个项目往往会在集群里部署多个Pod以提高负载性能

Deployment是控制Pod的一种控制器, 主要作用是保持Pod的数量在预期数量中, 比如以下的配置

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx 
          imagePullPolicy: IfNotPresent
          resources:
            limits:
              cpu: "1"
              memory: "1024Mi"
      restartPolicy: Always
  selector:
    matchLabels:
      app: nginx

重点关注replicas字段, 配置声明了期望有3个nginx实例在集群中运行, Deployment会尽力满足这个需求

HorizontalPodAutoscaler简称HPA, 是k8s的一种横向扩容机制, 通常作用在Deployment上

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx
spec:
  maxReplicas: 6
  minReplicas: 3
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50

scaleTargetRef字段指示了HPA要作用在哪个对象上, HPA会监控这个对象的某些指标, 在指标达到某个阈值后k8s会自动扩容或缩容这个对象, metrics字段指示了需要监控的指标, 上例的配置监控的指标CPU使用率, 阈值是50%, 这里的CPU使用率指的是Deployment配置中的

resources:
  limits:
    cpu: "1"
    memory: "1024Mi"

上例的配置限制了容器最多使用1个核的CPU以及1024Mi(1Gi)的内存, 所以HPA的触发条件是这个Deployment的Pods的CPU平均使用率高于了容器限制的50%

用通俗点的话描述这个HPA, nginx这个Deployment最少要有3个Pod在运行, 当这三个Pod的CPU平均使用率超过50%, HPA就会对这个Deployment进行扩容, 即是增加Pod数量, 最多增加到6个Pod

当然, 在这几个Pod的CPU平均使用率下降之后, HPA会自动缩容把Pod的数量控制在minReplicas字段限制的数量

有了HPA问题似乎就解决了, 当流量上升的时候, 原有的Pod的CPU使用率会上升, 上升到50%的时候HPA会自动创建Pod来负载均衡, 整体负载能力就提高了

但当流量无限上升的时候, HPA难免还是会遇到集群资源不够的问题, 按照上面的Deployment配置, 假设每个Pod都能吃满limit中的配置, 也就是一个Pod会占用1个CPU核心以及1G的内存, 如果集群共有16核和16G内存, 那最大也只能负载16个Pod, 按照最佳实践一般都需要预留20%~30%的硬件资源给k8s的系统组件, 所以这时候难免还是需要购买新的节点供HPA扩容使用, 一个节点从购买到就绪成为k8s的节点需要5分钟时间, 这时间是无法接受的, 因为这五分钟内现有的服务器已经无法承担突发流量了, 系统已经开始崩溃

对于这个问题, k8s社区诞生了一个virtual-kubelet, 具体细节这里不展开, 阿里云在vk的基础上开发了ack-vk-node组件, 把vk运行在阿里云的ECI实例上, 以快速提供k8s工作节点, 使得可以在秒级时间内快速为k8s集群添加节点, 并在不需要的时候快速销毁, 下面将这种节点成为vk-node

在上述的架构上, 阿里云提供了ElasticWorkload组件

apiVersion: autoscaling.alibabacloud.com/v1beta1
kind: ElasticWorkload
metadata:
  name: nginx
spec:
  elasticUnit:
    - annotations:
        virtual-kubelet: 'true'
      labels:
        virtual-kubelet: 'true'
      max: 32
      min: 0
      name: virtual-kubelet
      nodeSelector:
        type: virtual-kubelet
      tolerations:
        - key: virtual-kubelet.io/provider
          operator: Exists
  replicas: 3
  sourceTarget:
    apiVersion: apps/v1
    kind: Deployment
    max: 10
    min: 3
    name: nginx

上例是一段ElasticWorkload配置, sourceTarget字段指示了工作的对象, 还是上面的Deployment, max字段指示了最多在k8s工作节点上运行多少个Pod, 注意, 这里的工作节点不包括vk-node

replicas字段和Deployment的replicas字段作用一样, 用来指示当前的Deployment的Pod数量

elasticUnit配置了在vk-node的状态, max字段指示最多允许在vk-node上运行32个Pod

用通俗的话描述上例配置, 当replicas数量<=10时, 这10个Pod都会在k8s的工作节点上运行, >10的Pod会在vk-node上运行, 这样就无需购买太多的保有节点了

现在可以通过手动调整ElasticWorkloadreplicas字段来控制Deployment的Pod数量来触发上述的行为, 但是这样会很不方便, 需要实时的盯着服务器流量来调整replicas

可能你已经发现了, 可以用HPA来控制ElasticWorkload, 调整一下HPA的配置

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx
spec:
  maxReplicas: 32
  minReplicas: 3
  scaleTargetRef:
    apiVersion: autoscaling.alibabacloud.com/v1beta1
    kind: ElasticWorkload
    name: nginx
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50

将HPA的scaleTargetRef字段指向ElasticWorkload对象, 这样HPA控制器在需要扩容的时候就会修改ElasticWorkload对象的replicas字段

总结一下整体的配置, nginx这个Deployment正常情况下会运行HPA的minReplicas字段配置的数量的Pods, 也就是3个, 当这3个Pods的CPU使用率>=50%的时候, HPA会开始逐步调整ElasticWorkloadreplicas数量, 注意, 这个调整不是一步到位的, HPA会逐步的调高Pod的数量, 然后再观察平均CPU使用率再决定需不需要继续扩容, ElasticWorkload会根据自己的replicas字段调整Deployment的replicas字段, 使Deployment控制器创建出HPA需要的Pod数量, 当HPA需要把Pod数量调整到>10的时候, ElasticWorkload会把超过10的Pod调度到vk-node中运行

上面的解决方案已经能很大程度的提升了系统的弹性负载性能, 但还是有以下几点不足

  • 虽然vk-node可以在秒级内就绪, 但还是需要时间, 实测一个Pod从调度到vk-node到可以提供服务, 大概要15s的时间

  • HPA通过metrics-server获取Pod的资源使用情况, 会有一定的延迟, 也就是Pod的资源使用率已经上升了, 但是HPA要在下一个监控周期才会发现

对于上面两个问题的解决方案是

  • 给HPA喂自定义指标, 比如可以将业务的实时QPS当做指标, 这样HPA能更快速的感知到流量上升趋势

  • 将HPA的阈值调低, 在流量还没有到顶峰的时候就准备好vk-node, 这样就能更从容的面对

k8s为业务提供了无限的可能, 希望有更多的同学可以参与到公司的k8s运维工作中

最后更新于