首页 TG成品账号购买内容详情

为什么改了配置,Pod 却没重启?Kubernetes 真相来了

2026-04-05 6 纸飞机账号购买

有不少人平常会讲“Pod重启了”,然而,于Kubernetes当中,此种说法实际上不太严谨。

鉴于众人所说的“Pod 重启”,常常混淆了两件截然不同之事:其一为,同一个 Pod 当中的容器重启了;其二为,旧的 Pod 被删掉之后,又构建了一个全新的 Pod。

本文所要讲明白说清晰的,恰恰是Kubernetes在不同场景状况之下,究竟会引发触发哪一种行为表现。本文乃是依据基于Kubernetes 1.35 GA验证通过核实没问题的生产环境内部机制指南。

用于配套的仓库,其网址是,github.com/opscart/k8s-pod-restart-mechanics。

术语问题

被工程师们常常提及的“Pod重启了”这个表述,实际上并不严谨,在Kubernetes当中,人们常说的“Pod重启”,当中通常混淆了几种全然不同的情况。

请问,能否这样表述会更准确些,即现象方面,Pod UID 出现了变化、Pod IP 出现了变化、Restart Count 出现了变化?

同一个 Pod 内部的容器进程被重新拉起

+1

容器重启

旧 Pod 被删除,控制器重新创建一个新 Pod

通常会变

新 Pod 从 0 开始

Pod 重建 / Pod 被替换

Pod 原地调整 CPU

原地 resize

Pod在原地进行内存调整,并且所采用的策略是RestartContainer。

+1

同 Pod 内容器重启

有一种极为实用的判断方式是,首先查看Pod UID是否发生了改变,接着查看restartCount是否有所增加。

核心洞察:kubelet 实际上在看什么

kubelet所观察的对象是Pod Spec,并非ConfigMap、Secret,也不是Istio的CRD。倘若Pod Spec未发生变化,那么kubelet便不会触发任何动作。

这个事实,能够对生产环境里面那多数的,“为什么我的配置没有更新”这样的排障问题作出解释。

需要注意的是:

就决策矩阵变更项而言,同 Pod 内容器,会重启吗,还是会触发 Pod 重建或者替换呢,并且这种情况是否会自动发生呢。

容器镜像

不会

会,由Deployment触发,由StatefulSet等控制器触发。

环境变量(Pod Spec 中定义)

不会自动发生

通常需要

否 —— 需要手动 rollout

ConfigMap(volume 挂载)

不会

不会

部分自动 —— 文件会更新,但是否生效取决于应用是否热加载

设置了环境变量的,从配置映射中来获取值的,配置映射(环境变量从其获取 / 值从其获取)。

不会自动发生

通常需要

否,需要通过手动进行rollout,又或者借助Reloader这一方式。

Secret(volume 挂载)

不会

不会

部分自动 —— 文件会更新,但是否生效取决于应用是否热加载

Secret(envFrom / valueFrom)

不会自动发生

通常需要

否 —— 需要手动 rollout 或借助 Reloader

不会

不会

会 —— kubelet 自动轮转

CPU resize(K8s 1.35+)

不会

不会

否 —— 手动 patch

Memory resize(K8s 1.35+)

取决于 resizePolicy

不会

否 —— 手动 patch

用于Istio服务网格的虚拟服务规则制定,以及用于目标规则设定的相关内容,其在服务治理等方面有着重要效用。

不会

不会

会 —— xDS push

NetworkPolicy

不会

不会

会 —— CNI Agent

Service ports

不会

不会

会 —— kube-proxy

RBAC

不会

不会

会 —— API Server

节点 drain / eviction

不会在原 Pod 内重启

会 —— Pod 被驱逐后由控制器补出新 Pod

向下的这张流程图形,将同一个矩阵予以转化,变成了更适宜于线上进行排障之际所运用的决策路径。

图1:完整的决策流程图——此更改是否需要重启 pod?

场景 1:ConfigMap ,为何相同的变更会呈现出两种不同的行为?

图2,ConfigMap环境变量与,卷挂载——环境变量pod冻结,卷pod通过,kubelet符号链接交换自动同步。

1)环境变量模式(envFrom / valueFrom)

当处于execve()这个操作的时候,内核会将环境变量进行复制,复制的目的地是 /proc/ ,有这样的情况发生。

关于“/environ” ,这段内存是归进程自身所拥有的,外部的系统是没有办法再去对其进行修改操作的情况下,你在更新 “ConfigMap” 之后, “kubelet” 是看不到 “Pod Spec” 所发生的变化的,在这样的情形下,于是就什么事情都不会去做了,进程会在没有期限的情况下保留旧有的值。

