Kubernetes提供了Horizo​​ntal Pod Autoscaler,它可以根据您提供的指标自动缩放资源(Deployment,ReplicationController,ReplicaSet,StatefulSet)中副本的数量。通常,所使用的度量标准是CPU /内存负载,对于大多数应用程序而言已足够。

Horizo​​ntal Pod Autoscaler的局限性在于它具有用于评估以下指标的硬编码算法:

desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]

如果您在缩放方面需要更大的灵活性,除了此算法之外,Horizo​​ntal Pod Autoscaler不能满足您的需求,则需要编写自己的缩放逻辑。

对于游戏服务器,按CPU负载或内存使用量进行扩展没有意义,即使您实施了自定义指标,缩放算法也不会以合理的方式进行缩放。游戏服务器应根据服务器上的玩家数量或希望加入服务器的玩家数量进行伸缩。

Custom Pod Autoscaler (CPA)是 自定义Pod自动伸缩器框架的一部分。其允许更轻松,更快地开发Kubernetes自动伸缩器。通过使用此项目,可以扩展提供的Docker基本镜像并插入自己的逻辑,从而创建Custom Pod Autoscaler。

特点

  • 支持任何语言,环境和框架;唯一的要求是它必须可由Shell命令或HTTP请求启动。
  • 支持Horizo​​ntal Pod Autoscaler的所有配置选项(冷却窗口,同步周期等)
  • 允许快速轻松地进行原型开发。
  • 摘除所有复杂的Kubernetes API交互。
  • 公开HTTP REST API以与更广泛的系统/手动干预集成。
  • 可以使用有限的Kubernetes API或生命周期知识编写自动伸缩器。
  • 在构建时或部署时进行配置。
  • 允许缩放到零或从零开始。
  • 可以在没有主节点访问权限的情况下进行配置,可以在EKS或GKE等托管提供程序上进行配置。

工作原理

Custom Pod Autoscaler 具有一个基本程序,该程序处理与用户逻辑的交互,例如,通过使用shell命令并将数据传递到其中。开发自定义Pod自动缩放器时,您需要为两个阶段定义逻辑:

  • 指标收集-收集或生成指标;可以调用指标API,在本地运行计算并发出HTTP请求。
  • 评估指标-获取这些收集的指标,并使用它们来确定资源应具有多少个副本。

这两个逻辑都是构建Custom Pod Autoscaler所需的所有自定义逻辑,基本程序将处理所有Kubernetes API交互以扩展/获取资源。

示例

开发Custom Pod Autoscaler是一个简单而灵活的过程,它可以用您喜欢的任何一种语言完成,并且可以使用各种Docker镜像。对于本指南,我们将构建一个简单的基于Python的自动缩放器,但是您可以采用此处概述的原理并以任何语言实现自动缩放器,其他语言实现的示例
在本指南中,我们将创建一个基于Python的Kubernetes自动缩放器。自动缩放器将根据资源标签numPods来工作,并将标签中提供的值作为目的值。这只是一个示例,但希望可以让让您明白编写自定义自动扩缩器的原理和方法。可以在 python-custom-autoscaler 中查阅完整代码。

1:创建工程

为项目python-custom-autoscaler创建一个新目录。

mkdir python-custom-autoscaler

2:编写自定义Pod伸缩器的配置

我们将为此自动缩放器设置Custom Pod Autoscaler配置,该配置将定义要运行的脚本,如何调用它们以及该脚本的超时时间。创建一个新文件config.yaml

evaluate:
  type: "shell"
  timeout: 2500
  shell:
    entrypoint: "python"
    command:
      - "/evaluate.py"
metric:
  type: "shell"
  timeout: 2500
  shell:
    entrypoint: "python"
    command:
      - "/metric.py"
runMode: "per-resource"

该配置文件指定了我们要添加的两个脚本,即度量收集器和评估器-定义应通过shell命令调用它们,并且它们应在2500毫秒(2.5秒)后超时。还指定了runMode,我们选择了per-resource-这意味着度量标准收集将仅针对资源运行一次,并且将向度量标准脚本提供资源信息。每个Pod的一个替代选项是,它将为资源所具有的每个Pod运行度量收集脚本,并为该脚本提供单独的Pod信息。

3: 编写指标收集

现在,我们将创建自动缩放器的度量收集部分,该部分将简单地阅读所提供的资源描述,并从中提取numPods标签值,然后再将其输出回给评估者进行决策。

创建metric.py:

import os
import json
import sys

def main():
    # Parse spec into a dict
    spec = json.loads(sys.stdin.read())
    metric(spec)

def metric(spec):
    # Get metadata from resource information provided
    metadata = spec["resource"]["metadata"]
    # Get labels from provided metdata
    labels = metadata["labels"]

    if "numPods" in labels:
        # If numPods label exists, output the value of the numPods
        # label back to the autoscaler
        sys.stdout.write(labels["numPods"])
    else:
        # If no label numPods, output an error and fail the metric gathering
        sys.stderr.write("No 'numPods' label on resource being managed")
        exit(1)

if __name__ == "__main__":
    main() 

度量收集阶段从自动Pod伸缩器获取相关信息。在此示例中,我们以per-resource运行-意味着仅对要管理的资源调用一次度量脚本,并将资源信息通过管道传递到该脚本中。例如,如果我们正在管理部署,则自动缩放器将提供我们正在管理的部署的完整JSON描述,例如通过管道传递的值。

{
  "resource": {
    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "metadata": {
      "name": "hello-kubernetes",
      "namespace": "default",
      "labels": {
        "numPods": "3"
      },
    },
    ...
  },
  "runType": "scaler"
}

