在前面介绍的文章中,我们了解到了 Kubernetes 的控制对象 Deployment 通过 ReplicaSet 管理无状态 Pod,StatefulSet管理有状态Pod。今天我们将继续了解 Kubernetes 的 DaemonSet 控制对象。
DaemonSet 的作用是在 Kubernetes集群的每个节点(Node)都有且仅运行一个 Pod。当有新的 Node 加入到集群中,也会自动的在该 Node 上创建一个Pod,反之当有Node被删除,该Node上的Pod也将会被回收。

每个 Node 上有且仅运行一个 Pod 的场景很常见:例如监控、日志插件,需要收集该Node上的监控指标、日志数据,还有一些网络代理插件、存储插件等。
与其他的控制对象一样,DaemonSet也是通过一个大的 for 循环来控制对象,可以很容易猜想到 DaemonSet 的运行机制:
从Etcd中获取当前集群的 Pod 列表;
遍历所有的Pod,检测该Pod上是否存在该Pod;
不存在,则在该节点创建该Pod;
存在,但是Pod数量大于1,删除多余的 Pod;
存在,且Pod数量为1,不做任何处理
重复 1,2步。
我们通过一个 DaemonSet 对象的 yaml 定义文件来了解 DaemonSet:
apiVersion: apps/v1kind: DaemonSetmetadata:name: logstashspec:selector:matchLabels:name: logstashtemplate:metadata:labels:name: logstashspec:tolerations:- key: node-role.kubernetes.io/mastereffect: NoSchedulecontainers:- name: logstashimage: logstash:7.8.0resources:limits:memory: 100Mirequests:cpu: 50mmemory: 100MivolumeMounts:- name: configPathmountPath: /usr/share/logstash/pipelinereadOnly: trueterminationGracePeriodSeconds: 30volumes:- name: configPathhostPath:path: /data/logstash/conf
示例中的 DaemonSet对象管理的Pod是一个logstash 应用,logstash从配置中读取需要采集的日志(容器、docker)并发送给日志存储服务(通常是 ElasticSearch)。这个配置文件通过 Volume挂载在磁盘的 data/logstash/conf 目录。
与其他控制对象一样,DaemonSet也通过 selector 选择符合携带 label 条件的 Pod。
在 template 定义的 Pod 中见识到了个新的标识 tolarations:
tolerations:- key: node-role.kubernetes.io/mastereffect: NoSchedule
DaemonSet 正是通过 tolerations 实现能够在每个 Node上创建 Pod。
Kubernetes 建议用户将 master 节点与应用节点区分开,master节点上运行的是 Kubernetes 的 APIServer、Scheduller 等构件,避免与线上应用争抢资源,所以不会让其他的 Pod调度到 master 节点,其实现方式就是给 master节点打上master 污点标签。
而在上面yaml示例中 tolarations 的 node-role.kubernetes.io/master表示容忍master 污点,使 Pod 能够在 master 节点上被创建。
同时Kubernetes还会给 Daemonset 控制的 Pod 自动添加一个 tolaration,如下:
tolerations:- key: node.kubernetes.io/unschedulableoperator: Existseffect: NoSchedule
当前 toleration 表示容忍打上了 unshedulable 的 Node ,允许该 Pod 在当前 Node上运行。unschedulable 表示状态为 Not Ready 还无法访问的 Node,而 DaemonSet 允许在Node 还无法被调度时创建 Pod。这也是许多网络插件能够在 Node被调度前在该 Node上启动的原因。
当有新的 Node 节点加入到集群,或者 DaemonSet 在进行控制循环时,需要在指定在的 Node 上创建 Pod。那么 Kubernetes 是如何指定 Pod创建的Node的呢?
在前面讲解Pod时,有提到当某个Pod被调度后,会在其描述文件中自动加上 spec.nodeSelector.nodeName 标识,表示已经被调度至某个 Node。
我们一样的可以使用 spec.nodeSelector.name 指定需要调度到的Node。但是 Kubernetes 提供了一个新的更加完善的功能来取代 nodeSelector,即 nodeAffinity。
我们来看一个 nodeAffinity的 yaml 定义:
spec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: node.nameoperator: Invalues:- node1
首先通过 nodeAffinity 的 operator可以看到其提供了更强大的逻辑运算(不仅仅只是相等逻辑)来选择 Node。Operator 包括:
In: 在values 列表中的任意一个
NotIn:不在values 列表中的任意一个
Exists:key存在
DoesNotExist:key不存在
Gt:value 进行字符串比较后较大的
Lt:value 进行字符串比较后较小的
如果nodeAffinity中nodeSelector有多个选项,节点满足任何一个条件即可;如果matchExpressions有多个选项,则节点必须同时满足这些选项才能运行pod 。
我们再来看比较长的一段 :requiredDuringSchedulingIgnoredDuringExecution 我们分两段看:
requiredDuringScheduling:表示在调度的时候一定要找到一个满足条件的 Node,找不到就不停的重试;
IgnoredDuringExecution:表示在调度完成后 Node 的标签发生变化了就不重新调度了,Pod会继续运行。
而这一长串还包括其他的值
同理,requiredDuringSchedulingRequiredDuringExecution表示:
RequiredDuringExecution:表示在调度完成后 Node 的标签发生变化了重新选择节点。
而 preferredDuringSchedulingIgnoredDuringExecution
preferredDuringScheduling:表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署。
那么 preferredDuringSchedulingRequiredDuringExecution可根据对应颜色的字体组合来说明含义。
DaemonSet 会在创建 Pod 时,自动的给该Pod加上 nodeAffinity 标识来指定运行的Pod。




