轉載請聲明出處哦~,本篇文章發佈於luozhiyun的博客:https://www.luozhiyun.comhtml
最近也一直在加班,處理項目中的事情,發現問題越多越是感受本身的能力不足,但願本身能多學點。我以爲人生的意義就是在於可以不斷的尋求突破吧。node
這篇文章會講DaemonSet和Job與CronJob一塊兒。在講其中某一塊內容的時候,我會將一些其餘內容也關聯上,讓讀者儘量的看明白些,而後這篇開始我會開始加入一些主要源碼的分析。docker
若是以爲我講的不錯的,能夠發個郵件鼓勵一下我噢~shell
Daemon Pod有三個主要特徵:api
Daemon Pod能夠運用在網絡插件的Agent組件上、日誌組件、監控組件等。網絡
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 中。app
這個DaemonSet中使用 selector 選擇管理全部攜帶了 name=fluentd-elasticsearch 標籤的 Pod。而後使用template定義了pod模板。elasticsearch
而後在運行這個DaemonSet後,一個叫DaemonSet Controller的控制器會從 Etcd 裏獲取全部的 Node 列表,而後遍歷全部的 Node。而後檢查Node上是否是又name=fluentd-elasticsearch 標籤的 Pod 在運行。ui
若是沒有這樣的pod,那麼就建立一個這樣的pod;若是node上這樣的pod數量大於1,那麼就會刪除多餘的pod。google
運行:
$ 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
設置更新策略。目前支持兩種策略:
具體的滾動更新能夠在:5.深刻k8s:StatefulSet控制器回顧一下。
若是想讓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,以此來覆蓋系統默認的配置。
在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「容忍」這個「污點」。
http://www.javashuo.com/article/p-nkdboqbv-ke.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/