查看原文
其他

Pod 垂直自动伸缩的使用

Gaurav Agarwal k8s技术圈 2022-09-08

VPA(Pod 垂直自动伸缩)是 Kubernetes 中非常棒的一个功能,但是平时使用却并不多。这是因为在最初 Kubernetes 就是为水平扩展而构建的,垂直扩展一个 Pod 貌似不是一个好的方式,如果你想处理更多的负载,新增一个 Pod 副本可能更好。

但是这需要大量的资源优化,如果你没有适当地调整你的 Pod,通过提供适当的资源请求和限制配置,可能最终会很频繁地驱逐你的 Pod,或者浪费很多有用的资源。开发人员和系统管理员通过对资源的各种监控,以及通过基准测试或通过对资源利用率和流量的监控,来调整这些 Pod 的资源请求和限制的最佳值。

当流量不稳定、资源利用率不理想的时候,可能就相对复杂一些了,随着容器在微服务架构中的不断发展,系统管理员更注重稳定性,这样就导致最后使用的资源请求往往会远远超过实际的需求。

Kubernetes 提供了一个解决方案来解决这个问题,那就是 VPA(Vertical Pod Autoscaler),接下来我们来了解下 VPA 的作用,以及什么时候应该使用它。

VPA 如何工作

VPA 主要使用两个组件来实现自动伸缩,实现原理如下所示:

  • VPA 推荐器检查历史资源利用率和当前使用的模式,并推荐一个理想的资源请求值
  • 如果你将更新模式定义为自动,VPA 自动调整器将驱逐正在运行的 Pod,并根据新的资源请求值创建一个新的 Pod
  • VPA 自动调整器还将按照最初定义的限制值的比例来调整限制值。

实际上资源限制并没有什么意义,因为调整器会不断调整它,不过,如果你想要避免内存泄露,可以在 VPA 中设置最大的资源值。

VPA 资源清单

我们先来看一个 VPA 的资源清单文件长什么样子,如下所示:

# vpa-example.yaml
apiVersion: autoscaling.k8s.io/v1beta2
kind: VerticalPodAutoscaler
metadata:
  name: nginx-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind:       Deployment
    name:       nginx
  updatePolicy:
    updateMode: "On"
  resourcePolicy:
    containerPolicies:
    - containerName: "nginx"
      minAllowed:
        cpu: "250m"
        memory: "100Mi"
      maxAllowed:
        cpu: "2000m"
        memory: "2048Mi"
    - containerName: "istio-proxy"
      mode: "Off"

和其他资源清单文件一样,VPA 一样有 apiVersion、kind、metadata、spec 这些属性,在 spec 规范部分,我们看到有一个 targetRef 的属性,它指定这个 VPA 对象所应用的资源对象,updatePolicy 定义了这个 VPA 是否会根据 updateMode 属性进行推荐或自动缩放。如果 updateMode 属性设置为 Auto,则 Pod 会自动垂直缩放,如果设置为 Off,则只是推荐理想的资源请求值。resourcePolicy 属性运行我们为每个容器指定容器策略,并给出 minAllowedmaxAllowed 资源值,这一点极为重要,可以避免自动的内存泄露。你还可以在一个 Pod 中的指定容器上关闭资源推荐和自动缩放,通常在 Istio sidecar 或 initContainer 中设置。

局限性

VPA 是有限制的。

不能和 HPA 一起使用

由于 VPA 会自动修改请求和限制值,所以不能将其与 HPA 一起使用,因为 HPA 依靠 CPU 和内存利用率来进行水平伸缩。有一个例外的情况是,当你 HPA 依靠自定义和外部指标来进行伸缩的时候。

需要至少两个健康的 Pod 才能工作

这种违背了它的初衷,也是没有被广泛使用的原因之一。由于 VPA 会破坏一个 Pod,并重新创建一个 Pod 来进行垂直自动伸缩,因此它需要至少两个监控的 Pod 副本来确保不会出现服务中断。这在单实例有状态应用上造成了不必要的复杂,你不得不考虑副本的设计。对于无状态应用来说,你又可以更好地使用 HPA 而不是 VPA 了,所以是不是很尴尬😓?

默认最小内存分配为250MiB

无论指定了什么配置,VPA 都会分配250MiB的最小内存,虽然这个默认值可以在全局层面进行修改,但对于消耗较少内存的应用程序来说,是比较浪费资源的。

不能用于单个 Pod

VPA 只适用于 Deployments、StatefulSets、DaemonSets、ReplicaSets 等控制器,你不能将它直接用于独立的 Pod,当然这个也还好,因为我们基本上也不使用独立的 Pod,而是会使用上层的控制器来管理 Pod。

