2.10 DaemonSet

2.10 DaemonSet #

DaemonSet 会在 Kubernetes 集群的每个节点上都运行一个 Pod,就好像是 Linux 系统里的 “守护进程”(Daemon)一样。

DaemonSet 和 Deployment 有很大区别,Deployment 能够创建任意多个的 Pod 实例,并且维护这些 Pod 的正常运行,保证应用始终处于可用状态。但是,Deployment 并不关心这些 Pod 会在集群的哪些节点上运行,在它看来,Pod 的运行环境与功能是无关的,只要 Pod 的数量足够,应用程序应该会正常工作。但是对一些业务比较特殊服务,它们不是完全独立于系统运行的,而是与主机存在 “绑定” 关系,必须要依附于节点才能产生价值,比如:

  • 网络应用(如 kube-proxy),必须每个节点都运行一个 Pod,否则节点就无法加入 Kubernetes 网络。

  • 监控应用(如 Prometheus),必须每个节点都有一个 Pod 用来监控节点的状态,实时上报信息。

  • 日志应用(如 Fluentd),必须在每个节点上运行一个 Pod,才能够搜集容器运行时产生的日志数据。

  • 安全应用,每个节点都要有一个 Pod 来执行安全审计、入侵检查、漏洞扫描等工作。

以上这些业务如果用 Deployment 来部署就不太合适了,因为 Deployment 所管理的 Pod 数量是固定的,而且可能会在集群里 “漂移”,但,实际的需求却是要在集群里的每个节点上都运行 Pod,也就是说 Pod 的数量与节点数量保持同步。

DaemonSet,它在形式上和 Deployment 类似,都是管理控制 Pod,但管理调度策略却不同。DaemonSet 的目标是在集群的每个节点上运行且仅运行一个 Pod

2.10.1 描述 DaemonSet #

kubectl 不提供自动创建 DaemonSet YAML 样板的功能,不过可以在 Kubernetes 的官网 https://kubernetes.io/zh/docs/concepts/workloads/controllers/daemonset/上找到 DaemonSet 的 YAML 示例,以下是一个 DaemonSet 的 YAML 描述:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: redis-ds
  labels:
    app: redis-ds

spec:
  selector:
    matchLabels:
      name: redis-ds

  template:
    metadata:
      labels:
        name: redis-ds
    spec:
      containers:
        - image: redis:5-alpine
          name: redis
          ports:
            - containerPort: 6379

DaemonSet 仅仅是在 Pod 的部署调度策略上和 Deployment 不同,其他的都是相同的,某种程度上可以把 DaemonSet 看做是 Deployment 的一个特例。

2.10.2 使用 DaemonSet #

Master 默认是不跑应用的,所以 DaemonSet 就只生成了一个 Pod,运行在了 worker 节点上,但是按照 DaemonSet 的本意,应该在每个节点上都运行一个 Pod 实例才对,但 Master 节点却被排除在外了,这是由 Kubernetes 节点的两个属性:污点(taint)和容忍度(toleration)导致的。

污点和容忍度 #

“污点” 是 Kubernetes 节点的一个属性,它的作用也是给节点 “贴标签”,但为了不和已有的 labels 字段混淆,就改成了 taint。和 “污点” 相对的,就是 Pod 的“ 容忍度”,也就是 Pod 能否 “容忍” 污点。

集群里的节点各式各样,有的节点 “纯洁无瑕”,没有 “污点”;而有的节点因为某种原因粘上了 “泥巴”,也就有了 “污点”。Pod 也脾气各异,有的 “洁癖” 很严重,不能容忍 “污点”,只能挑选 “干净” 的节点;而有的 Pod 则要求不那么高,可以适当地容忍一些小 “污点”。

Kubernetes 在创建集群的时候会自动给节点 Node 加上一些 “污点”,方便 Pod 的调度和部署。可以用 kubectl describe node 来查看 Master 和 Worker 的状态:

在 Kubernetes v1.24 中,master 节点将不再使用污点 node-role.kubernetes.io/master,而是改成 node-role.kubernetes.io/control–plane。

Master 节点默认有一个 taint,名字是 node-role.kubernetes.io/master,它的效果是 NoSchedule,也就是说这个污点会拒绝 Pod 调度到本节点上运行,而 Worker 节点的 taint 字段则是空的。通常来说 Pod 都不能容忍任何 “污点”,所以加上了 taint 属性的 Master 节点也就会无缘 Pod 了。

有 2 种方法可以让让 DaemonSet 在 Master 节点(或者任意其他节点)上运行了。

第一种方法是去掉 Master 节点上的 taint,让 Master 变得和 Worker 一样 “纯洁无瑕”,DaemonSet 自然就不需要再区分 Master/Worker。

操作 Node 上的 “污点” 属性需要使用命令 kubectl taint,然后指定节点名、污点名和污点的效果,去掉污点要额外加上一个 -。比如要去掉 Master 节点的 “NoSchedule” 效果,可以使用这条命令:

kubectl taint node master node-role.kubernetes.io/master:NoSchedule-

这种方法修改的是 Node 的状态,影响面比较大,可能会导致很多 Pod 都跑到这个节点上运行,可以选择保留 Node 的 “污点”,为需要的 Pod 添加 “容忍度”,实现精细化调度。

第二种方法是为 Pod 添加字段 tolerations,让它能够 “容忍” 某些 “污点”,就可以在任意的节点上运行。

tolerations 是一个数组,里面可以列出多个被 “容忍” 的 “污点”,需要写清楚 “污点” 的名字、效果。比较特别是要用 operator 字段指定如何匹配 “污点”,一般使用 Exists,也就是说存在这个名字和效果的 “污点”。

如果想让 DaemonSet 里的 Pod 能够在 Master 节点上运行,就要写出这样的一个 tolerations,容忍节点的 node-role.kubernetes.io/master:NoSchedule 这个污点:

tolerations:
- key: node-role.kubernetes.io/master
  effect: NoSchedule
  operator: Exists

所以现在的 DaemonSet YAML 描述文件就变成了这样:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: redis-ds
  labels:
    app: redis-ds

spec:
  selector:
    matchLabels:
      name: redis-ds

  template:
    metadata:
      labels:
        name: redis-ds
    spec:
      containers:
        - image: redis:5-alpine
          name: redis
          ports:
            - containerPort: 6379
      tolerations:
        - key: node-role.kubernetes.io/master
          effect: NoSchedule
          operator: Exists

重新 apply 部署后可以看到有两个 Pod,分别运行在 Master 和 Worker 节点上。

2.10.3 静态 Pod #

DaemonSet 是在 Kubernetes 里运行节点专属 Pod 最常用的方式,但不是唯一的方式,Kubernetes 还支持另外一种叫 “静态 Pod” 的应用部署手段。

“静态 Pod” 非常特殊,不受 Kubernetes 系统的管控,不与 apiserver、scheduler 发生关系。但既然是 Pod,也必然会 “跑” 在容器运行时上,也会有 YAML 文件来描述它,而唯一能够管理它的 Kubernetes 组件也就只有在每个节点上运行的 kubelet 。

“静态 Pod” 的 YAML 文件默认都存放在节点的 /etc/kubernetes/manifests 目录下,它是 Kubernetes 的专用目录。

如果有一些 DaemonSet 无法满足的特殊的需求,可以考虑使用静态 Pod,编写一个 YAML 文件放到这个目录里,节点的 kubelet 会定期检查目录里的文件,发现变化就会调用容器运行时创建或者删除静态 Pod。

参考 #