6.深入k8s:守護進程DaemonSet

轉載請聲明出處哦~,本篇文章發佈於luozhiyun的博客:https://www.luozhiyun.com

img

最近也一直在加班,處理項目中的事情,發現問題越多越是感覺自己的能力不足,希望自己能多學點。我覺得人生的意義就是在於能夠不斷的尋求突破吧。

這篇文章會講DaemonSet和Job與CronJob一起。在講其中某一塊內容的時候,我會將一些其他內容也關聯上,讓讀者儘可能的看明白些,然後這篇開始我會開始加入一些主要源碼的分析。

如果覺得我講的不錯的,可以發個郵件鼓勵一下我噢~

Daemon Pod有三個主要特徵:

  1. 這個 Pod 運行在 Kubernetes 集羣裏的每一個節點(Node)上;
  2. 每個節點上只有一個這樣的 Pod 實例;
  3. 當有新的節點加入 Kubernetes 集羣后,該 Pod 會自動地在新節點上被創建出來;而當舊節點被刪除後,它上面的 Pod 也相應地會被回收掉。

Daemon Pod可以運用在網絡插件的Agent組件上、日誌組件、監控組件等。

創建一個DaemonSet

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: mirrorgooglecontainers/fluentd-elasticsearch:v2.4.0
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

這個 DaemonSet,管理的是一個 fluentd-elasticsearch 鏡像的 Pod。通過 fluentd 將 Docker 容器裏的日誌轉發到 ElasticSearch 中。

這個DaemonSet中使用 selector 選擇管理所有攜帶了 name=fluentd-elasticsearch 標籤的 Pod。然後使用template定義了pod模板。

然後在運行這個DaemonSet後,一個叫DaemonSet Controller的控制器會從 Etcd 裏獲取所有的 Node 列表,然後遍歷所有的 Node。然後檢查Node上是不是又name=fluentd-elasticsearch 標籤的 Pod 在運行。

如果沒有這樣的pod,那麼就創建一個這樣的pod;如果node上這樣的pod數量大於1,那麼就會刪除多餘的pod。

運行:

$ kubectl apply -f ds-els.yaml

然後查看運行情況:

$ kubectl get pod -n kube-system -l name=fluentd-elasticsearch

NAME                          READY   STATUS    RESTARTS   AGE
fluentd-elasticsearch-nwqph   1/1     Running   0          4m11s

由於我這是單節點,所以只有一個pod運行了。

然後查看一下 Kubernetes 集羣裏的 DaemonSet 對象:

$ kubectl get ds -n kube-system fluentd-elasticsearch
NAME                    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
fluentd-elasticsearch   1         1         1       1            1           <none>          27m

然後我們來稍微看一下源碼,k8s是通過daemon_controller裏面的manage方法來管理Pod刪減操作的:

manage方法裏面首先會獲取daemon pod 與 node 的映射關係,然後判斷每一個 node 是否需要運行 daemon pod,然後遍歷完node之後將需要創建的Pod列表和需要刪除Pod的列表交給syncNodes執行。

func (dsc *DaemonSetsController) manage(ds *apps.DaemonSet, nodeList []*v1.Node, hash string) error { 
	// 獲取已存在 daemon pod 與 node 的映射關係
	nodeToDaemonPods, err := dsc.getNodesToDaemonPods(ds)
	if err != nil {
		return fmt.Errorf("couldn't get node to daemon pod mapping for daemon set %q: %v", ds.Name, err)
	}
 
	// 判斷每一個 node 是否需要運行 daemon pod
	var nodesNeedingDaemonPods, podsToDelete []string
	for _, node := range nodeList {
		nodesNeedingDaemonPodsOnNode, podsToDeleteOnNode, err := dsc.podsShouldBeOnNode(
			node, nodeToDaemonPods, ds)

		if err != nil {
			continue
		}
		//將需要刪除的Pod和需要在某個節點創建Pod存入列表中
		nodesNeedingDaemonPods = append(nodesNeedingDaemonPods, nodesNeedingDaemonPodsOnNode...)
		podsToDelete = append(podsToDelete, podsToDeleteOnNode...)
	}
 
	podsToDelete = append(podsToDelete, getUnscheduledPodsWithoutNode(nodeList, nodeToDaemonPods)...)
 
	//爲對應的 node 創建 daemon pod 以及刪除多餘的 pods
	if err = dsc.syncNodes(ds, podsToDelete, nodesNeedingDaemonPods, hash); err != nil {
		return err
	}

	return nil
}

下面我們看一下podsShouldBeOnNode方法是如何判斷哪些Pod需要創建和刪除的:

在podsShouldBeOnNode會調用nodeShouldRunDaemonPod方法來判斷該node是否需要運行 daemon pod 以及能不能調度成功,然後獲取該node上有沒有創建該daemon pod。

通過判斷shouldRun, shouldContinueRunning將需要創建 daemon pod 的 node 列表以及需要刪除的 pod 列表獲取到,shouldSchedule 主要檢查 node 上的資源是否充足,shouldContinueRunning 默認爲 true。

