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。