Kubernetes DaemonSet
顾名思义,DaemonSet 的主要功能是可在 K8s 集群中运行一个守护进程 Pod。DaemonSet 可确保在所有(或部分)工作节上点运行 Pod 的副本。
随着节点被添加到集群中,Pod 也被添加进去。当从集群中删除节点时,这些 Pod 会被垃圾回收。删除DaemonSet将清理其创建的 Pod。与其他编排对象不同的是,DaemonSet 开始运行的时间往往早于整个 K8s 集群出现的时间。
守护进程 Pod 具有以下特点:
- 它运行在 K8s 集群中的每个节点(大多数情况下)上
- 每个节点上只有一个这样的 Pod
- 当有新节点加入 K8s 集群时,就会在该新节点上自动创建 Pod
- 当一个节点被删除时,Pod 会相应地被回收
DaemonSet的一些典型用途:
- 在每个节点上运行集群存储守护进程,例如 ceph。
- 在每个节点上运行日志收集守护进程,例如 fluentd。
- 在每个节点上运行节点监控守护进程,例如 Prometheus node exporter。
K8s 系统守护程序集
事实上,K8s 本身就是使用 DaemonSet 来运行系统组件的。如果您运行以下命令:可以看到 K8s 集群中运行着 aws-node 和 kube-proxy 两个 DaemonSet 。
$ kubectl get daemonset -n kube-systemNAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGEaws-node 2 2 2 2 2 <none> 160dkube-proxy 2 2 2 2 2 <none> 160d
DaemonSet 定义
为了理解 DaemonSet 是如何工作的,首先需要看一下它的定义:这个 DaemonSet 管理着一个 fluentd-elasticsearch 镜像 Pod。该镜像的功能非常有用:通过 fluentd 可将 Docker 容器中的日志转发到 ElasticSearch。 需要注意的是,在 DaemonSet 上,为了防止它占用过多的主机资源,一般应该添加 resources 字段来限制它的 CPU 和内存使用。 可以看到,DaemonSet 其实和 Deployment很像,只不过没有 replicas 字段;它还使用选择器来选择和管理所有带有
apiVersion: apps/v1kind: DaemonSetmetadata:name: fluentd-elasticsearchnamespace: kube-systemlabels:k8s-app: fluentd-loggingspec:selector:matchLabels:name: fluentd-elasticsearchtemplate:metadata:labels:name: fluentd-elasticsearchspec:tolerations:# this toleration is to have the daemonset runnable on master nodes# remove it if your masters can't run pods- key: node-role.kubernetes.io/masteroperator: Existseffect: NoSchedulecontainers:- name: fluentd-elasticsearchimage: quay.io/fluentd_elasticsearch/fluentd:v2.5.2resources:limits:memory: 200Mirequests:cpu: 100mmemory: 200MivolumeMounts:- name: varlogmountPath: /var/log- name: varlibdockercontainersmountPath: /var/lib/docker/containersreadOnly: trueterminationGracePeriodSeconds: 30volumes:- name: varloghostPath:path: /var/log- name: varlibdockercontainershostPath:path: /var/lib/docker/containers
<font style="color:rgb(30, 107, 184);">name=fluentd-elasticsearch</font> 标签的 Pod。
这些 Pod 模板也是用 template 字段定义的。在该字段中,使用 fluentd-elasticsearch:2.5.2 镜像定义了一个容器,该容器挂载了两个 hostPath 类型的卷,分别对应主机的 /var/log 和 /var/lib/docker/ 容器目录。
很明显,fluentd 启动后,会从这两个目录中收集日志信息,然后转发给 ElasticSearch 保存。这样就可以通过 ElasticSearch 来轻松检索这些日志。
在所有节点上运行 Pod
那么 DaemonSet 是如何保证每个节点上只托管一个 Pod 的呢?这通常由守护进程集控制器DaemonSet Controller 处理。DaemonSet 控制器首先从 api-server 上获取所有节点的列表(从 etcd 获取数据),然后遍历所有节点,并检查当前每个节点上是否有 DaemonSet Pod 在运行。
检查结果可能有以下三种情况:- 没有这样的 Pod,就意味着是在该 Node 上创建这样的 Pod;
- 如果有这样的 Pod,但数量大于 1,则表示应该从该 Node 中删除多余的 Pod;
- 正好有 1 个这样的 Pod,说明这个节点是正常的。
但是,在 K8s 项目中,
nodeSelector:name: Node_Name
<font style="color:rgb(30, 107, 184);">nodeSelector</font> 实际上是一个会被弃用的字段 因为,现在有一个功能更全的新字段来代替它,即:<font style="color:rgb(30, 107, 184);">nodeAffinity</font>。
<font style="color:rgb(0, 0, 0);">nodeAffinity</font>
举个例子吧:
在上面的例子中,声明了一个
apiVersion: v1kind: Podmetadata:name: with-node-affinityspec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: metadata.nameoperator: Invalues:- node-name
<font style="color:rgb(30, 107, 184);">spec.affinity</font> 字段,并定义了一个 <font style="color:rgb(30, 107, 184);">nodeAffinity</font>。其中,<font style="color:rgb(30, 107, 184);">spec.affinity</font> 字段是 Pod 中与调度相关的字段。
<font style="color:rgb(30, 107, 184);">requiredDuringSchedulingIgnoredDuringExecution</font>:表示每次调度时都要考虑该<font style="color:rgb(30, 107, 184);">nodeAffinity</font>。同时,这也意味着在某些情况下 nodeAffinity 也可以设置为被忽略;- 这个 Pod 以后只被允许在”metadata.name“ 为 “node-name“ 的节点上运行。
<font style="color:rgb(30, 107, 184);">nodeAffinity</font> 定义添加到 Pod 的 API 对象中。其中,需要绑定的节点名称为当前正在遍历的节点。
容忍度(Tolerations)
另外,DaemonSet 会自动为这个 Pod 添加另一个称为 tolerations 的调度相关字段。该字段意味着这个 Pod 将“容忍(Toleration)”一些节点“污点(Taint)”。 看一个例子:这个 Toleration 的意思是:“容忍”所有标记为不可调度的“污点”的节点;“容忍”的效果是允许调度。 正常情况下,标记为不可调度的“污点”的 Node 不会有任何 Pod 被调度(效果:NoSchedule)。但是,DaemonSet 会自动将这个特殊的 Toleration 添加到托管的 Pod 中,这样这些 Pod 就可以忽略这个限制,然后确保每个节点上都会调度一个 Pod。当然,如果节点出现故障,Pod 可能启动失败,DaemonSet 会一直尝试,直到 Pod 启动成功。
apiVersion: v1kind: Podmetadata:name: with-tolerationspec:tolerations:- key: node.kubernetes.io/unschedulableoperator: Existseffect: NoSchedule
创建 DaemonSet
最后,创建这个 fluentd DaemonSet。执行以下命令:创建成功之后,可以看到如下输出:
$ kubectl create -f fluentd-elasticsearch.yaml
此时,若想通过
$ kubectl get pod -n kube-system -l name=fluentd-elasticsearchNAME READY STATUS RESTARTS AGEfluentd-elasticsearch-dqfv9 1/1 Running 0 10mfluentd-elasticsearch-pf9z5 1/1 Running 0 10m
<font style="color:rgb(30, 107, 184);">kubectl get</font> 查看 Kubernetes 集群中的 DaemonSet 对象,则:
$ kubectl get ds -n kube-system fluentd-elasticsearchNAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGEfluentd-elasticsearch 2 2 2 2 2 <none> 1h
结论
DaemonSet 只管理 Pod 对象,然后通过 **<font style="color:rgb(0, 0, 0);">nodeAffinity</font>** 和 **<font style="color:rgb(0, 0, 0);">Toleration</font>** 的小功能保证每个节点上只有一个 Pod。这个控制器的实现原理和 Deployment 类似,应该简单易懂。
