无需重启,使用 Shell Operator 对 Pod 进行垂直扩缩容

通常情况下,要修改 Pod 的资源定义,是需要重启 Pod 的。在 Kubernetes 1.27 中,有一个 Alpha 状态的 InPlacePodVerticalScaling 开关,开启这一特性,就能在不重启 Pod 的情况下,修改 Pod 的资源定义。

要使用这个功能,需要在 kube-apiserverfeatureGates 中显式地设置启用,启用这一特性之后,就可以进行测试了。

例如 Kind 集群,需要在配置中加入:

featureGates:
  "InPlacePodVerticalScaling": true

测试一下

假设下面的 Pod 定义:

apiVersion: v1
kind: Pod
metadata:
  name: stress
spec:
  containers:
  - name: stress
    image: colinianking/stress-ng:latest
    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired
    - resourceName: memory
      restartPolicy: RestartContainer    
    command: ["sleep", "3600"]
    resources:
      limits:
        cpu: 200m
        memory: 200M
      requests:
        cpu: 200m
        memory: 200M

可以看到,spec 中加入了 resizePolicy 字段,用来指定对 CPU 和内存的扩缩容策略。内容很直白:

  • CPU 的扩缩容策略是 NotRequired,即不重启 Pod;
  • 内存的扩缩容策略是 RestartContainer,即重启 Pod。

将上述内容提交到 Kubernetes 中运行。启动之后,如果运行 kubectl get po stress -o yaml,会发现状态字段中加入了如下内容:

- allocatedResources:
    cpu: 200m
    memory: 200Mi

说明此时分配给容器的资源。如果这时候对 CPU 进行修改,例如修改为:

resources:
  limits:
    cpu: 800m
    memory: 200Mi
  requests:
    cpu: 100m
    memory: 100Mi

修改后查看 Pod 列表,会发现 Pod 没有重启:

$ kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
stress   1/1     Running   0          4m14s

重新获取 YAML,会看到状态字段的一些变化:

  1. resize: InProgress:表示正在扩缩容;
  2. 当前分配的资源也发生了变化:

    - allocatedResources:
      cpu: 100m
      memory: 100Mi
    

自动纵向扩缩容

到目前为止,VPA 还没有支持这一特性。我们可以简地使用 Prometheus 对 Pod 资源压力进行监控,然后使用 Shell Operator 来实现自动扩缩容。总体思路就是,定期读取 Prometheus,获取指定 Pod 的 CPU 和使用情况,如果 CPU 使用率超过 80%,则将其 CPU 上限扩容一倍。

Prometheus 监控指标

Awesome Prometheus alerts 提供了如下的告警定义,用于表达 CPU 用量和其 Limit 的关系:

  - alert: ContainerHighCpuUtilization
    expr: (sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (pod, container) / sum(container_spec_cpu_quota{container!=""}/container_spec_cpu_period{container!=""}) by (pod, container) * 100) > 80
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: Container High CPU utilization (instance {{ $labels.instance }})
      description: "Container CPU utilization is above 80%\n  VALUE = {{ $value }}\n  LABELS = {{ $labels }}"

我们把它写入 Python 代码:

CPU_USAGE_QUERY = '''(sum(rate(container_cpu_usage_seconds_total{{namespace="{0}", pod="{1}", container="{2}"}}[5m])) by (pod, container) 
/ sum(container_spec_cpu_quota{{namespace="{0}", pod="{1}",container="{2}"}}
/container_spec_cpu_period{{namespace="{0}", pod="{1}", container="{2}"}}) by (pod, container) * 100)'''

定期运行

要设置 Shell Operator 的定期运行,需要使用 Schedule 类型的配置,下面的 Configmap 设置每两分钟运行一次:

apiVersion: v1
data:
  config.yaml: |+
    configVersion: v1
    schedule:
    - crontab: "*/2 * * * *"
      allowFailure: true
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: so-config

我们会将这个 Configmap 加载到 Pod 定义中,

...
volumeMounts:
- mountPath: /conf/
  name: operator-config
...
volumes:
- configMap:
  name: so-config
name: operator-config
...

在 Hook 代码执行参数中带有 --config 参数时,读取该配置进行返回:

if len(sys.argv) > 1 and sys.argv[1] == "--config":
    with open("/conf/config.yaml", "r") as f:
        print(f.read())
    exit(0)

RBAC

Shell Operator 需要对 Pod 资源进行扩容,所以需要如下授权:

- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch", "patch"]

构建 Docker 镜像

FROM flant/shell-operator
RUN apk update && \
    apk add --no-cache py3-requests
COPY main.py /hooks

测试

Workload 中,我们设置的资源是 100M 内存+100m CPU 的配置,使用 kubectl exec -it stress -- sh 进入 Pod 之后,执行 stress-ng --cpu 1 --fork 2 制造一点压力,触发 Shell Operator 中的脚本对 Pod 进行纵向扩容,在 Prometheus 会看到如下曲线:

images/prom.png

随着每次运行和扩容,CPU 水位不断下降,直到稳定。打开 Pod 定义,会看到扩容的痕迹:

  containerStatuses:
  - allocatedResources:
      cpu: 6400m
      memory: 100M
    containerID: containerd://10c55739a6a63f3464184f5384a2f2b091a235b7b6689bcdb58526e3eb8bdb19
    image: docker.io/colinianking/stress-ng:latest
    imageID: docker.io/colinianking/stress-ng@sha256:1b10c09968ea3460196596398f7811c7a604489a8311b3dbf477f552ac5ea972
    lastState:
      terminated:
        containerID: containerd://e3f1fe628086e291830b47247c88403d3ce0f4fd5db38b18afcca444659011d3
        exitCode: 0
        finishedAt: "2024-09-08T08:07:49Z"
        reason: Completed
        startedAt: "2024-09-08T07:07:49Z"
    name: stress
    ready: true
    resources:
      limits:
        cpu: 6400m
        memory: 100M

结论

全部代码被上传到了 https://github.com/fleeto/vscale-by-shelloperator。内容当然还是非常简陋,例如缺乏缩容手段、没有对上限进行限制,防抖动措施也是缺乏的。另外该特性还处在 Alpha 阶段,因此不推荐在生产环境中使用。

Avatar
崔秀龙

简单,是大师的责任;我们凡夫俗子,能做到清楚就很不容易了。

comments powered by Disqus
下一页
上一页