本文发表已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已失效。

Kubernetes 中的自动扩缩容

编者按:本文是关于 Kubernetes 1.3 新特性的 系列深度文章之一。

使用 Kubernetes 的客户能够快速响应终端用户请求,并以前所未有的速度交付软件。但是,当你构建的服务比预期更受欢迎,并且计算资源耗尽时会发生什么?在 Kubernetes 1.3 中,我们自豪地宣布我们有一个解决方案:自动扩缩容。在 Google Compute Engine (GCE) 和 Google Container Engine (GKE)(即将支持 AWS)上,Kubernetes 会在你需要时立即自动扩容集群,并在你不需要时缩容以节省费用。

自动扩缩容的优势

为了更好地理解自动扩缩容在哪里能提供最大的价值,我们从一个例子开始。想象一下,你有一个全天候的生产服务,其负载随时间变化,在美国白天非常繁忙,而晚上相对较低。理想情况下,我们希望集群中的节点数量和部署中的 Pod 数量能够根据负载动态调整,以满足终端用户需求。新的集群自动扩缩容功能与水平 Pod 自动扩缩器 (Horizontal Pod Autoscaler) 结合使用,可以为你自动处理这个问题。

在 GCE 上设置自动扩缩容

以下说明适用于 GCE。对于 GKE,请查阅此处提供的集群操作手册中的自动扩缩容部分:此处

在开始之前,我们需要有一个启用了 Google Cloud Monitoring、Google Cloud Logging 和 Stackdriver 的活跃 GCE 项目。有关项目创建的更多信息,请阅读我们的入门指南。我们还需要下载一个最新版本的 Kubernetes 项目(v1.3.0 或更高版本)。

首先,我们设置一个开启了集群自动扩缩器的集群。集群中的节点数量将从 2 个开始,并自动扩缩容到最多 5 个。为了实现这一点,我们将导出以下环境变量:

export NUM\_NODES=2

export KUBE\_AUTOSCALER\_MIN\_NODES=2

export KUBE\_AUTOSCALER\_MAX\_NODES=5

export KUBE\_ENABLE\_CLUSTER\_AUTOSCALER=true

并通过运行以下命令启动集群:

./cluster/kube-up.sh

kube-up.sh 脚本会创建一个集群并带有集群自动扩缩器插件。如果存在可以在新节点上调度的待定 Pod,自动扩缩器将尝试向集群添加新节点。

让我们看看我们的集群,它应该有两个节点:

$ kubectl get nodes

NAME                           STATUS                     AGE

kubernetes-master              Ready,SchedulingDisabled   2m

kubernetes-minion-group-de5q   Ready                      2m

kubernetes-minion-group-yhdx   Ready                      1m

运行并暴露 PHP-Apache 服务器

为了演示自动扩缩容,我们将使用基于 php-apache 服务器的自定义 Docker 镜像。该镜像可以在此处找到。它定义了一个执行 CPU 密集型计算的 index.php 页面。

首先,我们将启动一个运行该镜像的部署,并将其暴露为一个服务:

$ kubectl run php-apache \   

  --image=gcr.io/google\_containers/hpa-example \

  --requests=cpu=500m,memory=500M --expose --port=80  

service "php-apache" createddeployment "php-apache" created

现在,我们将等待一段时间,并验证部署和服务都已正确创建并正在运行:

$ kubectl get deployment

NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE

php-apache   1         1         1            1           49s



$ kubectl get pods  
NAME                          READY     STATUS    RESTARTS   AGE

php-apache-2046965998-z65jn   1/1       Running   0          30s

现在我们可以通过使用服务的地址调用 wget 来检查 php-apache 服务器是否正常工作:

$ kubectl run -i --tty service-test --image=busybox /bin/sh  
Hit enter for command prompt  
$ wget -q -O- http://php-apache.default.svc.cluster.local

OK!

启动水平 Pod 自动扩缩器

现在部署正在运行,我们将为其创建一个水平 Pod 自动扩缩器。要创建它,我们将使用 kubectl autoscale 命令,如下所示:

$ kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10

这定义了一个水平 Pod 自动扩缩器,它将维护我们在这些说明的第一步中创建的 php-apache 部署所控制的 Pod 的 1 到 10 个副本。粗略地说,水平自动扩缩器将通过部署增加和减少副本数量,以将所有 Pod 的平均 CPU 利用率保持在 50%(由于每个 Pod 通过 kubectl run 请求 500 milli-cores,这意味着平均 CPU 使用率为 250 milli-cores)。有关算法的更多详细信息,请参阅此处

我们可以通过运行以下命令检查自动扩缩器的当前状态:

$ kubectl get hpa

NAME         REFERENCE                     TARGET    CURRENT   MINPODS   MAXPODS   AGE

php-apache   Deployment/php-apache/scale   50%       0%        1         20        14s

请注意,当前 CPU 消耗为 0%,因为我们没有向服务器发送任何请求(CURRENT 列显示了相应复制控制器控制的所有 Pod 的平均值)。

提高负载

现在,我们将看看我们的自动扩缩器(集群自动扩缩器和水平 Pod 自动扩缩器)如何响应服务器负载的增加。我们将启动两个无限循环向我们的服务器发送查询(请在不同的终端中运行它们):

$ kubectl run -i --tty load-generator --image=busybox /bin/sh  
Hit enter for command prompt  
$ while true; do wget -q -O- http://php-apache.default.svc.cluster.local; done