func (dsc *DaemonSetsController) podsShouldBeOnNode(
	node *v1.Node,
	nodeToDaemonPods map[string][]*v1.Pod,
	ds *apps.DaemonSet,
) (nodesNeedingDaemonPods, podsToDelete []string, err error) {
	//判斷該 node 是否需要運行 daemon pod 以及能不能調度成功
	shouldRun, shouldContinueRunning, err := dsc.nodeShouldRunDaemonPod(node, ds)
	if err != nil {
		return
	}
	//獲取該節點上的指定ds的pod列表
	daemonPods, exists := nodeToDaemonPods[node.Name]

	switch {
	//如果daemon pod是可以運行在這個node上,但是還沒有創建,那麼創建一個
	case shouldRun && !exists: 
		nodesNeedingDaemonPods = append(nodesNeedingDaemonPods, node.Name)
	//	需要 pod 一直運行
	case shouldContinueRunning: 
		var daemonPodsRunning []*v1.Pod
		for _, pod := range daemonPods {
			if pod.DeletionTimestamp != nil {
				continue
			}
			//如果 pod 運行狀態爲 failed,則刪除該 pod
			if pod.Status.Phase == v1.PodFailed { 
				...
				podsToDelete = append(podsToDelete, pod.Name)
			} else {
				daemonPodsRunning = append(daemonPodsRunning, pod)
			}
		} 
		//如果節點上已經運行 daemon pod 數 > 1,保留運行時間最長的 pod,其餘的刪除
		if len(daemonPodsRunning) > 1 {
			sort.Sort(podByCreationTimestampAndPhase(daemonPodsRunning))
			for i := 1; i < len(daemonPodsRunning); i++ {
				podsToDelete = append(podsToDelete, daemonPodsRunning[i].Name)
			}
		}
	//	如果 pod 不需要繼續運行但 pod 已存在則需要刪除 pod
	case !shouldContinueRunning && exists: 
		for _, pod := range daemonPods {
			if pod.DeletionTimestamp != nil {
				continue
			}
			podsToDelete = append(podsToDelete, pod.Name)
		}
	}

	return nodesNeedingDaemonPods, podsToDelete, nil
}

DaemonSet 對象的滾動更新和StatefulSet是一樣的,可以通過 .spec.updateStrategy.type 設置更新策略。目前支持兩種策略:

  • OnDelete:默認策略,更新模板後,只有手動刪除了舊的 Pod 後纔會創建新的 Pod;
  • RollingUpdate:更新 DaemonSet 模版後,自動刪除舊的 Pod 並創建新的 Pod。

具體的滾動更新可以在:5.深入k8s:StatefulSet控制器回顧一下。

僅在某些節點上運行 Pod

如果想讓DaemonSet在某個特定的Node上運行,可以使用nodeAffinity。

如下:

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: metadata.name
            operator: In
            values:
            - node1

上面的這個pod,我們指定了nodeAffinity,matchExpressions的含義是這個pod只能運行在metadata.name是node1的節點上,operator=In表示部分匹配的意思,除此之外operator還可以指定:In,NotIn,Exists,DoesNotExist,Gt,Lt等。

requiredDuringSchedulingIgnoredDuringExecution表明將pod調度到一個節點必須要滿足的規則。除了這個規則還有preferredDuringSchedulingIgnoredDuringExecution將pod調度到一個節點可能不會滿足規則

當我們使用如下命令的時候:

$ kubectl edit pod -n kube-system fluentd-elasticsearch-nwqph

...
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchFields:
          - key: metadata.name
            operator: In
            values:
            - node1
...

可以看到DaemonSet自動幫我們加上了affinity來進行節點調度。我們也可以自己在yaml裏面設置affinity,以此來覆蓋系統默認的配置。

Taints and Tolerations

在k8s集羣中,我們可以給Node打上污點,這樣可以讓pod避開那些不合適的node。在node上設置一個或多個Taint後,除非pod明確聲明能夠容忍這些污點,否則無法在這些node上運行。

例如:

kubectl taint nodes node1 key=value:NoSchedule

上面給node1打上了一個污點,這將阻止pod調度到node1這個節點上。

如果要移除這個污點,可以這麼做:

kubectl taint nodes node1 key:NoSchedule-

如果我們想讓pod運行在有污點的node節點上,我們需要在pod上聲明Toleration,表明可以容忍具有該Taint的Node。

比如我們可以聲明如下pod:

apiVersion: v1
kind: Pod
metadata:
  name: pod-taints
spec:
  tolerations:
  - key: "key"
    operator: "Equal"
    value: "value"
    effect: "NoSchedule"
  containers:
    - name: pod-taints
      image: busybox:latest

operator在這裏可以是Exists表示無需指定value,值爲Equal表明需要指明和value相等。

NoSchedule表示如果一個pod沒有聲明容忍這個Taint,則系統不會把該Pod調度到有這個Taint的node上。除了NoSchedule外,還可以是PreferNoSchedule,表明如果一個Pod沒有聲明容忍這個Taint,則系統會盡量避免把這個pod調度到這一節點上去,但不是強制的。

在上面的fluentd-elasticsearch DaemonSet 裏,我們加上了

tolerations:
- key: node-role.kubernetes.io/master
  effect: NoSchedule

是因爲在默認情況下,Kubernetes 集羣不允許用戶在 Master 節點部署 Pod。因爲,Master 節點默認攜帶了一個叫作node-role.kubernetes.io/master的“污點”。所以,爲了能在 Master 節點上部署 DaemonSet 的 Pod,我就必須讓這個 Pod“容忍”這個“污點”。

Reference

https://www.cnblogs.com/breezey/p/9101677.html

https://kubernetes.io/docs/concepts/workloads/controllers/daemonset

https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/

https://kuboard.cn/learning/k8s-intermediate/workload/wl-daemonset/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章