Kubernetes - Configmap热更新原理

mac2024-12-14  26

GitHub地址: https://github.com/QingyaFan/container-cloud/issues/2

Kubernetes中提供configmap,用来管理应用的配置,configmap具备热更新的能力,但只有通过目录挂载的configmap才具备热更新能力,其余通过环境变量,通过subPath挂载的文件都不能动态更新。这篇文章里我们来看看configmap热更新的原理,以及为什么只有目录形式挂载才具备热更新能力。

configmap热更新原理

我们首先创建一个configmap(configmap-test.yaml)用于说明,其内容如下。我们初始化好这个configmapkubectl apply -f configmap-test.yaml。

apiVersion: v1 kind: ConfigMap metadata: name: marvel-configmap data: marvel: | { name: "iron man", skill: [ "fight", "fly" ] }

configmap资源对象会存储在etcd中,我们看下存储的是什么东东,哦,原来就是明文存储的。

[root@bogon ~]# ETCDCTL_API=3 etcdctl get /registry/configmaps/default/marvel-configmap /registry/configmaps/default/marvel-configmap k8s v1 ConfigMap� � marvel-configmapdefault"*$02d3b66f-da26-11e9-a8c5-0800275f21132����b� 0kubectl.kubernetes.io/last-applied-configuration�{"apiVersion":"v1","data":{"marval":"{\n name: \"iron man\",\n skill: [\n \"fight\", \"fly\"\n ]\n}\n"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"marvel-configmap","namespace":"default"}} zD marval:{ name: "iron man", skill: [ "fight", "fly" ] } "

接下来使用一个redis的pod来挂载这个configmap:

apiVersion: extensions/v1beta1 kind: Deployment metadata: name: redis labels: name: redis spec: strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 type: RollingUpdate template: metadata: labels: name: redis spec: containers: - image: redis:5.0.5-alpine name: redis resources: limits: cpu: 1 memory: "100M" requests: cpu: "200m" memory: "55M" ports: - containerPort: 6379 name: redis volumeMounts: - mountPath: /data name: data - mountPath: /etc/marvel name: marvel volumes: - name: data emptyDir: {} - name: marvel configMap: name: marval-configmap items: - key: marvel path: marvel restartPolicy: Always imagePullPolicy: Always

我们启动这个deploy,然后修改一下configmap,把用拳头锤别人的浩克加上,看多长时间可以得到更新。

apiVersion: v1 kind: ConfigMap metadata: name: marvel-configmap data: marvel: | { name: "iron man", skill: [ "fight", "fly" ] }, { name: "hulk", skill: [ "fist" ] }

经过测试,经过了11s时间,pod中的内容得到了更新。再把黑寡妇也加上,耗时48s得到了更新。

apiVersion: v1 kind: ConfigMap metadata: name: marvel-configmap data: marvel: | { name: "iron man", skill: [ "fight", "fly" ] }, { name: "hulk", skill: [ "fist" ] }, { name: "Black widow", skill: [ "magic" ] }

所以更新延迟不一定,为什么呢?接下来我们看下configmap热更新的原理。

是kubelet在做事

kubelet是每个节点都会安装的主要代理,负责维护节点上的所有容器,并监控容器的健康状况,同步容器需要的数据,数据可能来自配置文件,也可能来自etcd。kubelet有一个启动参数--sync-frequency,控制同步配置的时间间隔,它的默认值是1min,所以更新configmap的内容后,真正容器中的挂载内容变化可能在0~1min之后。修改一下这个值,修改为5s,然后更改configmap的数据,检查热更新延迟时间,都降低到了3s左右,但同时kubelet的资源消耗会上升,尤其运行比较多pod的node上,性能会显著下降。

怎么实现的呢

Kubelet是管理pod生命周期的主要组件,同时它也会维护pod所需的资源,其中之一就是configmap,实现定义在pkg/kubelet/configmap/中,kubelet主要是通过 configmap_manager 来管理每个pod所使用的configmap,configmap_manager有三种:

Simple ManagerTTL Based ManagerWatch Manager

默认使用 Watch Manager。其实Manager管理的主要是缓存中的configmap对象,而kubelet同步的是Pod和缓存中的configmap对象。如下图所示:

Simple Manager

Simple Manager直接封装了访问api-server的逻辑,其更新延迟(图中delay)为0。

TTL Based Manager

当pod启动或者更新时,pod 引用的缓存中的configmap都会被无效化。获取configmap时(GetObject()),先尝试从TTL缓存中获取,如果没有,过期或者无效,将会从api-server获取,获取的内容更新到缓存中,替换原来的内容。

CacheBasedManager 的定义在 pkg/kubelet/util/manager/cache_based_manager.go 中。

func NewCacheBasedManager(objectStore Store, getReferencedObjects func(*v1.Pod) sets.String) Manager { return &cacheBasedManager{ objectStore: objectStore, getReferencedObjects: getReferencedObjects, registeredPods: make(map[objectKey]*v1.Pod), } }

Watch Manager

每当pod启动或更新时,kubelet会对该 pod 新引用的所有configmap对象启动监控(watches),watch负责利用新的configmap对缓存的configmap更新或替换。

WatchBasedManager 的定义在 pkg/kubelet/util/manager/watch_based_manager.go 中。

func NewWatchBasedManager(listObject listObjectFunc, watchObject watchObjectFunc, newObject newObjectFunc, groupResource schema.GroupResource, getReferencedObjects func(*v1.Pod) sets.String) Manager { objectStore := NewObjectCache(listObject, watchObject, newObject, groupResource) return NewCacheBasedManager(objectStore, getReferencedObjects) }

总结

只有当Pod使用目录形式挂载configmap时才会得到热更新能力,其余两种使用configmap的方式是Pod环境变量注入和subPath形式。

因为kubelet是定时(以一定的时间间隔)同步Pod和缓存中的configmap内容的,且三种Manager更新缓存中的configmap内容可能会有延迟,所以,当我们更改了configmap的内容后,真正反映到Pod中可能要经过syncFrequency + delay这么长的时间。

最新回复(0)