Kubernetes 配置熱更新之 reloader
背景
配置中心問題:
對于在云原生中配置中心,例如configmap和secret對象,雖然可以進行直接更新資源對象。
- 對于引用這些有些不變的配置是可以打包到鏡像中的,那可變的配置呢?
- 信息泄漏,很容易引發(fā)安全風(fēng)險,尤其是一些敏感信息,比如密碼、密鑰等。
- 每次配置更新后,都要重新打包一次,升級應(yīng)用。鏡像版本過多,也給鏡像管理和鏡像中心存儲帶來很大的負擔。
- 定制化太嚴重,可擴展能力差,且不容易復(fù)用。
使用方式:
Configmap或Secret使用有兩種方式,一種是env系統(tǒng)變量賦值,一種是volume掛載賦值,env寫入系統(tǒng)的configmap是不會熱更新的,而volume寫入的方式支持熱更新!
- 對于env環(huán)境的,必須要滾動更新pod才能生效,也就是刪除老的pod,重新使用鏡像拉起新pod加載環(huán)境變量才能生效。
- 對于volume的方式,雖然內(nèi)容變了,但是需要我們的應(yīng)用直接監(jiān)控configmap的變動,或者一直去更新環(huán)境變量才能在這種情況下達到熱更新的目的。
- 應(yīng)用不支持熱更新,可以在業(yè)務(wù)容器中啟動一個sidercar容器,監(jiān)控configmap的變動,更新配置文件,或者也滾動更新pod達到更新配置的效果。
解決方案
ConfigMap 和 Secret 是 Kubernetes 常用的保存配置數(shù)據(jù)的對象,你可以根據(jù)需要選擇合適的對象存儲數(shù)據(jù)。通過 Volume 方式掛載到 Pod 內(nèi)的,kubelet 都會定期進行更新。但是通過環(huán)境變量注入到容器中,這樣無法感知到 ConfigMap 或 Secret 的內(nèi)容更新。
目前如何讓 Pod 內(nèi)的業(yè)務(wù)感知到 ConfigMap 或 Secret 的變化,還是一個待解決的問題。但是我們還是有一些 Workaround 的。
如果業(yè)務(wù)自身支持 reload 配置的話,比如nginx -s reload,可以通過 inotify 感知到文件更新,或者直接定期進行 reload(這里可以配合我們的 readinessProbe 一起使用)。
如果我們的業(yè)務(wù)沒有這個能力,考慮到不可變基礎(chǔ)設(shè)施的思想,我們是不是可以采用滾動升級的方式進行?沒錯,這是一個非常好的方法。目前有個開源工具Reloader,它就是采用這種方式,通過 watch ConfigMap 和 Secret,一旦發(fā)現(xiàn)對象更新,就自動觸發(fā)對 Deployment 或 StatefulSet 等工作負載對象進行滾動升級。
reloader簡介
reloader簡介:
Reloader 可以觀察 ConfigMap 和 Secret 中的變化,并通過相關(guān)的 deploymentconfiggs、 deploymentconfiggs、 deploymonset 和 statefulset 對 Pods 進行滾動升級。
reloader安裝:
helm安裝:
helm repo add stakater https://stakater.github.io/stakater-charts
helm repo update
helm install stakater/reloader
Kustomize:
kubectl apply -k https://github.com/stakater/Reloader/deployments/kubernetes
資源清單安裝:
kubectl apply -f https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml
# 在此安裝在common-service 名稱空間下,
[root@master reloader]# kubectl apply -f reloader.yaml
clusterrole.rbac.authorization.k8s.io/reloader-reloader-role created
clusterrolebinding.rbac.authorization.k8s.io/reloader-reloader-role-binding created
deployment.apps/reloader-reloader created
serviceaccount/reloader-reloader created
[root@master reloader]# kubectl get all -n common-service
NAME READY STATUS RESTARTS AGE
pod/reloader-reloader-66d46d5885-nx64t 1/1 Running 0 15s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/reloader-reloader 1/1 1 1 16s
NAME DESIRED CURRENT READY AGE
replicaset.apps/reloader-reloader-66d46d5885 1 1 1 16s
配置忽略:
reloader能夠配置忽略cm或者secrets資源,可以通過配置在reader deploy中的spec.template.spec.containers.args,如果兩個都忽略,那就縮小deploy為0,或者不部署reoader。
Args | Description |
–resources-to-ignore=configMaps | To ignore configMaps |
–resources-to-ignore=secrets | To ignore secrets |
配置:
自動更新:
reloader.stakater.com/search 和 reloader.stakater.com/auto 并不在一起工作。如果你在你的部署上有一個 reloader.stakater.com/auto : “ true”的注釋,該資源對象引用的所有configmap或這secret的改變都會重啟該資源,不管他們是否有 reloader.stakater.com/match : “ true”的注釋。
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
spec:
template: metadata:
制定更新:
指定一個特定的configmap或者secret,只有在我們指定的配置圖或秘密被改變時才會觸發(fā)滾動升級,這樣,它不會觸發(fā)滾動升級所有配置圖或秘密在部署,后臺登錄或狀態(tài)設(shè)置中使用。
一個制定deployment資源對象,在引用的configmap或者secret種,只有reloader.stakater.com/match: "true"為true才會出發(fā)更新,為false或者不進行標記,該資源對象都不會監(jiān)視配置的變化而重啟。
kind: Deployment
metadata:
annotations:
reloader.stakater.com/search: "true"
spec:
template:
cm配置:
kind: ConfigMap
metadata:
annotations:
reloader.stakater.com/match: "true"
data:
key: value
指定cm:
如果一個deployment掛載有多個cm或者的場景下,我們只希望更新特定一個cm后,deploy發(fā)生滾動更新,更新其他的cm,deploy不更新,這種場景可以將cm在deploy中指定為單個或著列表實現(xiàn)。
例如:一個deploy有掛載nginx-cm1和nginx-cm2兩個configmap,只想nginx-cm1更新的時候deploy才發(fā)生滾動更新,此時無需在兩個cm中配置注解,只需要在deploy中寫入configmap.reloader.stakater.com/reload:nginx-cm1,其中nginx-cm1如果發(fā)生更新,deploy就會觸發(fā)滾動更新。
如果多個cm直接用逗號隔開。
# configmap對象
kind: Deployment
metadata:
annotations:
configmap.reloader.stakater.com/reload: "nginx-cm1"
spec:
template: metadata:
# secret對象
kind: Deployment
metadata:
annotations:
secret.reloader.stakater.com/reload: "foo-secret"
spec:
template: metadata:
無需在cm或secret中添加注解,只需要在引用資源對象中添加注解即可。
測試驗證
deploy:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
# reloader.stakater.com/auto: "true"
reloader.stakater.com/search: "true"
labels:
run: nginx
name: nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
run: nginx
template:
metadata:
labels:
run: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
# 必須匹配volumes的名稱,定義configmap
- name: nginx-cm
mountPath: /data/cfg
readOnly: true
volumes:
# 定義邏輯卷的名稱
- name: nginx-cm
configMap:
# 使用configmap資源的名稱
name: nginx-cm
items:
# 使用configmap中到那個key
- key: config.yaml
# 使用configmap中到key映射到容器中到文件名稱
path: config.yaml
mode: 0644
configmap:
apiVersion: v1
data:
config.yaml: |
# project settings
# go2cloud_api service config
DEFAULT_CONF:
port: 8888
# data disk api
UNITTEST_TENCENT_ZONE: ap-chongqing-1
kind: ConfigMap
metadata:
name: nginx-cm
annotations:
reloader.stakater.com/match: "true"
測試:
[root@master ns-default]# kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-68c9bf4ff7-9gmg6 1/1 Running 0 10m
[root@master ns-default]# kubectl get cm
NAME DATA AGE
nginx-cm 1 28m
# 更新cm內(nèi)容
[root@master ns-default]# kubectl edit cm nginx-cm
configmap/nginx-cm edited
# 查看po發(fā)生了滾動更新,重新加載配置文件
[root@master ns-default]# kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-66c758b548-9dllm 0/1 ContainerCreating 0 4s
nginx-68c9bf4ff7-9gmg6 1/1 Running 0 10m
注意事項
- reloader為全局資源對象,建議部署在一個公共服務(wù)的ns下,然后其他ns也可以正常使用reloader特性。
- Reloader.stakater.com/auto : 如果配置configmap或者secret在 deploymentconfigmap/deployment/daemonsets/Statefulsets
- secret.reloader.stakater.com/reload 或者 configmap.reloader.stakater.com/reload 注釋中被使用,那么 true 只會重新加載 pod,不管使用的是 configmap 還是 secret。
- reloader.stakater.com/search 和 reloader.stakater.com/auto 不能同時使用。如果你在你的部署上有一個 reloader.stakater.com/auto : “ true”的注釋,那么它總是會在你修改了 configmaps 或者使用了機密之后重新啟動,不管他們是否有 reloader.stakater.com/match : “ true”的注釋。
反思
Reloader通過 watch ConfigMap 和 Secret,一旦發(fā)現(xiàn)對象更新,就自動觸發(fā)對 Deployment 或 StatefulSet 等工作負載對象進行滾動升級。
如果我們的應(yīng)用內(nèi)部沒有去實時監(jiān)控配置文件,利用該方式可以非常方便的實現(xiàn)配置的熱更新。