Kubernetes - DaemonSet
Table of Contents
DaemonSet 保证在所有的(或者一些)节点上运行一个 Pod 副本。只要节点添加到集群时,会将 Pods 添加到他们上, 节点从集群移除时,这些 Pods 会被垃圾回收。删除 DaemonSet 会清除它们创建的 Pod。
一些典型的 DaemonSet 应用场景:
- 运行集群的存储守护进程,比如在每一个节点上都运行
glusterd
,ceph
。 - 运行集群每个节点上的日志收集,比如
fluentd
或者filebeat
。 - 运行集群每个节点的监控,比如 Promethues Node Exporter
1. 书写 DaemonSet 规范
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: # 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/master effect: NoSchedule containers: - name: fluentd-elasticsearch image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2 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
1.1. Pod Tempate
.spec.template
是 .spec
必填字段之一,是一个 pod template。除了 apiVersion
和 kind
之外,其它的跟 Pod
的架构完全相同。
- 除了 Pod 的必填字段之外,DaemonSet 中的 Pod template 必须指定适当的标签(见 Pod Selector)。
- DaemonSet 的 Pod 模板必须要有 重启策略,等于
Always
,或者不指定,默认值也是Always
。
1.2. Pod Selector
.spec.selector
字段是一个 pod 选择器,和 Job 的 .spec.selector
类似。它是一个对象由两个字段组成:
matchLabels
和 ReplicationController 中的.spec.selector
相同。matchExpressions
允许通过指定键,值列表来构建更复杂的选择器。
当两个都指定时,需要同时满足条件(与运算)。如果指定了 .spec.selector
,必须要与 .spec.template.metadata.labels
相匹配。如果不匹配,将会被 API 拒绝。
同样,不可以创建与测标签相匹配的 Pod(不管是另外的 DaemonSet 还是其它的控制器)。
Kubernetes 1.8 以后,这个字段是你必须要指定的,留空不再会设置默认值。而且一旦创建之后,就不允许修改了。
2. Daemon Pods 是如何调度的
2.1. 默认的调度器调度
DaemonSet 保证所有合适的节点运行一个 Pod 副本。一般来说,节点上运行的 Pod 都是由集群的调度器来选择的。 但是,DaemonSet 的 Pods 是由 DaemonSet 控制器创建和选择的,就引入了以下问题:
- 不一致的 Pod 行为:普通的 Pods 等待调度会处于
Pending
状态,但是 DaemonSet Pod 不会被创建为Pending
状态。 这可能会让人感到迷惑。 - Pod 抢占 由默认调度器处理。当抢占功能打开之后,DaemonSet 控制器将不会考虑 Pod 优先级和抢占策略来自己做出决定。
通过添加 NodeAffinity
到 DaemonSet 取代 .spec.nodeName
,允许你使用默认的调度器而不是 DaemonSet 控制器来调度。
然后默认的调度器来将 pod 绑定到目标主机。DaemonSet 控制器只执行 DaemonSet 的创建和修改操作,并且不会修改 DaemonSet
的 spec.template
。
nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchFields: - key: metadata.name operator: In values: - target-host-name
此外, node.kubernetes.io/unschedulable:NoSchedule
容忍会自动添加到 DaemonSet Pods 中。默认的调度器当调度 DaemonSet
Pods 时,会自动忽略 unschedulable
节点。
2.2. Taints and Tolerations
Although Daemon Pods respect taints and tolerations, the following tolerations are added to DaemonSet Pods automatically according to the related features.
Toleration Key | Effect | Version | Description |
---|---|---|---|
node.kubernetes.io/not-ready | NoExecute | 1.13+ | DaemonSet pods will not be evicted when there are node problems such as a network partition. |
node.kubernetes.io/unreachable | NoExecute | 1.13+ | DaemonSet pods will not be evicted when there are node problems such as a network partition. |
node.kubernetes.io/disk-pressure | NoSchedule | 1.8+ | |
node.kubernetes.io/memory-pressure | NoSchedule | 1.8+ | |
node.kubernetes.io/unschedulable | NoSchedule | 1.12+ | DaemonSet pods tolerate unschedulable attributes by default scheduler. |
node.kubernetes.io/network-unavailable | NoSchedule | 1.12+ | DaemonSet pods, who uses host network, tolerate network-unavailable attributes by default scheduler. |
3. 与 Daemon Pods 通信
- Push 用来将更新发送给另外一个服务,比如统计数据。它们没有 clients。
- NodeIP and Known Port DaemonSet 中的 Pod 可以使用
hostPort
,以便可以通过节点 IP 来访问 Pod。客户端以某种方式 知道节点 IP 列表,然后通过端口进行通信。 - DNS 使用相同的 pod 选择器创建 headless service,然后使用 =endpoint* 资源发现 DaemonSet 或者从 DNS 中检索多个 A 记录
- Service 相同的 pod 选择器创建 service,然后使用 service 随机访问一个节点上的 pod。
4. 更新 DaemonSet
当节点 labes 被修改之后,DaemonSet 立即将 Pod 添加到新匹配的节点,然后从不匹配的节点删除 Pod。
你可以修改 DaemonSet 的 Pod,但是不允许更新所有字段,而且下一次节点创建时还会使用原始的模板。
你可以删除 DaemonSet。如果你使用 kubeclt
指定了 -cascade=false
,Pods 会保留在节点上。
如果你随后用相同的选择器创建了新的 DaemonSet,新的 DaemonSet 会使用已经存在的 Pods。如果 Pod 需要替换,DaemonSet
会根据它的 updateStrategy
对其进行替换。
你可以在 DaemonSet 上执行 滚动更新。
5. 替代品
5.1. Init 脚本
你可以直接在节点上运行守护进程(比如使用 init
, upstartd
, systemd
)。这样也是可以的。但是使用 DaemonSet 有如下好处:
- 和应用程序相同的方式监控和管理守护程序的日志。
- 守护进程和应用程序使用相同的配置语言和工具(比如:Pod 模板,
kubectl
)。 - 在具有资源限制的容器中运行守护进程可以增加守护进程和应用容器之间的隔离。但是,这也可以通过容器运行守护进程也行 (比如,直接使用 Docker 启动)。
5.2. 裸 Pods(Bare Pods)
可以指定节点直接创建 Pod。然而,DaemonSet 会替换由于任何原因(例如在节点故障或者破坏性节点维护,比如内核升级的情况下) 删除或终止的 Pod。因为你应该使用 DaemonSet 而不是创建单个 Pod。
5.3. 静态 Pods
通过向 kubelet 监听的目录写入文件来创建 Pod,称之为静态 Pods。不像 DaemonSet,静态 Pods 不能使用 kubectl 或其它的 kubernetes API 客户端管理。静态 pod 不依赖于 apiserver,使得它们在集群引导的情况下很有用。
另外,静态 Pods 可能在将来会被弃用。