我们必须等待片刻(大约一分钟)以使统计信息传播。之后,我们将检查水平 Pod 自动扩缩器的状态:

$ kubectl get hpa

NAME         REFERENCE                     TARGET    CURRENT   MINPODS   MAXPODS   AGE

php-apache   Deployment/php-apache/scale   50%       310%      1         20        2m



$ kubectl get deployment php-apache

NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE

php-apache        7         7         7            3           4m

水平 Pod 自动扩缩器已将我们部署中的 Pod 数量增加到 7 个。现在,让我们检查一下所有 Pod 是否都在运行:

jsz@jsz-desk2:~/k8s-src$ kubectl get pods

php-apache-2046965998-3ewo6        0/1       Pending   0          1m

php-apache-2046965998-8m03k        1/1       Running   0          1m

php-apache-2046965998-ddpgp        1/1       Running   0          5m

php-apache-2046965998-lrik6        1/1       Running   0          1m

php-apache-2046965998-nj465        0/1       Pending   0          1m

php-apache-2046965998-tmwg1        1/1       Running   0          1m

php-apache-2046965998-xkbw1        0/1       Pending   0          1m

如我们所见,有些 Pod 处于待定状态。让我们描述其中一个待定 Pod,以获取待定状态的原因:

$ kubectl describe pod php-apache-2046965998-3ewo6

Name: php-apache-2046965998-3ewo6

Namespace: default

...

Events:

  FirstSeen From SubobjectPath Type Reason Message



  1m {default-scheduler } Warning FailedScheduling pod (php-apache-2046965998-3ewo6) failed to fit in any node

fit failure on node (kubernetes-minion-group-yhdx): Insufficient CPU

fit failure on node (kubernetes-minion-group-de5q): Insufficient CPU



  1m {cluster-autoscaler } Normal TriggeredScaleUp pod triggered scale-up, mig: kubernetes-minion-group, sizes (current/new): 2/3

该 Pod 处于待定状态,因为系统中没有足够的 CPU 资源。我们看到与该 Pod 相关联的 TriggeredScaleUp 事件。这意味着该 Pod 触发了集群自动扩缩器的反应,一个新的节点将被添加到集群中。现在我们将等待反应(大约 3 分钟),并列出所有节点:

$ kubectl get nodes

NAME                           STATUS                     AGE

kubernetes-master              Ready,SchedulingDisabled   9m

kubernetes-minion-group-6z5i   Ready                      43s

kubernetes-minion-group-de5q   Ready                      9m

kubernetes-minion-group-yhdx   Ready                      9m

如我们所见,集群自动扩缩器添加了一个新节点 kubernetes-minion-group-6z5i。让我们验证所有 Pod 现在是否都在运行:

$ kubectl get pods

NAME                               READY     STATUS    RESTARTS   AGE

php-apache-2046965998-3ewo6        1/1       Running   0          3m

php-apache-2046965998-8m03k        1/1       Running   0          3m

php-apache-2046965998-ddpgp        1/1       Running   0          7m

php-apache-2046965998-lrik6        1/1       Running   0          3m

php-apache-2046965998-nj465        1/1       Running   0          3m

php-apache-2046965998-tmwg1        1/1       Running   0          3m

php-apache-2046965998-xkbw1        1/1       Running   0          3m

添加节点后,所有 php-apache Pod 都在运行了!

停止负载

我们将通过停止用户负载来结束我们的示例。我们将终止向服务器发送请求的两个无限循环,并验证结果状态:

$ kubectl get hpa

NAME         REFERENCE                     TARGET    CURRENT   MINPODS   MAXPODS   AGE

php-apache   Deployment/php-apache/scale   50%       0%        1         10        16m



$ kubectl get deployment php-apache

NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE

php-apache        1         1         1            1           14m

如我们所见,在所示的情况下,CPU 利用率降至 0,副本数量降至 1。

删除 Pod 后,大部分集群资源处于未使用状态。集群缩容可能比扩容花费更长时间,因为集群自动扩缩器会确保节点确实不再需要,这样短期的非活动状态(例如 Pod 升级等)不会触发节点删除(参阅集群自动扩缩器文档)。大约 10-12 分钟后,您可以验证集群中的节点数量是否减少:

$ kubectl get nodes

NAME                           STATUS                     AGE

kubernetes-master              Ready,SchedulingDisabled   37m

kubernetes-minion-group-de5q   Ready                      36m

kubernetes-minion-group-yhdx   Ready                      36m

集群自动扩缩器移除了节点 kubernetes-minion-group-6z5i 后,我们集群中的节点数量现在又回到了两个。

其他用例

正如我们所示,使用水平 Pod 自动扩缩器和集群自动扩缩器的组合,可以非常容易地根据负载动态调整 Pod 的数量。

然而,集群自动扩缩器本身在集群负载不规律时也非常有用。例如,与开发或持续集成测试相关的集群在周末或夜间可能需求较少。批处理集群可能有一段时间所有作业都已完成,新的作业将在几个小时后才开始。拥有空闲的机器是浪费金钱。

在所有这些情况下,集群自动扩缩器都可以减少未使用节点的数量,并带来相当可观的节省,因为您只需为您实际运行 Pod 所需的节点付费。它还确保您始终拥有足够的计算能力来运行您的任务。