调整分配给容器的 CPU 和内存资源
Kubernetes v1.33 [beta]
(enabled by default: true)
本页面说明了如何在不重新创建 Pod 的情况下,更改分配给容器的 CPU 和内存资源请求与限制。
传统上,更改 Pod 的资源需求需要删除现有 Pod 并创建一个替代 Pod, 这通常由工作负载控制器管理。 而就地 Pod 调整功能允许在运行中的 Pod 内变更容器的 CPU 和内存分配,从而可能避免干扰应用。
关键概念:
- 期望资源(Desired Resources):容器的
spec.containers[*].resources
字段表示容器的期望资源,对于 CPU 和内存是可变的。 - 实际资源(Actual Resources):
status.containerStatuses[*].resources
字段反映当前运行容器实际配置的资源。 对于尚未启动或重新启动的容器,该字段表示其下次启动时分配的资源。 - 触发调整(Triggering a Resize):你可以通过更新 Pod 规约中的
requests
和limits
来请求调整。 这通常通过kubectl patch
、kubectl apply
或kubectl edit
操作 Pod 的resize
子资源来完成。 当期望资源与已分配资源不一致时,Kubelet 会尝试调整容器资源。 - 已分配资源(Allocated Resources,进阶):
status.containerStatuses[*].allocatedResources
字段用于记录由 Kubelet 确认的资源值,主要用于内部调度逻辑。 在大多数监控和验证场景中,建议关注status.containerStatuses[*].resources
字段。
如果某个节点上存在处于挂起或未完成调整状态的 Pod(见下文 Pod 调整状态), 调度器会在进行调度决策时, 使用容器的期望请求、已分配请求和实际请求三者中的最大值。
准备开始
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
你的 Kubernetes 服务器版本必须不低于版本 1.33.要获知版本信息,请输入 kubectl version
.
你需要在控制平面和集群中的所有节点上启用 InPlacePodVerticalScaling
特性门控。
要使用 --subresource=resize
参数,kubectl
客户端版本需至少为 v1.32。
Pod 大小调整状态
Kubelet 会通过更新 Pod 的状态状况来反映调整请求的当前状态:
type: PodResizePending
:Kubelet 当前无法立即执行该请求。message
字段会说明原因:reason: Infeasible
:请求的资源在当前节点上不可行(例如请求超出节点总资源)。reason: Deferred
:请求的资源当前无法满足,但未来可能满足(例如其他 Pod 被移除后), Kubelet 会重试调整。
type: PodResizeInProgress
:Kubelet 已接受调整并分配了资源,但调整仍在进行中。
这一状态通常很短暂,但也可能因资源类型或运行时行为而延长。 执行过程中的任何错误都会在message
字段中报告,同时带有reason: Error
。
如何重试 Deferred 调整大小
如果请求的调整大小操作被标记为 Deferred,kubelet 会定期重新尝试执行该调整,例如当其他 Pod 被移除或缩容时。 当存在多个延迟的调整操作时,kubelet 会按照以下优先级顺序进行重试:
- 优先级(基于 PriorityClass)较高的 Pod,其调整请求会先被重试。
- 如果两个 Pod 拥有相同的优先级,则会先重试 Guaranteed 类型的 Pod,再重试 Burstable 的类型 Pod。
- 如果上述条件均相同,则优先处理在延迟状态下停留时间更长的 Pod。
需要注意的是,即使高优先级的调整被再次标记为待处理,也不会阻塞其余待处理的调整操作;其余的待处理调整仍会被继续重试。
利用 observedGeneration
字段
Kubernetes v1.34 [beta]
(enabled by default: true)
- 顶层的
status.observedGeneration
字段显示了 kubelet 已确认的最新 Pod 规约所对应的metadata.generation
。 你可以使用该字段来判断 kubelet 已处理的最近一次调整请求。 - 在
PodResizeInProgress
状态条件,conditions[].observedGeneration
字段表示当前正在进行的调整操作开始时, 该 Pod 规约(podSpec)的metadata.generation
。 - 在
PodResizePending
状态条件,conditions[].observedGeneration
字段表示上一次尝试为待处理调整请求分配资源时, Pod 规约的metadata.generation
。
容器调整策略
容器可以在资源需求中指定可选的 resizePolicy
数组。
该数组中的每一项定义了某种资源在就地调整期间应如何处理。
你可以通过在容器规约中设置 resizePolicy
,控制在调整资源时容器是否需要重启。
这样可以针对不同资源类型(CPU 或内存)进行精细化控制。
resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired
- resourceName: memory
restartPolicy: RestartContainer
NotRequired
:(默认)在不重启容器的情况下应用资源变更。RestartContainer
:重启容器以应用新的资源值。 对于内存变更,许多应用和运行时无法动态调整内存分配,因此通常需要重启。
如果未为某个资源指定 resizePolicy[*].restartPolicy
,则默认为 NotRequired
。
说明:
如果 Pod 的整体 restartPolicy
为 Never
,则所有容器的 resizePolicy
必须对所有资源都设置为 NotRequired
。
此类 Pod 不允许配置需要重启的调整策略。
示例场景:
考虑一个容器,其 CPU 的 restartPolicy
为 NotRequired
,内存的 restartPolicy
为 RestartContainer
:
- 如果仅更改 CPU 资源,容器将原地调整大小。
- 如果仅更改内存资源,容器将重启。
- 如果同时更改 CPU 和内存资源,容器将重启(由于内存策略)。
限制
对于 Kubernetes v1.34,原地调整 Pod 资源大小存在以下限制:
- 资源类型:只能调整 CPU 和内存资源。
- 内存减少:如果内存调整的重启策略为
NotRequired
(或未指定),kubelet 会尽力在降低内存限制时避免 OOM(内存不足导致的进程被杀死), 但并不提供任何保证。在降低容器内存限制之前,如果内存使用量已超过请求的限制,则此次调整会被跳过, 状态将保持在 "In Progress"。之所以称为尽力而为,是因为该过程仍可能受到竞争条件影响: 在检查完成后,内存使用量可能会立即出现峰值。
- QoS 类:Pod 的原始服务质量(QoS)类
(Guaranteed、Burstable 或 BestEffort)在创建时确定,不能通过调整大小来更改。
调整后的资源值仍必须遵守原始 QoS 类的规则:
- Guaranteed:调整后,CPU 和内存的请求必须继续等于限制。
- Burstable:CPU 和内存的请求和限制不能同时变为相等 (因为这会将其更改为 Guaranteed)。
- BestEffort:不能添加资源要求(
requests
或limits
) (因为这会将其更改为 Burstable 或 Guaranteed)。
- 资源移除:一旦设置了资源请求和限制,就不能完全移除; 只能更改为不同的值。
- 操作系统:Windows Pod 不支持原地调整大小。
- 节点策略:由静态 CPU 或内存管理器策略管理的 Pod 不能原地调整大小。
- 交换内存:使用交换内存的 Pod 不能调整内存请求,
除非内存的
resizePolicy
为RestartContainer
。
这些限制可能会在未来的 Kubernetes 版本中放宽。
示例 1:调整 CPU 而不重启
首先,创建一个设计用于原地 CPU 调整和需要重启的内存调整的 Pod。
apiVersion: v1
kind: Pod
metadata:
name: resize-demo
spec:
containers:
- name: pause
image: registry.k8s.io/pause:3.8
resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired # Default, but explicit here
- resourceName: memory
restartPolicy: RestartContainer
resources:
limits:
memory: "200Mi"
cpu: "700m"
requests:
memory: "200Mi"
cpu: "700m"
创建 Pod:
kubectl create -f pod-resize.yaml
这个 Pod 以 Guaranteed QoS 类启动。验证其初始状态:
# 等待 Pod 运行
kubectl get pod resize-demo --output=yaml
观察 spec.containers[0].resources
和 status.containerStatuses[0].resources
。
它们应该与清单文件匹配(700m CPU,200Mi 内存)。注意 status.containerStatuses[0].restartCount
(应该为 0)。
现在,将 CPU 请求和限制增加到 800m
。使用带有 --subresource resize
命令行参数的 kubectl patch
。
kubectl patch pod resize-demo --subresource resize --patch \
'{"spec":{"containers":[{"name":"pause", "resources":{"requests":{"cpu":"800m"}, "limits":{"cpu":"800m"}}}]}}'
# 替代方法:
# kubectl -n qos-example edit pod resize-demo --subresource resize
# kubectl -n qos-example apply -f <updated-manifest> --subresource resize --server-side
说明:
--subresource resize
命令行参数要求 kubectl
客户端版本为 v1.32.0 或更高。
较早版本会报告 invalid subresource
错误。
在应用补丁后再次检查 Pod 状态:
kubectl get pod resize-demo --output=yaml --namespace=qos-example
你应该看到:
spec.containers[0].resources
现在显示cpu: 800m
。status.containerStatuses[0].resources
也显示cpu: 800m
,表明节点上的调整已成功。status.containerStatuses[0].restartCount
保持为0
,因为 CPU 的resizePolicy
是NotRequired
。
示例 2:调整内存并重启
现在,将同一个 Pod 的内存增加到 300Mi
。
由于内存的 resizePolicy
是 RestartContainer
,容器将会重启。
kubectl patch pod resize-demo --subresource resize --patch \
'{"spec":{"containers":[{"name":"pause", "resources":{"requests":{"memory":"300Mi"}, "limits":{"memory":"300Mi"}}}]}}'
在应用补丁后立即检查 Pod 状态:
kubectl get pod resize-demo --output=yaml
你现在应该观察到:
spec.containers[0].resources
显示memory: 300Mi
。status.containerStatuses[0].resources
也显示memory: 300Mi
。status.containerStatuses[0].restartCount
增加到1
(如果之前发生过重启,可能会更多), 表明容器已重启以应用内存变更。
故障排查:不可行的调整请求
接下来,尝试请求不合理的 CPU 数量,例如 1000 个完整核心(写作 "1000"
而不是 "1000m"
毫核),这很可能超出节点容量。
# 尝试使用过大的 CPU 请求进行补丁
kubectl patch pod resize-demo --subresource resize --patch \
'{"spec":{"containers":[{"name":"pause", "resources":{"requests":{"cpu":"1000"}, "limits":{"cpu":"1000"}}}]}}'
查询 Pod 的详细信息:
kubectl get pod resize-demo --output=yaml
你会看到表明问题的变更:
spec.containers[0].resources
反映了期望状态(cpu: "1000"
)。- Pod 添加了一个
type: PodResizePending
和reason: Infeasible
的条件。 - 状况的
message
会解释原因(Node didn't have enough capacity: cpu, requested: 800000, capacity: ...
) - 重要的是,
status.containerStatuses[0].resources
仍然显示之前的值(cpu: 800m
,memory: 300Mi
), 因为不可行的调整未被 Kubelet 应用。 - 由于这次失败的尝试,
restartCount
不会发生变化。
要修复这个问题,你需要使用可行的资源值再次对 Pod 进行补丁。
清理
删除 Pod:
kubectl delete pod resize-demo
接下来
对于应用开发人员
对于集群管理员
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.