在Kubernetes中, 更新configmap及secret是无法自动更新POD的, 如果需要配置生效, 需要重新部署,这无疑会增加多余操作, 刚好最近在测试gitlab的CI/CD, 需要这个自动能力,调研了以下几种可用方式.
为何configmap/secret更新后POD不自动重启
configmap/secret更新后大约10s后会更新到pod中, 但是pod无法感应到configmap/secret的更新, 这其实是由于configmap/secret机制的, 因为获取configmap是否更新的操作是由kubelet是定时(以一定的时间间隔)同步Pod和缓存中的configmap内容的,且三种Manager更新缓存中的configmap内容可能会有延迟,所以,当我们更改了configmap的内容后,真正反映到Pod中可能要经过syncFrequency + delay这么长的时间, 因此无法做到立即重启pod, 所以把重启的操作交给操作人员来决定.
由于configmap与secret的机制相差无几, 因此下面的例子以configmap为例, secret只不过加密的configmap,原理一样.
要实现cm/secret更新后自动重启Pod, 一般分为以下几种方法:
- 配置文件inotifywatch
- 发送重启信号
- Sidecar
- reloader
选型的话最好就是不需要业务端支持.不可能实现这个还需要改造一堆代码,不现实
发送信号
这个一般都需要应用程序本身支持接收信号量机制, 比如nginx就支持使用HUP
来做热重启
这个因为涉及到需要修改业务代码, 一般就不会考虑
Inotifywatch检查
可以使用脚本来watch配置文件是否存在更新,
Github有现成的可用,主要实现用了inotifywatch 参考这里configmap-auto-reload
sidecar
这个github也有现成的方案, prometheus跟alertmanager的热重启用的就是这个sidecar
可以参考configmap-reload
reloader
这个开源库使用的是kubernetes的list/watch机制来监听指定configmap的变化来执行滚动升级操作
实现可以参考reloader
最终经过一番测试之后,选择了reloader, 用这个实现pod的自动重启对业务来说影响最小.
reloader使用
这里使用一个例子: 使用了两个configmap,一个挂载到了容器的env, 一个挂载到了容器的配置文件.
1 | apiVersion: v1 |
1 | apiVersion: v1 |
1 | apiVersion: apps/v1 |
更新configmap后会发现pod很快就重启了
从reloader中看到以下日志:
进入容器看到env更新了
configmap配置文件也对应更新了
完美
Reloader源码分析
再来看deployment的yaml文件,会发现在容易的env中新增了两个环境变量(因为有两个configmap)
核心思路就是通过计算新的configmap与旧的configmap的哈希值是否有变化,源代码核心的就是这一段了:
1 | func updateContainers(upgradeFuncs callbacks.RollingUpgradeFuncs, item interface{}, config util.Config, autoReload bool) constants.Result { |
gitlab CI/CD中实现重启
当然,如果结合gitlab的CI/CD功能,因为可以在gitlab-ci.yml
中写脚本,就很容易用以下两种方式同样可以实现
- 当更新configmap时,只进行pod重启, 可以使用patch增加一个环境变量:
1 | kubectl patch deployment <deployment-name> -p '{"spec":{"template":{"spec":{"containers":[{"name":"<container-name>","env":[{"name":"RESTART_TIME","value":"'$(date +%s)'"}]}]}}}}' |
- 或者直接在CI计算一个configmap文件的md5或者hash值, 然后将这个值传入deployment, 这样也会使pod重启.
1 | ... |