我们制作的脚本仅解析该JSON并提取numPods值。如果未提供numPods,则脚本将出错,并提供一条错误消息,自动缩放器将提取并记录该错误消息。如果提供了numPods,则该值将通过标准输出输出并由自动伸缩器读取,然后传递到评估阶段。

4:编写评估器

现在,我们将创建评估器,该评估器将读取度量收集阶段提供的度量,并基于这些收集的度量来计算副本的数量-在这种情况下,它将简单地是上一步提供的度量值。如果该值不是整数,则脚本将返回错误。

创建evaluate.py:

import json
import sys
import math

def main():
    # Parse provided spec into a dict
    spec = json.loads(sys.stdin.read())
    evaluate(spec)

def evaluate(spec):
    try:
        value = int(spec["metrics"][0]["value"])

        # Build JSON dict with targetReplicas
        evaluation = {}
        evaluation["targetReplicas"] = value

        # Output JSON to stdout
        sys.stdout.write(json.dumps(evaluation))
    except ValueError as err:
        # If not an integer, output error
        sys.stderr.write(f"Invalid metric value: {err}")
        exit(1)

if __name__ == "__main__":
    main()

传递到此步骤的JSON值如下所示:

{
  "metrics": [
    {
      "resource": "hello-kubernetes",
      "value": "3"
    }
  ],
  "resource": {
    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "metadata": {
      "name": "hello-kubernetes",
      "namespace": "default",
      "labels": {
        "numPods": "3"
      },
    },
    ...
  },
  "runType": "scaler"
}

这只是指标值,在本例中为上一步中的5,但封装在JSON对象中,并带有其他信息,例如运行类型和部署名称。

此步骤输出的JSON值如下所示:

{
  "targetReplicas": 5
}

Custom Pod Autoscaler程序期望该响应采用此JSON序列化形式,并且targetReplicas定义为整数。

5: 编写dockerfile

我们的Dockerfile将会非常简单,我们将使用Python 3 docker镜像,并在其中内置Custom Pod Autoscaler二进制文件。创建一个新文件Dockerfile:

# Pull in Python build of CPA
FROM custompodautoscaler/python:latest

# Add config, evaluator and metric gathering Py scripts
ADD config.yaml evaluate.py metric.py /

这个Dockerfile只是插入我们的两个脚本和我们的配置文件中。现在,我们已经完成了自动缩放器的创建,让我们看看它的工作原理。

6:功能测试

首先,我们应该通过安装Custom Pod Autoscaler Operator在集群上启用自定义自动缩放器,对于本指南,我们使用的是v1.0.1,但是请从Custom Pod Autoscaler Operator 发行版中签出最新版本,并参阅安装指南以获取最新的安装信息。

VERSION=v1.0.1
kubectl apply -f https://github.com/jthomperoo/custom-pod-autoscaler-operator/releases/download/${VERSION}/cluster.yaml

这将在整个集群范围内安装Custom Pod Autoscaler Operator v1.0.1。

现在,我们应该为自动伸缩器创建一个部署以进行管理,并创建一个部署YAML文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-kubernetes
  labels:
    numPods: "3"
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-kubernetes
  template:
    metadata:
      labels:
        app: hello-kubernetes
    spec:
      containers:
      - name: hello-kubernetes
        image: paulbouwer/hello-kubernetes:1.5
        ports:
        - containerPort: 8080

这是我们将使用自定义自动缩放器进行管理的部署,它最初设置为具有1个副本,但其中包含numPods:“3”标签,因此如果我们的自动缩放器正常工作,它将读取该值并设置副本计数到3。

部署此应用:

kubectl apply -f deployment.yaml

现在,我们需要为新的自动缩放器构建镜像,并切换到Minikube Docker注册表作为目标:

eval $(minikube docker-env)

接下来构建镜像:

docker build -t python-custom-autoscaler .

让我们为自动缩放器创建YAML,创建一个新文件cpa.yaml

apiVersion: custompodautoscaler.com/v1
kind: CustomPodAutoscaler
metadata:
  name: python-custom-autoscaler
spec:
  template:
    spec:
      containers:
      - name: python-custom-autoscaler
        image: python-custom-autoscaler:latest
        imagePullPolicy: IfNotPresent
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: hello-kubernetes
  config:
    - name: interval
      value: "10000"

该YAML概述了我们的自动缩放器实例的名称,使用模板的自动缩放器的镜像定义,我们使用scaleTargetRef定位要管理的资源以及一些基本的额外配置(间隔设置为10000毫秒),这意味着自动缩放器将每10秒运行一次。

现在,我们应该将自动缩放器部署到集群中:

kubectl apply -f cpa.yaml

如果我们执行kubectl get pods,我们应该看到已经创建了一个运行自动缩放器的新Pod。使用kubectl logs <POD NAME HERE> --follow,我们应该能够查看运行自动缩放器是否发生任何错误,以及有关自动缩放器如何决定缩放的信息。
自动缩放器扩容后,应将托管部署的副本数增加到3(由numPods标签指定),并通过kubectl get pod进行检查。尝试更新deployment.yaml标签值,然后再次部署它,查看副本数如何变化。

思考

DigitalOcean 开源的CPA operator,做了大量的抽象,可以让大家集中精力在自己的逻辑上,而无需过多了解k8s底层。

随着容器化的大规模落地,对于自定义HPA的需求也越来越多。CPA operator完美的解决了这一痛点。


iyacontrol
1.4k 声望2.7k 粉丝

专注kubernetes,devops,aiops,service mesh。