Kubernetes 1.35: In-Place Pod Resize é GA — Escale Verticalmente Sem Reiniciar
Atualize CPU e memória de Pods em execução sem recriá-los
O Problema: Mudanças “Simples” que Causam Disrupções
Um serviço em produção mostra throttling de CPU. A latência P95 está subindo. A solução é óbvia: aumentar o limite de CPU de 500m para 700m.
Em versões anteriores do Kubernetes, essa “mudança simples” desencadeava uma cascata de eventos indesejados:
# Mudar isto...
resources:
limits:
cpu: "500m"
# ...para isto
resources:
limits:
cpu: "700m"
Consequências no K8s ≤1.34:
- Pod completamente recriado
- Conexões ativas encerradas
- Cache em memória perdido
- Estado local eliminado
- Jobs em progresso interrompidos
Nenhum código foi alterado. Nenhum comportamento foi alterado. Apenas um número foi ajustado. Mas o sistema tratou esse ajuste como um deployment completo.
Kubernetes 1.35 muda isso fundamentalmente.
A Solução: In-Place Resource Update (GA)
A partir do Kubernetes 1.35, a atualização in-place de recursos de Pod é GA (Generally Available). Isso significa que CPU e memória podem ser modificadas em Pods em execução, e o nó aplica os novos valores sem o ciclo automático de recriação.
Características principais:
- Mudanças de CPU aplicadas em tempo real (sem restart)
- Mudanças de memória configuráveis (com ou sem restart de container)
- Pod nunca é recriado — apenas os cgroups são ajustados
- Estado, conexões e cache preservados
- Observável via status do Pod e conditions
Arquitetura: Como Funciona o Resize
O processo envolve vários componentes do Kubernetes trabalhando em coordenação:
O Novo Modelo Mental: Desired vs Actual vs Allocated
Kubernetes 1.35 introduz uma distinção importante em como os recursos são reportados:
| Campo | Descrição |
|---|---|
spec.containers[].resources |
O que o operador deseja (desired) |
status.containerStatuses[].allocatedResources |
O que o nó reservou |
status.containerStatuses[].resources |
O que o container está usando |
status.conditions |
Estado do resize (Pending, InProgress) |
status.observedGeneration |
Confirmação de que kubelet processou a mudança |
Essa visibilidade permite saber exatamente em qual estado o resize está em qualquer momento.
Configuração: resizePolicy
O comportamento do resize é configurado por tipo de recurso usando resizePolicy na spec do container:
Spec de Pod com Resize Policy
apiVersion: v1
kind: Pod
metadata:
name: app-con-resize
spec:
containers:
- name: app
image: minha-app:latest
ports:
- containerPort: 8080
resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired # CPU em tempo real
- resourceName: memory
restartPolicy: RestartContainer # Memória com restart
resources:
requests:
cpu: "300m"
memory: "256Mi"
limits:
cpu: "300m"
memory: "256Mi"
Recomendação para produção:
- CPU:
NotRequired— As mudanças de CPU são seguras de aplicar em tempo real - Memory:
RestartContainer— Mais previsível do que esperar que a app libere memória
Demo: Verificando que o Resize Funciona
Para demonstrar que o resize realmente funciona sem restart, é possível criar um servidor simples que exponha seu PID e os limites de cgroup atuais.
Servidor de Demo (Go)
package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func read(path string) string {
b, err := os.ReadFile(path)
if err != nil {
return fmt.Sprintf("unavailable (%v)", err)
}
return strings.TrimSpace(string(b))
}
func handler(w http.ResponseWriter, r *http.Request) {
pid := os.Getpid()
// cgroup v2 paths
cpuMax := read("/sys/fs/cgroup/cpu.max")
memMax := read("/sys/fs/cgroup/memory.max")
io.WriteString(w, fmt.Sprintf("pid=%d\n", pid))
io.WriteString(w, fmt.Sprintf("cpu.max=%s\n", cpuMax))
io.WriteString(w, fmt.Sprintf("memory.max=%s\n", memMax))
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("listening on :8080")
http.ListenAndServe(":8080", nil)
}
Dockerfile
FROM golang:1.23-alpine AS build
WORKDIR /src
COPY . .
RUN go build -o app .
FROM alpine:3.20
WORKDIR /app
COPY --from=build /src/app /app/app
EXPOSE 8080
ENTRYPOINT ["/app/app"]
Desplegar e Verificar
# Criar o Pod
kubectl apply -f pod-resize-demo.yaml
# Port-forward para acessar
kubectl port-forward pod/app-con-resize 8080:8080 &
# Verificar estado inicial
curl localhost:8080
# pid=7
# cpu.max=30000 100000
# memory.max=268435456
Executando o Resize
No Kubernetes 1.35, o resize é executado contra o subresource resize do Pod:
Aumentar CPU (Sem Restart)
kubectl patch pod app-con-resize --subresource resize --type merge -p '
{
"spec": {
"containers": [
{
"name": "app",
"resources": {
"requests": { "cpu": "700m" },
"limits": { "cpu": "700m" }
}
}
]
}
}'
Verificar que Funcionou
# O endpoint deve mostrar:
# - Mesmo PID (sem restart)
# - Novo valor de cpu.max
curl localhost:8080
# pid=7 <- MESMO PID
# cpu.max=70000 100000 <- NOVO LIMITE
# memory.max=268435456
# Confirmar que não houve restart
kubectl get pod app-con-resize -o jsonpath='{.status.containerStatuses[0].restartCount}'
# 0
Se o PID se mantém e cpu.max mudou, o resize in-place funcionou corretamente.
Aumentar Memória (Com Restart de Container)
Com a política RestartContainer para memória:
kubectl patch pod app-con-resize --subresource resize --type merge -p '
{
"spec": {
"containers": [
{
"name": "app",
"resources": {
"requests": { "memory": "512Mi" },
"limits": { "memory": "512Mi" }
}
}
]
}
}'
Neste caso:
restartCountserá incrementado- O PID mudará
- Mas o Pod NÃO será recriado — volumes e networking são mantidos
Observabilidade Durante o Resize
Kubernetes 1.35 fornece visibilidade do estado do resize via conditions:
kubectl describe pod app-con-resize
Conditions relevantes:
PodResizePending— O resize foi solicitado mas ainda não foi aplicadoPodResizeInProgress— O kubelet está aplicando a mudança
# Ver o estado detalhado
kubectl get pod app-con-resize -o jsonpath='{.status.conditions}' | jq
```This removes the uncertainty. There's no longer any guessing whether the change was applied — the system reports it explicitly.
---
## Limitations to Consider
The feature is powerful but has clear limits:
| Limitation | Description |
|------------|----------------|
| **QoS Class** | Cannot change post-creation (Guaranteed/Burstable/BestEffort) |
| **Init containers** | Do not support resize |
| **Ephemeral containers** | Do not support resize |
| **Sidecars** | Yes, support resize |
| **Windows Pods** | Not supported |
| **Memory decrease** | Best-effort without restart (the app must free memory) |
| **Node constraints** | Some CPU/Memory managers can block changes |
These limitations are part of what makes the feature safe. A feature that promises everything becomes dangerous. A feature that declares its limits is operable.
---
## Scheduler Protection
A valid concern: what happens if the resize is pending but the scheduler assumes it's already applied?
Kubernetes prevents this by being conservative during incomplete resizes. When scheduling, it considers the **maximum** between:
- What was requested (desired)
- What was allocated (allocated)
- What was applied (actual)
This prevents overcommit based on changes that haven't yet completed.
---
## Operational Impact
The most significant change is not technical — it's cultural.
**Before K8s 1.35:**
- Teams avoided resize until it was urgent
- Engineers over-provisioned to avoid touching resources afterward
- "Right-sizing" was a project, not a habit
- On-call delayed simple fixes out of fear of disruption
**With K8s 1.35:**
- CPU corrections without restart cost
- Faster iteration over resource configuration
- Response to throttling without maintenance window
- Resize becomes a normal operation, not an event
---
## Command Summary
```bash
# Apply CPU resize
kubectl patch pod POD_NAME --subresource resize --type merge -p '
{
"spec": {
"containers": [{
"name": "CONTAINER_NAME",
"resources": {
"requests": { "cpu": "NEW_VALUE" },
"limits": { "cpu": "NEW_VALUE" }
}
}]
}
}'
# Check resize status
kubectl describe pod POD_NAME | grep -A5 Conditions
# View current vs desired resources
kubectl get pod POD_NAME -o jsonpath='{.status.containerStatuses[0].resources}'
# Confirm there was no restart
kubectl get pod POD_NAME -o jsonpath='{.status.containerStatuses[0].restartCount}'
Conclusion
Kubernetes 1.35 solves a problem that should never have existed: the need to restart a process just because a resource limit was adjusted.
With in-place resize GA:
- CPU can be adjusted without any restart
- Memory can be configured for container restart (not Pod)
- Full observability of resize state
- Protection against overcommit during pending changes
Vertical scaling finally behaves like an adjustment, not a deployment.
Resources
Published on yoDEV.dev — The community of developers from Latin America


