ConfigMap更新Pod不重启?3种方案彻底解决

6 阅读1分钟

最近在做一个微服务配置中心迁移的项目时,我们团队遇到了一个让人头疼的问题:把应用配置从Apollo迁移到Kubernetes原生的ConfigMap之后,每次修改配置,Pod里的应用程序死活不生效。运维同事一脸困惑地问我:"ConfigMap我确实改了啊,kubectl get configmap看到的内容也是新的,为什么应用还是读的旧配置?"

这个问题,相信不少在生产环境中使用Kubernetes的团队都踩过。今天就把我们从发现问题到最终落地的完整过程分享出来,包括三种主流的滚动更新方案,以及我们最终的选型思路。

一、症状描述:ConfigMap改了,Pod纹丝不动

先还原一下现场。我们的Deployment大概长这样:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    spec:
      containers:
      - name: user-service
        image: registry.cn-hangzhou.aliyuncs.com/myteam/user-service:v1.2.0
        envFrom:
        - configMapRef:
            name: user-service-config
        volumeMounts:
        - name: config-volume
          mountPath: /etc/config
      volumes:
      - name: config-volume
        configMap:
          name: user-service-config

我们通过kubectl edit configmap user-service-config修改了数据库连接池的最大连接数,从50改成了100。改完之后,用kubectl describe configmap确认内容已经更新。但是进到Pod里一看,环境变量还是旧的,挂载的文件虽然过了一会儿更新了,但应用程序根本没有重新读取。

那么问题来了:Kubernetes为什么不在ConfigMap更新后自动重启Pod?

二、原因分析:这是设计,不是Bug

很多人第一反应是"这是不是Kubernetes的Bug",其实不是。这是Kubernetes的刻意设计,原因有两点:

第一,ConfigMap的更新和Pod的生命周期是解耦的。Kubernetes的设计哲学是声明式的,ConfigMap是一个独立的资源对象,它的变更不会自动触发Deployment的滚动更新。只有Pod Template(即.spec.template)发生变化时,Deployment控制器才会创建新的ReplicaSet并执行滚动更新。

第二,自动重启可能带来灾难性后果。想象一下,如果ConfigMap一改,所有引用它的Pod同时重启,在生产环境中这可能意味着服务中断。Kubernetes把这个决策权留给了用户。

根据Kubernetes官方文档的说明,以环境变量方式引用的ConfigMap数据,在Pod运行期间永远不会更新。以Volume挂载方式引用的数据,kubelet会周期性同步(默认约60秒),但应用程序是否重新读取文件,取决于应用自身的实现。

简单总结一下两种引用方式的行为差异:

  • envFrom/env方式:Pod运行期间完全不更新,必须重建Pod
  • volume挂载方式:文件内容会自动同步(有延迟),但应用不一定会重新加载

我们的应用是Spring Boot项目,启动时读取一次配置文件,运行期间不会主动监听文件变化。所以即使Volume里的文件更新了,应用也感知不到。

三、三种滚动更新方案详解

明确了问题根源之后,我们调研了业界主流的三种方案。下面逐一展开。

方案一:kubectl rollout restart(手动触发)

这是最简单直接的方式。Kubernetes 1.15版本之后,kubectl提供了rollout restart命令,可以在不修改任何配置的情况下触发Deployment的滚动更新。

操作步骤:

# 第一步:更新ConfigMap
kubect
...