技术博客

K8s In-Place Pod 资源调整与 Sidecar 容器 GA:不重启修改 CPU/内存实战

Kubernetes v1.36 让两个期待已久的功能更加成熟:In-Place Pod Resource Resize(不重启 Pod 直接调整 CPU/内存配额)和 Sidecar 容器生命周期管理(解决日志采集/服务网格 Agent 的启动和退出顺序问题)。本文结合实际场景讲解两个功能的使用方法、配置细节、与 HPA/VPA 结合的资源管理方案,以及已知限制。

KubernetesIn-Place ResizeSidecarVPA资源管理云原生v1.36

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 保证启动和退出的顺序。

两个功能都已趋于稳定,可以在生产环境中逐步启用。