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 Manager
- TTL Based Manager
- Watch 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
這麼長的時間。