一. 简介
DaemonSet 的主要作用,是在 Kubernetes 集群里,运行一个 Daemon Pod。 DaemonSet 只管理 Pod 对象,然后通过 nodeAffinity 和 Toleration 这两个调度器的小功能,保证了每个节点上有且只有一个 Pod。
所以,这个 Pod 有如下三个特征:
– 这个 Pod 运行在 Kubernetes 集群里的每一个节点(Node)上。
- 每个节点上只有一个这样的 Pod 实例。
- 当有新的节点加入 Kubernetes 集群后,该 Pod 会自动地在新节点上被创建出来。
- 当旧节点被删除后,它上面的 Pod 也相应地会被回收掉。
Daemon Pod
的意义确实是非常重要的。比如的作用:
- 网络插件的 Agent 组件,都必须运行在每一个节点上,用来处理这个节点上的容器网络。
- 存储插件的 Agent 组件,也必须运行在每一个节点上,用来在这个节点上挂载远程存储目录,操作容器的 Volume 目录。
- 监控组件和日志组件,也必须运行在每一个节点上,负责这个节点上的监控信息和日志搜集。
二. nodeAffinity
我们的 DaemonSet Controller 会在创建 Pod 的时候,自动在这个 Pod 的 API 对象里,加上这样一个 nodeAffinity 定义。
2.1 场景
在这个 Pod 里,声明了一个spec.affinity
字段,然后定义了一个nodeAffinity
。其中,spec.affinity
字段,是 Pod 里跟调度相关的一个字段。
2.2 案例
比如我们创建如下的一个YAML文件:
apiVersion: v1
kind: Pod
metadata:
name: demo-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: metadata.name
operator: In
values:
- demo-node
关于上面参数的具体含义:
- requiredDuringSchedulingIgnoredDuringExecution
这个代表nodeAffinity 必须在每次调度的时候予以考虑,同时,这也意味着可以设置在某些情况下不考虑这个 nodeAffinity; - 这个 Pod,将来只允许运行在“metadata.name”是“demo-node”的节点上。
2.3 作用
在 nodeAffinity 的定义处,可以支持更加丰富的语法,比如operator: In
(即:部分匹配)。
我们的DaemonSet Controller
会在创建 Pod 的时候,自动在这个 Pod 的 API 对象里,加上这样一个nodeAffinity
定义。
三. Toleration
3.1 场景
DaemonSet 还会给这个 Pod 自动加上另外一个与调度相关的字段,叫作 tolerations。这个字段意味着这个 Pod,会“容忍”(Toleration)某些 Node 的“污点”(Taint)。
3.2 案例
apiVersion: v1
kind: Pod
metadata:
name: with-toleration
spec:
tolerations:
- key: node.kubernetes.io/unschedulable
operator: Exists
effect: NoSchedule
在正常情况下,被标记了 unschedulable
“污点”的 Node,是不会有任何 Pod 被调度上去的(effect: NoSchedule)
。可是,DaemonSet
自动地给被管理的 Pod 加上了这个特殊的 Toleration,就使得这些 Pod 可以忽略这个限制,继而保证每个节点上都会被调度一个 Pod。
当然,如果这个节点有故障的话,这个 Pod 可能会启动失败,而 DaemonSet 则会始终尝试下去,直到 Pod 启动成功。
3.3 作用
通过这样一个 Toleration,调度器在调度这个 Pod 的时候,就会忽略当前节点上的“污点”,从而成功地将网络插件的 Agent 组件调度到这台机器上启动起来。
这种机制,正是我们在部署 Kubernetes 集群的时候,能够先部署 Kubernetes 本身、再部署网络插件的根本原因:因为当时我们所创建的Weave
的 YAML,实际上就是一个 DaemonSet。
四. ControllerRevision
DaemonSet 使用 ControllerRevision,来保存和管理自己对应的“版本”。在 Kubernetes 项目里,ControllerRevision
其实是一个通用的版本管理对象。这样,Kubernetes 项目就巧妙地避免了每种控制器都要维护一套冗余的代码和逻辑的问题。
4.1 概念
Kubernetes v1.7
之后添加了一个 API 对象,名叫 ControllerRevision
,专门用来记录某种 Controller 对象的版本。
ControllerRevision 对象,实际上是在 Data 字段保存了该版本对应的完整的 DaemonSet 的 API 对象。并且,在 Annotation 字段保存了创建这个对象所使用的 kubectl 命令。
4.2 回滚原理
现在DaemonSet Controller
就可以使用这个历史 API 对象,对现有的 DaemonSet 做一次 PATCH 操作(等价于执行一次kubectl apply -f “旧的 DaemonSet
对象”),从而把这个DaemonSet
“更新”到一个旧版本。
这也是为什么,在执行完这次回滚完成后,会发现,DaemonSet 的 Revision 并不会从 Revision=2 退回到 1,而是会增加成 Revision=3。这是因为,一个新的 ControllerRevision
被创建了出来。
五. 总结
DaemonSet其实就是依靠nodeAffinity和Toleration 实现的。这是一种不需要增加设计结构,而直接使用标签等方式完成的Daemon进程。这样的结构非常符合Kubernetes的控制器模式,一切皆对象。