2)卷挂载模式

在卷挂载模式当中,kubelet 进行同步配置所采用的方式并非是“对文件内容作出修改”,而是借助原子性的符号链接切换(symlink swap):

/etc/config/
├── ..2025_12_19_11_30_00/   ← 新数据目录(由 kubelet 创建)
│   └── APP_COLOR            ← "red"
├── ..data ───────────────── ..2025_12_19_11_30_00/   ← 原子切换 symlink
└── APP_COLOR ────────────── ..data/APP_COLOR

此次符号链接切换,会于..data 之上生成 IN_CREATE ,并非在文件自身产生 IN_MODIFY。若应用于已打开的文件描述符处监听 IN_MODIFY ,那么它会全然错过此次变更。这为 nginx 在无显式 inotify 处理情况下,不会因 ConfigMap 变化而自动 reload 的缘由。

实验结果(配套仓库 01-configmap/)

ConfigMap 更新:APP_COLOR blue → red
Pod A(环境变量):      APP_COLOR=blue  ← 值被冻结,restart count: 0
Pod B(volume 挂载):   APP_COLOR=red   ← 自动同步,restart count: 0

正确的 inotify 模式:监听目录,而不是文件

watcher.Add(filepath.Dir(configPath))  // 监听 /etc/config/,能捕获 IN_CREATE
// watcher.Add(configPath)             // 会完全错过 symlink 切换
for event := range watcher.Events {
    if event.Op&fsnotify.Create == fsnotify.Create {
        reloadConfig()
    }
}

场景2:镜像进行更新,在此过程之中,有着重建、容器重启以及CrashLoop这三者之间的区别,是怎样的呢?

这三种现象看起来很像,但本质完全不同。

成功的镜像更新:Pod 被重建

BEFORE: Pod UID aaa-bbb, IP 10.244.1.5, nginx:1.25, restarts: 0
AFTER:  Pod UID xxx-yyy, IP 10.244.1.6, nginx:1.27, restarts: 0
                ↑ UID 变了 —— 这是重建,不是容器重启

图3,滚动更新流程,呈现新的ReplicaSet的创建,pod的重建,以及保留旧的RS用于回滚。

ImagePullBackOff:旧 Pod 会被保留

Old pod: Running           ← Kubernetes 会让它继续存活
New pod: ImagePullBackOff  ← 新 Pod 拉镜像失败卡住,旧 Pod 不会被删掉

依旧是那同一个 Pod,重启的次数持续不断地在增加,出现了 CrashLoopBackOff 的情况。

Pod UID:       aaa-bbb  ← 没变
Restart count: 0 → 1 → 2 → 3  ← 同一个 Pod 对象内,容器不断崩溃

诊断规则

重启计数持续地不断上升,用户标识符保持不变:此种情况就是闪退循环。

重新启动计数是0,然而用户标识符变了,这属于滚动更新,这是容器编排单元重新构建。

StatefulSet 说明

StatefulSet的相关说明是,StatefulSet这样一种情况,其Pod在镜像发生变化的时候,同样是会被重新构建的,不过序号身份也就是像pod - 0、pod - 1这种,以及PVC绑定是可以被保留下来的。容器重启语义跟Deployment是全然一致的,而身份持久化并不意味着容器是仅在原地进行重启的。

场景 3:原地资源调整(K8s 1.35 GA)

Kubernetes 1.35 使 Pod 能够在原地进行 resize 并进入 GA 阶段,CPU 以及内存均可在不重新构建 Pod 的情形下予以调整,所以,原地 resize 是否能够使用,取决于 CRI 以及节点 OS 的支持情况;文中是在 containerd 1.7+ 与 Linux cgroups v2 相配合的条件下完成验证的。容器会出现何种状况,取决于你明确界定的 resizePolicy:

resizePolicy:
- resourceName: cpu
  restartPolicy: NotRequired      # 只更新 cgroup quota,不碰进程
- resourceName: memory
  restartPolicy: RestartContainer # 在同一个 Pod 内重启容器

针对于05-resource-resize/领域所开展的实验结果,这项过程对K8s 1.35及以上版本存在要求。

CPU resize 200m → 500m (NotRequired):
  UID: d7c99204  IP: 10.244.0.7  Restarts: 0  ← 全部不变
Memory resize 256Mi → 512Mi (RestartContainer):
  UID: d7c99204  IP: 10.244.0.7  Restarts: 1
       ↑ 同一个 Pod   ↑ 同一个 IP   ↑ 这是我们的策略选择,不是 K8s 强制要求

