Kubernetes - DaemonSet

Table of Contents

DaemonSet 保证在所有的(或者一些)节点上运行一个 Pod 副本。只要节点添加到集群时,会将 Pods 添加到他们上, 节点从集群移除时,这些 Pods 会被垃圾回收。删除 DaemonSet 会清除它们创建的 Pod。

一些典型的 DaemonSet 应用场景:

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。除了 apiVersionkind 之外,其它的跟 Pod 的架构完全相同。

  • 除了 Pod 的必填字段之外,DaemonSet 中的 Pod template 必须指定适当的标签(见 Pod Selector)。
  • DaemonSet 的 Pod 模板必须要有 重启策略,等于 Always ,或者不指定,默认值也是 Always

1.2. Pod Selector

.spec.selector 字段是一个 pod 选择器,和 Job 的 .spec.selector 类似。它是一个对象由两个字段组成:

  • matchLabelsReplicationController 中的 .spec.selector 相同。
  • matchExpressions 允许通过指定键,值列表来构建更复杂的选择器。

当两个都指定时,需要同时满足条件(与运算)。如果指定了 .spec.selector ,必须要与 .spec.template.metadata.labels 相匹配。如果不匹配,将会被 API 拒绝。

同样,不可以创建与测标签相匹配的 Pod(不管是另外的 DaemonSet 还是其它的控制器)。

Kubernetes 1.8 以后,这个字段是你必须要指定的,留空不再会设置默认值。而且一旦创建之后,就不允许修改了。

1.3. 仅在某些节点上运行 Pod

如果指定了 .spec.template.spec.nodeSelector ,控制器只会在匹配的节点(节点选择器)上创建 Pods。 同样的,如果你指定了 .spec.template.spec.affinity ,控制只会在匹配的节点(节点亲和性)上创建 Pods。 如果你什么都不指定,那么控制器会在所有的节点上创建 Pods。

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 可能在将来会被弃用。

First created: 2020-04-11 18:20:37
Last updated: 2022-12-11 Sun 12:49
Power by Emacs 29.0.91 (Org mode 9.6.6)