Skip to main content

Command Palette

Search for a command to run...

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

Updated
3 min read

通常情况下,要修改 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 会看到如下曲线:

随着每次运行和扩容,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 阶段,因此不推荐在生产环境中使用。

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts