3.5 控制器——DaemonSet(守護線程集)
每個DaemonSet可以確保某些甚至全部節點運行一個Pod的副本,當node加入集羣時,Pod就會加入這些節點,同樣的,當節點從集羣中移除時,這些pods被垃圾回收。刪除一個DaemonSet將會清理由其創建的Pod。DaemonSet典型的應用場景有:
- 運行羣集存儲守護程序,比如每個節點上的
glusterd
和ceph
; - 在每個節點上運行的日誌收集守護程序,比如
fluentd
和logstash
; - 在每個節點上運行節點監測守護程序,比如
Prometheus Node Exporter
和Sysdig Agent
以及collectd
等等;
在一個簡單的例子中,一個覆蓋所有節點的守護進程集將用於每種類型的守護進程。複雜的設置可能在單獨一種Daemon中使用多個DaemonSet,但可能帶有不同的標識、不同的存儲等。
【DaemonSet的規約Spec】
DaemonSet需要的字段有apiVersion
、kind
、metadata
,除此外,也需要.spec
的內容:
- Pod模板,即
.spec.template
是.spec
中必要的字段,除非它是嵌套的沒有apiVersion
或kind
。此外,DaemonSet中的Pod模板需要指定標籤pod selector,同時也DaemonSet中的Pod模板也需要重啓策略RestartPolicy
,這個值總是爲Always
(或者直接不指定,默認也是Always
); - 標籤選擇器Pod Selector,
.spec.selector
Job中選擇器一樣,和之前的一樣,必須指定一個Pod的選擇器和.spec.template
匹配,Pod選擇器如果不配置將不會有默認值,選擇器默認值與kubectl apply不兼容。同樣的,一旦DaemonSet被創建,.spec.selector
將不能改變,如果改變將會導致之前的Pod變成遊離(即沒有DaemonSet管理了),.spec.selector
字段由matchLabels
(和ReplicationController中的.spec.selector
一樣)和matchExpressions
(它允許通過指定key值和value值以及操作符創建更加複雜的選擇器組,即多個選擇器組成)組成。 - 節點選擇器:
.spec.template.spec.nodeSelector
,如果指定了這個值,DaemonSet控制器將會在和 node selector 匹配的節點上創建Pod;同樣的如果指定了.spec.template.spec.affinity
,DaemonSet控制器將會在和node.affinity匹配的節點上創建Pod;但如果都不指定,DaemonSet將會在所有節點(包括Master節點)上創建Pod。
注:關於上述的第二點,.spec.selector
如果指定,那必須和.spec.template.metadata.labels
相匹配,否則請求將會被API拒絕;此外,通常不應再通過其他的控制器創建標籤與此選擇器匹配的任何pods,否則DaemonSet會認爲這些Pod不是自己創建的,而是其他的控制器創建的。
下面是一個ES的DaemonSet配置daemonset.yaml
:
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: gcr.io/fluentd-elasticsearch/fluentd:v2.5.1
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:kubectl apply -f https://k8s.io/examples/controllers/daemonset.yaml
,在各個節點拉取對應的鏡像(這裏使用的是我自己的阿里雲的鏡像,已公開大家可以公用)並修改tag:
docker pull registry.cn-shanghai.aliyuncs.com/fluentd-elasticsearch/fluentd:v2.5.1
docker tag registry.cn-shanghai.aliyuncs.com/fluentd-elasticsearch/fluentd:v2.5.1 gcr.io/fluentd-elasticsearch/fluentd:v2.5.1
docker rmi registry.cn-shanghai.aliyuncs.com/fluentd-elasticsearch/fluentd:v2.5.1
注意修改docker的驅動爲cgroupfs
!
【Daemon Pod 的調度】
正常情況下,Pod運行的機器(節點)是由K8S的調度器選擇的,但由DaemonSet控制器創建的Pod是已經選擇了機器(在Pod創建時通過.spec.nodeName
指定),DaemonSet確保所有符合條件的節點都運行pod的副本。但1.12之後的版本中可以使用ScheduleDaemonSetPods
使用默認的K8S調度器進行調度,在DaemonSet的Pod中使用NodeAffinity
替換.spec.nodeName
,默認的調度器用於將Pod綁定到目標host。如果匹配node affinity的DaemonSet Pod 已經存在則替換。DaemonSet控制器僅會在創建、修改DaemonSet Pod時纔會操作,其不會對DaemonSet的spec.template
有任何改變。
【和Daemon Pod通信】
與DaemonSet中的pods通信的一些可能模式:
- Push:DaemonSet中的Pod被配置成將更新發送到另一個服務,比如數據庫的狀態,它們沒有客戶端;
- NodeIP和Known Port:DaemonSet中的Pod可以使用
hostPort
,因此那些Pod可以通過node IP獲取,客戶端並不知到節點IP的列表,只是按約定知道端口; - DNS:使用一些Pod selector創建一個Headless Service,然後使用
endpoints
資源發現DaemonSet或從DNS檢索多個記錄; - Service:使用一個Pod Selector創建一個Service,使用Service到達隨機節點上的Daemon;
【更新DaemonSet】
如果node的標籤改變了,DaemonSet將會及時的將這些Pod添加到新匹配到的節點,並且從新的未匹配的節點上刪除Pod。可以更改DaemonSet創建的Pod,但不允許更新Pod的所有字段,DaemonSet控制器將會在新節點創建時使用原始模板。
刪除DaemonSet,如果使用kubectl
指定了--cascade=false
,Pod將會遺留在節點上,可以使用不同的模板創建新的DaemonSet,新的DaemonSet將自識別節點上標籤匹配的Pod。儘管pod模板不匹配,但它不會修改或刪除它們,需要通過刪除Pod或Node來強制創建一個新的Pod。