Kubernetes 中的 ConfigMap 配置更新(续)

栏目: 编程工具 · 发布时间: 4年前

内容简介:【编者的话】之前的文章《这次碰到的问题,并不是上面的办法无法实现配置热更新,而是无法实现配置滚动发布。我们不妨看一个常见的例子,一个 Deployment 引用了一个 ConfigMap(简洁起见,删去了 selector 和 labels 等字段):这里我们采用了通过修改对象中的 PodTemplate 触发滚动更新这个方案来做 ConfigMap 的更新:

【编者的话】之前的文章《 Kubernetes Pod 中的 ConfigMap 更新 》中,我总结了三种 ConfigMap 或 Secret 的更新方法:通过 Kubelet 的周期性 Remount 做热更新,通过修改对象中的 PodTemplate 触发滚动更新,以及通过自定义 Controller 监听 ConfigMap 触发更新。但在最近的业务实践中,却碰到了这些办法都不好使的情况。这篇文章就将更为深入地讨论这个主题。 如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

问题在哪?

这次碰到的问题,并不是上面的办法无法实现配置热更新,而是无法实现配置滚动发布。我们不妨看一个常见的例子,一个 Deployment 引用了一个 ConfigMap(简洁起见,删去了 selector 和 labels 等字段):

apiVersion: apps/v1

kind: Deployment

metadata:

name: nginx-deployment

spec:

replicas: 3

template:

annotations:

  nginx-config-md5: d41d8cd98f00b204e9800998ecf8427e

spec:

  containers:

  - name: nginx

    image: nginx

    volumeMounts:

    - name: nginx-config

      mountPath: /etc/config

  volumes:

  - name: config-volume

    configMap: 

      name: nginx-config

---

apiVersion: v1

kind: ConfigMap

metadata:

name: nginx-config

data:

nginx.conf: |-

## some configurations...

这里我们采用了通过修改对象中的 PodTemplate 触发滚动更新这个方案来做 ConfigMap 的更新:

  • 每次部署时,计算 ConfigMap 的摘要(e.g. MD5),并填入 PodTemplate 的 Annotation 中;
  • 假如 ConfigMap 发生变化,则摘要也会变化[^1],此会触发一次 Deployment 的滚动更新。

现在,我们更新了一次配置,但很不幸新的配置是错误的,使用错误配置的 Pod 将无法正常工作(比如无法通过 readinessProbe 的检查)。最终,滚定更新的过程会卡住,错误的配置并不会让你的 Deployment 整个宕掉。

真的是这样吗?

并不是!

问题在这里就显现出来了,nginx-config 已经更新成了错误的值,尽管尚未重建的 Pod 目前暂且健康,但是,一旦这些 Pod 宕掉发生 Pod 重建,或者 Pod 中的容器重新读取了一次配置,这些 Pod 就会进入异常状态:整个集群现在是摇摇欲坠的。

问题的根源是,在原地更新 ConfigMap 或 Secret 时,我们没有做滚动发布,而是一次性将新配置更新到了整个集群的所有实例中,我们所谓的“滚动更新”其实是在控制各个实例何时读取新配置,但由于 Pod 随时可能挂掉重建,我们是无法做到准确控制这个过程的。

假如你认为“错误的配置”很少见,那一个更有力的例子是 StatefulSet 的灰度发布(使用 StatefulSet 的 Partition 字段控制只把部分副本更新到新的 ControllerRevision),假如我们 StatefulSet 的配置也做灰度发布,那配置更新的问题就更明显了。

显然,同样的问题对于另两种更新方案也存在。当然,这并不是说这三种方式是错误的,它们也有自己适用的场景,只是当我们需要配置文件的更新滚动发布时,它们就不再适用了。

解决方案

上述方案的问题在于原地更新,要解决这个问题,我们只需要在每次 ConfigMap 变化时,重新生成一个 ConfigMap,再更新 Deployment 使用这个新的 ConfigMap 即可。而重新生成 ConfigMap 最简单的方式就是在 ConfigMap 的命名中加上 ConfigMap 的 data 值计算出的摘要,比如:

apiVersion: v1

kind: ConfigMap

metadata:

name: nginx-config-d41d8cd98f00b204e9800998ecf8427e

data:

nginx.conf: |-

## some configurations...

