K8s In-Place Pod 资源调整与 Sidecar 容器 GA:不重启修改 CPU/内存实战
Kubernetes v1.36 让两个期待已久的功能更加成熟:In-Place Pod Resource Resize(不重启 Pod 直接调整 CPU/内存配额)和 Sidecar 容器生命周期管理(解决日志采集/服务网格 Agent 的启动和退出顺序问题)。本文结合实际场景讲解两个功能的使用方法、配置细节、与 HPA/VPA 结合的资源管理方案,以及已知限制。
Kubernetes 长期以来有两个让运维人员头疼的问题:
一是修改 Pod 的 CPU/内存限制必须重建 Pod——即便只是调整一个数字,Deployment 也会触发滚动更新,正在处理的请求会中断。
二是Sidecar 容器的生命周期难以控制——日志采集 Agent 可能在应用写完最后一条日志之前就退出了;Job 完成后因为 Sidecar 还活着导致 Job 一直 hang。
v1.36 对这两个问题给出了更成熟的答案。
In-Place Pod Resource Resize
核心原理
传统方式(重建 Pod):
修改 Deployment resources → 触发滚动更新 → 旧 Pod 逐步被新 Pod 替换
→ 每次调整都有 Pod 重建和调度开销
→ 有状态应用(数据库/缓存)重建代价大
In-Place Resize 方式:
直接 patch Pod 的 resources 字段(通过 /resize subresource)
→ Kubelet 更新 cgroup 限制
→ 不需要重建、不需要重调度
→ 正在处理的请求不中断
配置方法
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
# resizePolicy 控制每种资源调整时是否需要重启
resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired # CPU 调整:不需要重启容器
- resourceName: memory
restartPolicy: RestartContainer # 内存调整:需要重启容器
resizePolicy 的两个选项:
NotRequired:尽量不重启(能原地调整就原地调整)
RestartContainer:调整后重启容器(适合内存不能动态扩展的应用)
默认行为(不写 resizePolicy):
CPU:NotRequired
内存:RestartContainer
实际操作
# 查看当前 Pod 资源配置
kubectl get pod myapp -o jsonpath='{.spec.containers[0].resources}'
# 方式一:kubectl patch(通过 resize subresource)
kubectl patch pod myapp --subresource=resize -p '{
"spec": {
"containers": [{
"name": "app",
"resources": {
"requests": {"cpu": "800m", "memory": "768Mi"},
"limits": {"cpu": "1500m", "memory": "1.5Gi"}
}
}]
}
}'
# 方式二:kubectl edit(会自动走 resize subresource)
kubectl edit pod myapp
# 查看调整状态
kubectl describe pod myapp | grep -A 10 "Containers:"
# 关注 allocatedResources 字段,显示实际分配的资源
# 查看 resize 状态条件
kubectl get pod myapp -o jsonpath='{.status.conditions}' | jq '.[] | select(.type == "PodResizePending" or .type == "PodResizeInProgress")'
与 VPA 结合使用
# VerticalPodAutoscaler 开启 InPlaceOrRecreate 模式
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: myapp-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
updatePolicy:
updateMode: InPlaceOrRecreate # 优先原地调整,无法原地时才重建
resourcePolicy:
containerPolicies:
- containerName: app
minAllowed:
cpu: "200m"
memory: "256Mi"
maxAllowed:
cpu: "4"
memory: "8Gi"
InPlaceOrRecreate 模式的工作逻辑:
VPA 计算出推荐资源值
→ 先尝试 In-Place Resize
→ 如果 In-Place 无法满足(比如节点资源不足需要重调度)
→ 才触发 Pod 重建
对比旧的 updateMode: Auto:
Auto 直接重建,没有原地调整的尝试
InPlaceOrRecreate 减少了不必要的重建
限制和注意事项
当前限制:
✗ 静态 Pod 不支持(/etc/kubernetes/manifests/ 里的 Pod)
✗ Ephemeral Container 不支持
✗ 调整后资源如果超过节点可用量,Pod 进入 PodResizePending 状态
✗ 某些应用的内存无法动态调整(需要用 RestartContainer 策略)
监控建议:
关注 Pod 的 allocatedResources vs resources.requests 差值
差值过大说明有 resize 被 pending,需要扩容节点
Sidecar 容器生命周期管理
为什么需要原生 Sidecar 支持
传统方案的问题(普通容器 + initContainer):
问题1:Sidecar 可能先于主应用启动
initContainer 必须运行完才能启动主容器
普通容器没有顺序保证
→ 日志 Agent 在应用还没起来时就开始发送请求 → 报错
问题2:Sidecar 可能先于主应用退出
K8s 停止 Pod 时,所有容器同时收到 SIGTERM
Sidecar 可能在主应用处理完最后一批请求之前就退出
→ 最后一段日志丢失
问题3:Job 完成后 Pod 无法退出
Job 的主容器完成后退出(exit 0)
但 Sidecar 还在运行
→ Pod 一直不退出 → Job Controller 认为 Job 未完成
原生 Sidecar 的声明方式
apiVersion: batch/v1
kind: Job
metadata:
name: data-processing-job
spec:
template:
spec:
restartPolicy: Never
initContainers:
# Sidecar 以 initContainer 形式声明,加上 restartPolicy: Always
- name: log-agent
image: fluent/fluent-bit:3.2
restartPolicy: Always # 关键:这使它成为 Sidecar
volumeMounts:
- name: varlog
mountPath: /var/log/app
env:
- name: FLUSH_INTERVAL
value: "1"
# 普通 initContainer(没有 restartPolicy: Always)
# 正常顺序运行,完成后退出
- name: db-migration
image: flyway:latest
command: ["flyway", "migrate"]
containers:
- name: worker
image: data-worker:latest
volumeMounts:
- name: varlog
mountPath: /var/log/app
volumes:
- name: varlog
emptyDir: {}
生命周期保证
启动顺序(有了原生 Sidecar 后):
1. Sidecar(initContainers + restartPolicy: Always)启动
2. Sidecar 变为 Ready
3. 普通 initContainers 按顺序执行
4. 所有 initContainers 完成后,主容器启动
退出顺序(有了原生 Sidecar 后):
1. 主容器退出(正常退出或收到 SIGTERM)
2. Sidecar 收到 SIGTERM(此时主容器已退出)
3. Sidecar 有时间处理剩余数据(比如刷新日志缓冲区)
4. Sidecar 退出
5. Pod 退出
Job 完成行为:
主容器(worker)退出 exit 0 → Job 视为完成
Sidecar 收到退出信号,正常退出
Pod 完整退出 → Job Controller 记录完成
→ 不再有 Job hang 的问题
典型使用场景
场景一:日志采集(Fluent Bit + 应用容器)
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
initContainers:
- name: fluent-bit # Sidecar:日志采集
image: fluent/fluent-bit:3.2
restartPolicy: Always
volumeMounts:
- name: logs
mountPath: /var/log/app
- name: fluent-bit-config
mountPath: /fluent-bit/etc
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: logs
mountPath: /var/log/app
volumes:
- name: logs
emptyDir: {}
- name: fluent-bit-config
configMap:
name: fluent-bit-config
场景二:服务网格(Envoy Sidecar 手动注入)
# 在 Istio 自动注入不可用时,手动声明 Envoy Sidecar
spec:
initContainers:
- name: istio-proxy
image: docker.io/istio/proxyv2:1.23
restartPolicy: Always
args:
- proxy
- sidecar
securityContext:
allowPrivilegeEscalation: false
runAsUser: 1337
两个功能的组合使用
典型场景:有状态应用(如 Redis)+ 日志采集
Pod 结构:
Sidecar:redis-exporter(采集 Redis 指标,随 Redis 生命周期)
Sidecar:log-agent(采集日志,随应用生命周期)
主容器:redis
资源调整:
白天流量高峰:扩大 Redis 内存 limit(In-Place Resize)
夜间低峰:收缩内存,释放节点资源给其他服务
结合 VPA + InPlaceOrRecreate:
VPA 自动根据实际使用量推荐资源值
InPlaceOrRecreate 尽量不重启 Redis
→ 动态资源管理,对外无感知
小结
In-Place Pod Resizing 解决了“调整资源必须重启”的历史痛点,在有状态应用和 AI 推理服务场景下尤其有价值。结合 VPA 的 InPlaceOrRecreate 模式,可以实现真正无感知的资源动态调整。
原生 Sidecar 支持让日志采集、服务网格代理、配置热加载等场景的生命周期管理变得可靠——不再依赖 preStop hook 或 sleep 等 workaround,而是通过标准 API 保证启动和退出的顺序。
两个功能都已趋于稳定,可以在生产环境中逐步启用。
