天天看點

Kubernetes - Configmap熱更新原理

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

Kubernetes中提供configmap,用來管理應用的配置,configmap具備熱更新的能力,但隻有通過目錄挂載的configmap才具備熱更新能力,其餘通過環境變量,通過subPath挂載的檔案都不能動态更新。這篇文章裡我們來看看configmap熱更新的原理,以及為什麼隻有目錄形式挂載才具備熱更新能力。

configmap熱更新原理

我們首先建立一個configmap(configmap-test.yaml)用于說明,其内容如下。我們初始化好這個configmap

kubectl apply -f configmap-test.yaml

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

configmap資源對象會存儲在etcd中,我們看下存儲的是什麼東東,哦,原來就是明文存儲的。

[[email protected] ~]# 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對象。如下圖所示:

Kubernetes - 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

這麼長的時間。

繼續閱讀