重点:memory的默认的resizePolicy是NotRequired。要是你省略它,memory resize会无声无息地更新cgroup,而不是重启容器——你的JVM heap依旧维持旧有的大小。针对memory,务必始终明确地界定resizePolicy。

如何执行 resize

kubectl patch pod my-pod -n my-namespace \
  --subresource resize \
  -p '{"spec":{"containers":[{"name":"app","resources":{
    "requests":{"cpu":"250m","memory":"128Mi"},
    "limits":{"cpu":"500m","memory":"256Mi"}
  }}]}}'
# 注意:不要加 --type=merge,否则和 --subresource resize 一起会触发校验错误

场景 4:Istio 路由 —— 通过 xDS 实现零重启

伊斯蒂奥虚拟服务以及目标规则的变更绝不会引致容器重新启动。伊斯蒂奥德会跟每一个特使边车维持一条持久的双向gRPC流,路由更新会在毫秒级别被推送,以内存里的方式进行切换,不触及任何容器组外部基本单元,也不会去写文件。

实验结果(配套仓库 04-istio-routing/)

四个 Pod,三次路由变更:
  100% v1  →  80/20 canary  →  100% v2
Restart counts: BEFORE 0 0 0 0 / AFTER 0 0 0 0
Pod age: 三次变更过程中始终不变

场景 5:Stakater Reloader,将那需要人工操办的步骤予以自动化。

在应用借助环境变量去消费 ConfigMap 的情形下,一旦 ConfigMap 做出更新,就总是得有人去实施:kubectl rollout restart ,以此来确保应用能够同步更新,适应新的配置信息,从而保持正常、稳定的运行状态。

Reloader 的功用是将这一步骤予以自动化,它凭借 Kubernetes Watch 事件开展工作,由此可知检测近乎属于实时情形,并非采用轮询方式。

metadata:
  annotations:
    reloader.stakater.com/auto: "true"

生产环境当中存在的坑,Helm 默认的安装参数为,watchGlobally=false ,这所代表的意思是,Reloader 仅仅会监听它自身所处的 namespace,其他的 namespace 里面就算 Deployment 加上了注解,也会被悄然无声地忽略掉,不会产生报错,所以建议在安装的时候明确地加以开启。

helm install reloader stakater/reloader \
  --namespace reloader \
  --set reloader.watchGlobally=true

图 4:Stakater Reloader 内部工作流

实验得出的结果,位于配套仓库里的,是07 - stakater - reloader/。

ConfigMap 已更新,未执行 kubectl rollout restart
新 Pod APP_MESSAGE: "Hello from OpsCart v2 — auto reloaded!"
滚动容器重启被自动触发

当热加载出问题时

热加载并不总是比容器重启更安全。它有两类典型故障模式。

1)语义错误的配置被静默接受

表现为:

而要是运用“坏配置 + 容器重启”这般形式,失败常常会马上显现出来。所以。

建议: 在原子替换配置前,先做校验。

2)Envoy 悄悄拒绝 xDS 推送

被推送的是一个 RouteConfiguration,它是 Istiod 进行这项操作的结果,然而其所引用的 cluster ,这个 cluster 处于还未完成传播的状态,Envoy 会因为这样的情况拒绝这次更新,并且会继续沿用旧有的路由规则,在此过程中不会有任何 Pod event 被触发,对此给出的建议是监控 pilot_xds_push_errors,以及使用 istioctl proxy-status。

可观测性:每个运维都该知道的三个命令

 1)判断到底是“容器重启”还是“Pod 重建”
kubectl get pod  -o custom-columns=\
  "NAME:.metadata.name,UID:.metadata.uid,IP:.status.podIP,RESTARTS:.status.containerStatuses[0].restartCount"
2)查看 Pod 上的事件
kubectl describe pod  | grep -A 20 "Events:"`
 3)查看原地 resize 状态
kubectl get pod  -o jsonpath='{.status.resize}'

结论

容器重启,虽有着破坏性,可它是诚实的,其失败模式,会即刻且明显地展现出来。而hot - reload,优先着重于优化可用性,却把失败弄得更延后、更隐蔽。这两种策略,都是合理的。关键之处在于,你得有意识地去做出选择。目标可不是把重启自动化得更快。目标是要理解得足够深入呀:只有当进程确实需要死亡的时候,才去触发一次容器重启;在不需要的时候,则运用其他所有机制。

配套仓库:github.com/opscart/k8s-pod-restart-mechanics

作者是Shamsher Khan,他身为高级DevOps工程师,还是IEEE高级会员。

为什么改了配置,Pod 却没重启?Kubernetes 真相来了

相关标签: # Kubernetes # Pod重启 # 配置变更 # 容器重启 # 热加载