Kubernetes之DaemonSet

DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个 Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。

使用 DaemonSet 的一些典型用法:

  • 运行集群存储 daemon,例如在每个 Node 上运行 glusterd、ceph。
  • 在每个 Node 上运行日志收集 daemon,例如fluentd、logstash。
  • 在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、collectd、Datadog 代理、New Relic 代理,或 Ganglia gmond。

创建DaemonSet:

以下是DaemonSet的示例spec文件,运行fluentd-elasticsearch image:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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: k8s.gcr.io/fluentd-elasticsearch:1.20
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 #k8s将会给应用发送SIGTERM信号,可以用来正确、优雅地关闭应用,默认为30秒
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers

以上DaemonSet中没有RestartPolicy字段,默认为Always。如果有的话,必需将值设置成Always,否则在创建时出现不可用错误。

DaemonSet同样会受到Taint(污点)的抵制,如果不在Pod中配置匹配的Toleration(容忍度),那么DaemonSet不会在拥有Taint(污点)属性的node上部署Pod。上例中有如下内容:

1
2
3
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule

原因就是系统默认为master节点增加了 “node-role.kubernetes.io/master”的Taint(污点),以抵制普通Pod部署在Master节点上,为了使master成为专用节点而添加的Taint(污点)。因为我们预期上例DaemonSet在集群内全局部署,因此需要加入相匹配的Toleration(容忍度)这样Master节点也会部署DaemonSet。

仅在相同的 Node 上运行 Pod:

如果指定了 .spec.template.spec.nodeSelector,则DaemonSet只会在满足条件的node上部署pod。 类似这种情况,可以指定 .spec.template.spec.affinity,然后 DaemonSet Controller 将在能够与 Node Affinity 匹配的节点上创建 Pod。 如果根本就没有指定,则 DaemonSet Controller 将在所有节点上创建 Pod。
总之,可以通过Taint(污点)、Toleration(容忍度)、Affinity(亲和力)、node label控制DaemonSet部署pod的节点范围。

系统如何调度DaemonSet pod(自1.12起默认禁用):

默认情况下DaemonSet在创建pod时如果为其增加.spec.template.spec.nodeName字段,也就是说所创建的pod运行在那个节上在创建阶段就已经确定,所以DaemonSet中的pod实际上没有接受kubernetes scheduler的调度,它不需要调度,因此产生以下两个特性:

  • DaemonSet中的pod不遵从节点的unreachable条件,也就是即使节点被系统判定为不可达,DaemonSet仍然试图在其上部署pod。
  • 在集群引导阶段,即使kubernetes scheduler还没有部署生效,DaemonSet仍然可以将pod部署到集群中的任何节点,此特性主要是在集群引导阶段使用。

因为DaemonSet不同于常规pod的调度特性,它带来两个问题:

  • pod行为不一致。普通pod被创建以后等待调度的阶段称为pending,因为DaemonSet中的pod无需调度,因而无此状态,用户会因此产生迷惑。
  • pod优先级特性由kubernetes scheduler实现,DaemonSet无此特性。当系统打开pod优先级功能时,pod优先级特性会被DaemonSet忽略,DaemonSet控制器将自己做出调度决策。

为了解决以上两个问题可以使用ScheduleDaemonSetPods,ScheduleDaemonSetPods允许您使用默认调度器(而不是DaemonSet controller)调度DaemonSets,方法是向DaemonSet pods添加NodeAffinity项,而不是.spec.template.spec.nodeName。然后使用默认调度程序将pod绑定到目标主机。如果DaemonSet pod的节点关联性已经存在,则替换它。DaemonSet controller仅在创建或修改DaemonSet pods时执行这些操作,并且对DaemonSet的spec.template不做任何更改。

1
2
3
4
5
6
7
8
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: metadata.name
operator: In
values:
- target-host-name

其中”target-host-name”就是原来.spec.nodeName的值,这样pod就会被kubernetes scheduler调度。通过以上操作解决了上述两个问题。但DaemonSet的调度有自己因有的特性,在上文中提到的“不受节点unreachable条件限制”,为了使DaemonSet在使用kubernetes scheduler时仍然保持此特性需要打开集群的”TaintNodesByCondition”特性。

如果DaemonSet使用主机网络那么必需在DaemonSet中添加如下的Toleration:

1
node.kubernetes.io/network-unavailable:NoSchedule

DaemonSet自动添加的Toleration:

系统在某此条件下会自动为节点添加Taint,比如硬盘不足、网络不可达等,以阻止新pod往不满足条件的节点上调度。但DaemonSet的目的是在全部有资格的node上部署,不希望被这种Taint打断,因经系统也默认为DaemonSet上的pod添加Toleration。如下表:

容忍度 Effect 版本 描述
node.kubernetes.io/not-ready NoExecute 1.13+ 当存在诸如网络分区之类的节点问题时,不会驱逐DaemonSet pod。
node.kubernetes.io/unreachable NoExecute 1.13+ 当存在诸如网络分区之类的节点问题时,不会驱逐DaemonSet pod。
node.kubernetes.io/disk-pressure NoSchedule 1.8+
node.kubernetes.io/memory-pressure NoSchedule 1.8+
node.kubernetes.io/unschedulable NoExecute 1.12+ DaemonSet pods可以通过默认调度程序容忍不可调度的属性。
node.kubernetes.io/unreachable NoExecute 1.12+ 使用主机网络的DaemonSet pod可以通过默认调度程序容忍网络不可用的属性。

对 DaemonSet 执行回滚

找到想要 DaemonSet 回滚到的历史版本(revision)
列出 DaemonSet 的所有版本:

1
kubectl rollout history daemonset <daemonset-name>

该命令返回 DaemonSet 版本列表:

1
2
3
4
5
daemonsets "<daemonset-name>"
REVISION CHANGE-CAUSE
1 ...
2 ...
...

执行以下命令,来查看指定版本的详细信息:

1
kubectl rollout history daemonset <daemonset-name> --revision=1

该命令返回相应版本的详细信息:

1
2
3
4
5
6
7
8
9
10
daemonsets "<daemonset-name>" with revision #1
Pod Template:
Labels: foo=bar
Containers:
app:
Image: ...
Port: ...
Environment: ...
Mounts: ...
Volumes: ...

回滚到指定版本

1
2
# 在 --to-revision 中指定您从步骤 1 中获取的版本序号
kubectl rollout undo daemonset <daemonset-name> --to-revision=<revision>

如果成功,命令会返回:

1
daemonset "<daemonset-name>" rolled back

如果 --to-revision 参数未指定,将选中最近的版本。

观察 DaemonSet 回滚进度
kubectl rollout undo daemonset 向服务器表明启动 DaemonSet 回滚。 真正的回滚是在服务器端异步完成的。

执行以下命令,来观察DaemonSet回滚进度:

1
kubectl rollout status ds/<daemonset-name> 

回滚完成时,输出形如:

1
daemonset "<daemonset-name>" successfully rolled out

Kubernetes之DaemonSet
https://system51.github.io/2019/08/23/Kubernetes-DaemonSet/
作者
Mr.Ye
发布于
2019年8月23日
许可协议