# GKE 中配置 Pod 的垂直伸缩

原文：[Configuring vertical pod autoscaling](https://cloud.google.com/kubernetes-engine/docs/how-to/vertical-pod-autoscaling)

> 在 GKE 1.11.3 中提供了 Pod 垂直伸缩功能的 Beta 版本。这一功能在未来可能会收取费用，没有提供 SLA 以及过期策略，也有可能发生不向后兼容的更改。

本文阐述如何在 GKE 中配置 Pod 的垂直伸缩，该功能包含对 Pod 的 CPU 和内存申请进行调整的能力。

## 概述

可以配置 [VerticalPodAutoscaler](https://cloud.google.com/kubernetes-engine/docs/concepts/verticalpodautoscaler) CRD来对容器的[CPU](https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/)以及[内存](https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/)需求进行分析和调整。

## 开始之前

开始这一任务之前，首先要完成以下步骤：

* 确认开启了 [GKE API](https://console.cloud.google.com/apis/library/container.googleapis.com?q=kubernetes%20engine&_ga=2.131271884.-250761375.1547990554&_gac=1.19137164.1547990557.EAIaIQobChMIkbeckbr83wIVRKSWCh0_yAmwEAAYASAAEgJmc_D_BwE)
    
* 安装 [Cloud SDK](https://cloud.google.com/sdk/downloads)
    
* 设置缺省的 [Project ID](https://support.google.com/cloud/answer/6158840)
    
    `gcloud config set project [PROJECT_ID]`
    
* 如果运行的是 zonal 集群，设置缺省的 [compute zone](https://cloud.google.com/compute/docs/zones#available)：
    
    `gcloud config set compute/zone [COMPUTE_ZONE]`
    
* 如果运行的是 regional 集群，设置缺省的 [compute region](https://cloud.google.com/compute/docs/zones#available)：
    
    `gcloud config set compute/region [COMPUTE_REGION]`
    
* 更新 `gcloud` 到最新版本：
    
    `gcloud components update`
    

## 为集群启用 Pod 的垂直自动伸缩功能

可以使用下面的命令创建包含 Pod 垂直自动伸缩功能的新集群：

```bash
gcloud beta container clusters create [CLUSTER_NAME] \
    --enable-vertical-pod-autoscaling
```

`[CLUSTER_NAME]` 就是该集群的名称。

如果要给现有集群启用 Pod 垂直自动伸缩功能，可以用下列命令：

```bash
cloud beta container clusters update [CLUSTER-NAME] \
    --enable-vertical-pod-autoscaling
```

`[CLUSTER_NAME]` 就是该集群的名称。

## 获取资源推荐

下面的练习中会创建一个 VerticalPodAutoscaler，其中的 `updateMode` 设置为 `Off`。接下来创建一个包含两个 Pod 的 Deployment，每个 Pod 包含一个容器。当 Pod 创建时，VerticalPodAutoscaler 会分析容器的 CPU 和内存需要，并将推荐设置保存在 `status` 字段中。VerticalPodAutoscaler 在这一过程中不会对运行中的容器采取任何更新资源需求的措施。

VerticalPodAutoscaler 的配置如下：

```yaml
apiVersion: autoscaling.k8s.io/v1beta1
kind: VerticalPodAutoscaler
metadata:
  name: my-rec-vpa
spec:
  selector:
    matchLabels:
      purpose: try-recommend
  updatePolicy:
    updateMode: "Off"
```

将代码保存为 `my-rec-vpa.yaml` 并创建该资源：

`kubectl create -f my-rec-vpa.yaml`

Deployment 的代码如下：

```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-rec-deployment
  labels:
    purpose: try-recommend
spec:
  replicas: 2
  template:
    metadata:
      labels:
        purpose: try-recommend
    spec:
      containers:
      - name: my-rec-container
        image: nginx
```

上述代码中并未设置 CPU 和内存的请求数量。而 Deployment 中的 Pod，具有标签 `purpose: try-recommend`，符合 VerticalPodAutoscaler 的 `selector` 中定义的选择标准，因此是会受到管理的。

将上述代码命名为 `my-rec-deployment.yaml`，创建对象：

`kubectl create -f my-rec-deployment.yaml`

稍候片刻，查看 VirticalPodAutoscaler：

`kubectl get vpa my-rec-vpa --output yaml`

会显示 CPU 和内存资源的推荐设置：

```yaml
...
  recommendation:
    containerRecommendations:
    - containerName: my-rec-container
      lowerBound:
        cpu: 25m
        memory: 262144k
      target:
        cpu: 25m
        memory: 262144k
      upperBound:
        cpu: 7931m
        memory: 8291500k
...
```

现在看到了推荐的 CPU 和内存请求了，可以选择删除 Deployment，加入 CPU 和内存请求的相关内容，并重新启动。

### 自动更新资源

接下来的练习会创建一个 Deployment ，其中包含两个 Pod，每个 Pod 包含一个容器，容器请求 100m 的 CPU 以及 50M 的内存。然后创建一个 VerticalPodAutoscaler 对象，自动对 CPU 和内存的请求进行修正。

下面是该 Deployment 的代码：

```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-deployment
  labels:
    purpose: try-auto-requests
spec:
  replicas: 2
  template:
    metadata:
      labels:
        purpose: try-auto-requests
    spec:
      containers:
      - name: my-container
        image: k8s.gcr.io/ubuntu-slim:0.1
        resources:
          requests:
            cpu: 100m
            memory: 50Mi
        command: ["/bin/sh"]
        args: ["-c", "while true; do timeout 0.5s yes >/dev/null; sleep 0.5s; done"]
```

把上述代码保存为文件 `my-deployment.yaml`，并创建对象：

`kubectl create -f my-deployment.yaml`

列出运行中的 Pod：

`kubectl get pods`

输出内容中包含了 `my-deployment` 中的 Pod：

```bash
NAME                            READY     STATUS             RESTARTS   AGE
my-deployment-cbcdd49fb-d6bf9   1/1       Running            0          8s
my-deployment-cbcdd49fb-th288   1/1       Running            0          8s
```

为了后续方便，记录下 Pod 名称。

这个 Deployment 申请的 CPU 和内存非常小，所以可能提供更多资源会给这一 Deployment 带来更好的效果。

下面代码用于创建一个 `VerticalPodAutoscaler`：

```yaml
apiVersion: autoscaling.k8s.io/v1beta1
kind: VerticalPodAutoscaler
metadata:
  name: my-vpa
spec:
  selector:
    matchLabels:
      purpose: try-auto-requests
  updatePolicy:
    updateMode: "Auto"
```

这段代码中的 `selector` 字段中声明，所有带有标签 `purpose: try-auto-requests` 的 Pod 都会受其影响。

`updateMode` 字段取值为 `Auto`，代表 VerticalPodAutoscaler 会更新 Pod 的 CPU 和内存请求，也就是说 VerticalPodAutoscaler 会删除 Pod、调整 CPU 和内存申请，然后启动一个新 Pod。

把代码保存为 `my-vpa.yaml`，并创建该资源：

`kubectl create -f my-vpa.yaml`

几分钟之后，再次查看 Pod：

`kubectl get pods`

会看到 Pod 名称已经发生了变化，如果没有，请隔一段时间再次查看。

获取一个新 Pod 的信息：

`kubectl get pod [POD_NAME] --output yaml`

输出内容中，会看到 VerticalPodAutoscaler 提高了内存和 CPU 的设置。还会看到注解中也发生了更新：

```yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    vpaUpdates: 'Pod resources updated by my-vpa: container 0: cpu capped to node
      capacity, memory capped to node capacity, cpu request, memory request'
...
spec:
  containers:
  ...
    resources:
      requests:
        cpu: 510m
        memory: 262144k
    ...
```

查看一下 VerticalPodAutoscaler 的详情：

`kubectl get vpa my-vpa --output yaml`

输出内容中显示了三组对 CPU 和内存申请的建议：`lowerBound`、`target` 和 `upperBound`：

```yaml
...
  recommendation:
    containerRecommendations:
    - containerName: my-container
      lowerBound:
        cpu: 536m
        memory: 262144k
      target:
        cpu: 587m
        memory: 262144k
      upperBound:
        cpu: 27854m
        memory: "545693548"
```

`target` 的含义是，该容器使用 587m 的 CPU 和 262144 KB 的内存会获得更好的运行效果。

VerticalPodAutoscaler 会使用 `lowerBound` 和 `upperBound` 来决定是否重新创建 Pod，如果一个 Pod 申请的资源少于 `lowerBound` 或者大于 `upperBound`，就会被 VerticalPodAutoscaler 删除并使用 `target` 推荐的规格进行重建。

### Selector 不可重叠

VerticalPodAutoscaler 包含了一个 `selector` 字段，用来决定该对象的影响范围。如果创建了不止一个的 `VerticalPodAutoscaler`，要保证其 selector 不能发生重叠。

例如下面的两个 VerticalPodAutoscaler：

```yaml
...
kind: VerticalPodAutoscaler
metadata:
  name: my-vpa-1
spec:
  selector:
    matchLabels:
      app: metrics
      department: engineering
...
```

```yaml
...
...
kind: VerticalPodAutoscaler
metadata:
  name: my-vpa-2
spec:
  selector:
    matchLabels:
      department: engineering
...
```

带有 `app: metrics` 和 `department: engineering` 标签的 Pod 会被两个对象同时管理，会引发问题。