事实上,ConfigMap 滚动更新(ConfigMap Rollout)是社区中历时最久而尚未解决的问题之一(#22368),到目前为止,解决这个问题的方向也正是“每次更新新建一个 ConfigMap”这种“Immutable ConfigMap”模式( 详见这条评论 )。

这个方案自然就带来两个问题:

  • 如何做到每次配置文件更新时,都创建一个新的 ConfigMap?目前社区的态度是把这一步放到 Client 来解决,比如 Kustomize 和 Helm。
  • 历史 ConfigMap 会不断积累,怎么回收?针对这点,社区希望在服务端实现一个 GC 机制来清理没有任何资源引用的 ConfigMap。

当然,把逻辑放到 Client 里势必造成重复造轮子的问题:每一个 工具 都必须实现一遍类似的逻辑。因此也有人提议通过 Snapshot 的方式把逻辑全都推到服务端,这个方向目前八字都还没一撇,我们且按下不表。至少到现在为止,在 Client 做 ConfigMap 的新建与 Deployment 等对象的更新是最成熟的 ConfigMap 滚动更新方案。

因此,我们就在最后一节来说明 Helm 和 Kustomize 里怎么实现这个方案。

Helm 和 Kustomize 的实践方式

Kustomize

Kustomize 对这个方案有内置的支持,只需要使用 configGenerator 即可:

configMapGenerator:

- name: my-configmap

files:

- common.properties

这段 yaml 在 Kustomize 中就会生成一个 ConfigMap 对象,这个对象的 data 来自于 common.properties 文件,并且 name 中会加上该文件的 SHA 值作为后缀。

在 Kustomize 的其它 layer 中,只要以 my-configmap 作为 name 引用这个 ConfigMap 即可,当最终渲染时,Kustomize 会自动进行替换操作。

Helm

首先注意,Helm 在 tips and tricks 里提到的修改 Annotation 的方案是无法做到滚动更新的,原因见第一节,假如你想要的是合理的滚动更新的话,注意不要踩到坑里去。

Helm 对于这个方案没有比较好的支持,需要依托于 named template 机制来封装一下。思路是定义一个 named template,用于渲染 ConfigMap 的 data,然后针对这个 named template 计算 SHA 值并添加到 ConfigMap 名字中。

{{/*

定义一个 Named Template,将配置文件渲染为 ConfigMap

*/}}

{{- define "my-configmap.data" -}}

config.toml: |-

{{ include (print $.Template.BasePath "/_config.toml.tpl") . | indent 2 }}

another-config.yaml: |-

{{ include (print $.Template.BasePath "/_another-config.yaml.tpl") . | indent 2 }}

{{- end -}}



{{/*

ConfigMap 部分

*/}}

apiVersion: v1

kind: ConfigMap

metadata:

name: my-configmap-{{ include "my-configmap.data" . | sha256sum | trunc 8 }}

data:

{{ include "my-configmap.data" . | indent 2 }}

---

{{/*

Deployment 部分

*/}}

apiVersion: apps/v1

kind: Deployment

metadata:

name: nginx-deployment

spec:

replicas: 3

template:

spec:

  volumes:

    - name: my-config

      configMap:

        name: my-configmap-{{ include "my-configmap.data" . | sha256sum | trunc 8 }}

...(其它字段略)

关键点只有一个,那就是把 ConfigMap 的 data 定义成 Named Template,这样就可以很容易地访问这个模板的渲染值并且计算 SHA。

当然上面只是个示例,正式写 chart 时,还要做好合理的文件切分,比如把 Named Template 放到 _helpers.tpl 文件中统一维护。

当资源名字改变后,Helm 会自动删除旧的资源,因此使用 Helm 时不必担心 ConfigMap 累计过多的问题。缺点就是我们无法脱离 Helm 去做 rollback。

结语

事实上这种从 Client 端出发的解决方案并不是很优雅,或多或少都有比较 Hack 的感觉。尽管客观情况是 ConfigMap 和 Secret 现在的用法已经完全深入到所有的 Kubernetes 使用场景中,对于如此基础的资源,很难大刀阔斧地去做改进,甚至于小修小补都是“牵一发而动全身”,需要非常慎重地去考虑。但社区的创造力是无穷的,最近又有相关的 KEP 涌现出来(尽管还很粗糙),相信在不远的将来,我们会看到更好用的 ConfigMap Rollout 管理机制。

原文链接: https://aleiwu.com/post/config ... owup/


以上所述就是小编给大家介绍的《Kubernetes 中的 ConfigMap 配置更新(续)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

热点

热点

【美】马克•舍费尔(Mark Shaefer) / 曲秋晨 / 中国人民大学出版社 / 2017-1 / 49.00

你是不是常常困惑: 我创作内容,利用社交媒体,并紧跟每一次数字营销的创新和新平台的运作,可为什么我的业务和影响力没有明显起色? 2015年至2020年间,网络信息量将增长五到十倍,信息极度过剩。如何让你的内容脱颖而出?《热点》由全球顶尖营销专家马克•舍费尔所著,详尽披露如何收获核心粉丝、形成社会认同、引爆热点,进而成功塑造伟大的企业和个人品牌。一起来看看 《热点》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试