在推荐模式下使用 VPA

目前 VPA 在生产中的最佳方式是在推荐模式下使用,这有助于我们了解最佳的资源请求值是多少,以及随着时间推移它们是如何变化的。

一旦配置了,我们就可以通过获取这些 metrics 指标,并将其发送到监控工具中去,比如 Prometheus 和 Grafana 或者 ELK 技术栈。然后可以利用这些数据来调整 Pods 的大小。

当然现在并不建议在生产中用自动模式来运行 VPA,因为该特性还比较新。我们可以在开发环境中进行实验,看看在负载不断增加的情况下是如何表现的,我们可以很容易地通过运行负载测试找到 Pod 可以消耗的最大资源量,这也是我们获取 Pod 资源限制值的一种很好的方式。

测试

首先请确保已经在 Kubernetes 集群中启用了 VPA。首先,创建一个NGINX Deployment,默认请求为250MiB 内存和100m 的 CPU。

$ cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  strategy: {}
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        resources:
          requests:
            cpu: 100m
            memory: 250Mi
EOF

现在我们将该应用通过 LoadBalancer 服务(如果不支持可以使用 NodePort)来对外进行暴露:

$ kubectl expose deployment nginx --type=LoadBalancer --port 80

暴露后云服务商会提供一个负载均衡器,然后可以获得这个负载均衡器的 IP。

$ kubectl get svc nginx
NAME    TYPE           CLUSTER-IP   EXTERNAL-IP      PORT(S)        AGE
nginx   LoadBalancer   10.4.1.4     34.121.204.234   80:30750/TCP   46s

现在,让我们创建一个 VPA 资源对象,其中 updateMode 模式设置为 "Off",这并不会进行垂直扩展,只是提供一些建议。

# vpa-off.ayml
$ cat <<EOF | kubectl apply -f -
apiVersion: autoscaling.k8s.io/v1beta2
kind: VerticalPodAutoscaler
metadata:
  name: nginx-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind:       Deployment
    name:       nginx
  updatePolicy:
    updateMode: "Off"
  resourcePolicy:
    containerPolicies:
    - containerName: "nginx"
      minAllowed:
        cpu: "250m"
        memory: "100Mi"
      maxAllowed:
        cpu: "2000m"
        memory: "2048Mi"
EOF

接下来我们使用 hey 这个工具来进行一些负载测试,看看会得到什么样的结果。

$ hey -z 300s -c 1000 http://34.121.204.234

该命令使用1000个线程发出300秒的请求。我们可以运行下面的命令来获取 VPA 的相关信息:

$ kubectl describe vpa nginx-vpa

如果观察一段时间,我们会发现 VPA 会逐渐增加推荐的资源请求。

如果你看 Recommendation 推荐部分,可以看到如下所示的一些信息:

  • Target:这是 VPA 在驱逐当前 Pod 并创建另一个 Pod 时将使用的真实值。
  • Lower Bound:这反映了触发调整大小的下限,如果你的 Pod 利用率低于这些值,则 VPA 会将其逐出并缩小其规模。
  • Upper Bound:这表示下一次要触发调整大小的上限。如果你的 Pod 利用率高于这些值,则 VPA 会将其驱逐并扩大其规模。
  • Uncapped target:如果你没有为 VPA 提供最小或最大边界值,则表示目标利用率。

现在我们如果把 updateMode 设置成 Auto 会发生什么呢?执行下面的程序来测试下。

# vpa-auto.yaml
$ cat <<EOF | kubectl apply -f -
apiVersion: autoscaling.k8s.io/v1beta2
kind: VerticalPodAutoscaler
metadata:
  name: nginx-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind:       Deployment
    name:       nginx
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
    - containerName: "nginx"
      minAllowed:
        cpu: "250m"
        memory: "100Mi"
      maxAllowed:
        cpu: "2000m"
        memory: "2048Mi"
EOF

然后让我们重新运行负载测试,在另一个终端中,我们将持续观察 Pod,看它们被驱逐和重新创建的过程。

总结

VPA 是 Kubernetes 一个比较新的自动更新概念,比较适用于有状态应用,或者了解资源利用情况的应用。但是在目前的情况下,不建议在生产中使用它的自动更新模式,但它的推荐模式有利于我们观察资源的使用情况。

原文链接:https://medium.com/better-programming/understanding-vertical-pod-autoscaling-in-kubernetes-6d53e6d96ef3




K8S进阶训练营,点击下方图片了解详情


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存