守护服务(Daemon Service)模式允许在目标节点上放置和运行优先级高的、专注于基础设施的 Pod。它主要被管理员用来运行特定节点的 Pod,以增强 Kubernetes 平台能力。

问题描述

软件系统中守护进程的概念存在于很多层面。在操作系统层面上,守护进程是一个长期运行、自我恢复的计算机程序,作为后台进程运行。在 Unix 中,守护进程的名称以 “d” 结尾,如 httpd、named 和 sshd。在其他操作系统中,则使用服务启动的任务和幽灵作业等替代术语。

不管它们被称为什么,这些程序的共同特点是它们作为进程运行,通常不与显示器、键盘和鼠标交互,并在系统启动时启动。在应用程序层面也存在类似的概念。例如,在 JVM 中,守护线程在后台运行,为用户线程提供支持服务。这些守护线程的优先级较低,在后台运行,在应用程序的生命中没有发言权,执行的任务是垃圾收集或最终化。

同样,Kubernetes 中也有 DaemonSet 的概念。考虑到 Kubernetes 是一个分布式平台,分布在多个节点上,以管理应用 Pod 为首要目标,DaemonSet 由运行在集群节点上的 Pod 代表,并为集群的其他节点提供一些后台功能。

解决方案

ReplicaSet 和它的前身 ReplicationController 是控制结构,负责确保特定数量的 Pod 运行。这些控制器持续监控正在运行的 Pod 的列表,并确保 Pod 的实际数量总是与所需数量一致。在这方面,DaemonSet 是一个类似的控制器结构,负责确保一定数量的 Pod 总是在运行。不同的是,前两者运行特定数量的 Pod,通常是由高可用性和用户负载的应用需求驱动的,而不考虑节点数量。

另一方面,DaemonSet 在决定运行多少 Pod 实例和在哪里运行时,不受消费者负载的驱动。它的主要目的是在每个节点或特定节点上保持运行一个 Pod。接下来让我们在例 9-1 中看到这样一个 DaemonSet 的定义。

  1. # 例 9-1 DaemonSet 资源示例
  2. ---
  3. apiVersion: apps/v1
  4. kind: DaemonSet
  5. metadata:
  6. name: random-refresher
  7. spec:
  8. selector:
  9. matchLabels:
  10. app: random-refresher
  11. template:
  12. metadata:
  13. labels:
  14. app: random-refresher
  15. spec:
  16. # 只使用标签特征设置为值 hw-rng 的节点
  17. nodeSelector:
  18. feature: hw-rng
  19. containers:
  20. - image: k8spatterns/random-generator:1.0
  21. name: random-generator
  22. command:
  23. - sh
  24. - -c
  25. - >-
  26. "while true; do
  27. java -cp / RandomRunner /host_dev/random 100000;
  28. sleep 30; done"
  29. # DaemonSets 经常挂载节点的部分文件系统来执行维护操作
  30. volumeMounts:
  31. - mountPath: /host_dev
  32. name: devices
  33. volumes:
  34. - name: devices
  35. # hostPath 用于直接访问节点目录
  36. hostPath:
  37. path: /dev

考虑到这种行为,DaemonSet 的主要候选者通常是与基础设施相关的进程,如日志收集器、度量指标导出器,甚至是 kube-proxy,这些进程执行整个集群的操作。DaemonSet 和 ReplicaSet 的管理方式有很多不同,但主要有以下几点。

  • 默认情况下,DaemonSet 会在每个节点上放置一个 Pod 实例。这可以通过使用 nodeSelector 字段来控制和限制节点的子集。
  • 由 DaemonSet 创建的 Pod 已经指定了 nodeName。因此,DaemonSet 不需要 Kubernetes 调度器的存在就可以运行容器。这也允许使用 DaemonSet 来运行和管理 Kubernetes 组件。
  • 由 DaemonSet 创建的 Pod 可以在调度器启动之前运行,这使得它们可以在任何其他 Pod 放在节点上之前运行。
  • 由于没有使用调度器,节点的 unschedulable 字段不被 DaemonSet 控制器所尊重。
  • 由 DaemonSet 管理的 Pod 应该只在目标节点上运行,因此,许多控制器会以更高的优先级和不同的方式对待。例如,Descheduler 会避免驱逐这样的 Pod,集群 Autoscaler 会单独管理它们,等等。

通常情况下,一个 DaemonSet 会在每个节点或节点子集上创建一个单一的 Pod。考虑到这一点,DaemonSet 管理的 Pod 有几种方式可以到达。

  • 服务(Service):创建一个与 DaemonSet 相同的 Pod 选择器的 Service,并使用该 Service 到达一个 Daemon Pod 负载均衡到随机节点。
  • DNS:创建一个无头(Headless)服务,将相同的 Pod 选择器作为 DaemonSet,可用于从 DNS 中检索包含所有 Pod IP 和端口的多个 A 记录。
  • 带主机端口的 NodeIP:DaemonSet 中的 Pod 可以指定一个 hostPort,并通过节点 IP 地址和指定的端口成为可访问的对象。由于 hostIphostPort 以及协议的组合必须是唯一的,所以 Pod 可以被调度的地方数量是有限的。
  • 推送:DaemonSet Pod 中的应用可以将数据推送到 Pod 外部的已知位置或服务。不需要消费者到达 DaemonSet Pod。

:::info 另一种类似于 DaemonSet 运行容器的方式是通过静态 Pod(Static Pod)机制。Kubelet 除了与 Kubernetes API Server 对话并获取 Pod 清单外,还可以从本地目录中获取资源定义。这种方式定义的 Pod 只由 Kubelet 管理,并且只在一个节点上运行。API Server 并不观察这些 Pod,也没有控制器,也没有对它们进行健康检查。Kubelet 观察这样的 Pod,并在它们崩溃时重新启动它们。同样,Kubelet 也会定期扫描配置的目录,查看 Pod 定义的变化,并相应地添加或删除 Pod。 :::

:::tips 静态 Pod 可以用来分拆 Kubernetes 系统进程或其他容器的容器化版本。但 DaemonSet 与平台的其他部分集成度更好,推荐使用 DaemonSet,而不是静态 Pod。 :::

一些讨论

在本书中,我们描述的模式和 Kubernetes 特性主要是开发者而不是平台管理员使用的。DaemonSet 介于两者之间,更倾向于管理员工具箱,但我们把它包括在这里,因为它对应用开发者也有适用性。DaemonSet 和 CronJob 也是 Kubernetes 如何将 Crontab 和守护脚本等单节点概念转化为管理分布式系统的多节点集群基本要素的完美例子。这些都是开发人员也必须熟悉的新分布式概念。

参考资料