DaemonSet


1 )概述

  • 为了适配于不同的应用的部署场景K8s就提出了这个 DaemonSet 顾名思义就是后台进程的意思
  • docker在运行的时候加一个参数 -d 意思就是后台运行
  • 因为我们集群里面有很多任务是必须要在后台运行的
    • 比如说,存储的进程,ceph 或 glusterd
    • ceph 是一种开源的分布式多样存储的一个开源软件
    • 它能够把集群的这些用于存储的Service以这种服务的方式跑在K8s集群上
    • 底层它是一个分布式存储这么一个架构
    • 然后另外的应务场景景就是日志, 如:logstash 或 flunentd
    • 每一种应用都会输出各种各样的日志,包括集群自己本身的日志
    • 它就适合于以这种DaemonSet的方式运行
    • 还有就是监控的场景,包括监控的节点,如:Prometheus Node Exporter 或 collectd
  • 所以,K8s所有的功能的设计围绕的都是一个主题
    • 就是让云原生的应用,微服务在 K8s 上运行的更好
    • 它所有的功能都是为了这个目的去设计的
    • 每个功能,我们都应该去了解,它为什么会出现,以及解决了什么问题

2 )应用

  • $ vi ds-demo1.yaml

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: fluentd-elasticsearch
      namespace: kube-system # 注意在命名空间里
      labels:
        k8s-app: fluentd-logging
    spec:
      selector:
        matchLabels: # DaemonSet 会通过这个 label 来寻找所有包含 fluentd-elasticsearch 名称的pod
          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.9.0
            resources:
              limits:
                memory: 200Mi
                cpu: 100m
                memory: 200Mi
            volumeMounts: # 挂载主机的一些目录
            - name: varlog
              mountPath: /var/log
            - name: varlibdockercontainers
              mountPath: /var/lib/docker/containers
              readOnly: true
          terminationGracePeriodSeconds: 30
          volumes: # pv 挂载卷
          - name: varlog
            hostPath: # 主机的路径
              path: /var/log
          - name: varlibdockercontainers
            hostPath:
              path: /var/lib/docker/containers
    
  • $ kubectl apply -f ds-demo1.yaml

    daemonset.apps/fluentd-elasticsearch created
    
  • $ kubectl -n kube-system describe daemonset fluentd-elasticsearch

    Name:           fluentd-elasticsearch
    Selector:       name=fluentd-elasticsearch
    Node-Selector:  <none>
    Labels:         k8s-app=fluentd-logging
    Annotations:    deprecated.daemonset.template.generation: 1
    Desired Number of Nodes Scheduled: 3
    Current Number of Nodes Scheduled: 3
    Number of Nodes Scheduled with Up-to-date Pods: 3
    Number of Nodes Scheduled with Available Pods: 3
    Number of Nodes Misscheduled: 0
    Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
    Pod Template:
      Labels:  name=fluentd-elasticsearch
      Containers:
      fluentd-elasticsearch:
        Image:      quay.io/fluentd_elasticsearch/fluentd:v2.9.0
        Port:       <none>
        Host Port:  <none>
        Limits:
          cpu:        100m
          memory:     200Mi
        Environment:  <none>
        Mounts:
          /var/lib/docker/containers from varlibdockercontainers (ro)
          /var/log from varlog (rw)
      Volumes:
      varlog:
        Type:          HostPath (bare host directory volume)
        Path:          /var/log
        HostPathType:
      varlibdockercontainers:
        Type:          HostPath (bare host directory volume)
        Path:          /var/lib/docker/containers
        HostPathType:
    Events:
      Type    Reason            Age    From                  Message
      ----    ------            ----   ----                  -------
      Normal  SuccessfulCreate  4m14s  daemonset-controller  Created pod: fluentd-elasticsearch-pf8d5
      Normal  SuccessfulCreate  4m14s  daemonset-controller  Created pod: fluentd-elasticsearch-nm682
      Normal  SuccessfulCreate  4m14s  daemonset-controller  Created pod: fluentd-elasticsearch-stmlc
    
  • $ kubectl get all -n kube-system

    NAME                                     READY   STATUS    RESTARTS       AGE
    pod/coredns-7f6cbbb7b8-4vjd6             1/1     Running   6 (16h ago)    6d4h
    pod/coredns-7f6cbbb7b8-h895p             1/1     Running   6 (16h ago)    6d4h
    pod/etcd-master.k8s                      1/1     Running   14 (16h ago)   6d4h
    pod/fluentd-elasticsearch-nm682          1/1     Running   0              5m44s
    pod/fluentd-elasticsearch-pf8d5          1/1     Running   0              5m44s
    pod/fluentd-elasticsearch-stmlc          1/1     Running   0              5m44s
    pod/kube-apiserver-master.k8s            1/1     Running   13 (16h ago)   6d4h
    pod/kube-controller-manager-master.k8s   1/1     Running   26 (16h ago)   6d4h
    pod/kube-proxy-78gzp                     1/1     Running   7 (16h ago)    6d4h
    pod/kube-proxy-7rwvp                     1/1     Running   7 (16h ago)    6d4h
    pod/kube-proxy-946qv                     1/1     Running   6 (16h ago)    6d4h
    pod/kube-scheduler-master.k8s            1/1     Running   25 (16h ago)   6d4h
    
    NAME               TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
    service/kube-dns   ClusterIP   10.1.0.10    <none>        53/UDP,53/TCP,9153/TCP   6d4h
    
    NAME                                   DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
    daemonset.apps/fluentd-elasticsearch   3         3         3       3            3           <none>                   5m44s
    daemonset.apps/kube-proxy              3         3         3       3            3           kubernetes.io/os=linux   6d4h
    
    NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/coredns   2/2     2            2           6d4h
    
    NAME                                 DESIRED   CURRENT   READY   AGE
    replicaset.apps/coredns-7f6cbbb7b8   2         2         2       6d4h
    
  • $ kubectl -n kube-system describe pod/fluentd-elasticsearch-stmlc

    Name:         fluentd-elasticsearch-stmlc
    Namespace:    kube-system
    Priority:     0
    Node:         master.k8s/10.211.55.13
    Start Time:   Tue, 23 Apr 2024 15:54:09 +0800
    Labels:       controller-revision-hash=6b74446c5d
                  name=fluentd-elasticsearch
                  pod-template-generation=1
    Annotations:  <none>
    Status:       Running
    IP:           10.244.0.2
    IPs:
      IP:           10.244.0.2
    Controlled By:  DaemonSet/fluentd-elasticsearch
    Containers:
      fluentd-elasticsearch:
        Container ID:   docker://f581721a1865d49242ef4359b377cb71606d2263283502cc04f4f2b7cb643d95
        Image:          quay.io/fluentd_elasticsearch/fluentd:v2.9.0
        Image ID:       docker-pullable://quay.io/fluentd_elasticsearch/fluentd@sha256:54716d825ec9791ffb403ac17a1e82159c98ac6161e02b2a054595ad01aa6726
        Port:           <none>
        Host Port:      <none>
        State:          Running
          Started:      Tue, 23 Apr 2024 15:54:55 +0800
        Ready:          True
        Restart Count:  0
        Limits:
          cpu:     100m
          memory:  200Mi
        Requests:
          cpu:        100m
          memory:     200Mi
        Environment:  <none>
        Mounts:
          /var/lib/docker/containers from varlibdockercontainers (ro)
          /var/log from varlog (rw)
          /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-z5jjq (ro)
    Conditions:
      Type              Status
      Initialized       True
      Ready             True
      ContainersReady   True
      PodScheduled      True
    Volumes:
      varlog:
        Type:          HostPath (bare host directory volume)
        Path:          /var/log
        HostPathType:
      varlibdockercontainers:
        Type:          HostPath (bare host directory volume)
        Path:          /var/lib/docker/containers
        HostPathType:
      kube-api-access-z5jjq:
        Type:                    Projected (a volume that contains injected data from multiple sources)
        TokenExpirationSeconds:  3607
        ConfigMapName:           kube-root-ca.crt
        ConfigMapOptional:       <nil>
        DownwardAPI:             true
    QoS Class:                   Guaranteed
    Node-Selectors:              <none>
    Tolerations:                 node-role.kubernetes.io/master:NoSchedule
                                node.kubernetes.io/disk-pressure:NoSchedule op=Exists
                                node.kubernetes.io/memory-pressure:NoSchedule op=Exists
                                node.kubernetes.io/not-ready:NoExecute op=Exists
                                node.kubernetes.io/pid-pressure:NoSchedule op=Exists
                                node.kubernetes.io/unreachable:NoExecute op=Exists
                                node.kubernetes.io/unschedulable:NoSchedule op=Exists
    Events:
      Type    Reason     Age    From               Message
      ----    ------     ----   ----               -------
      Normal  Scheduled  8m14s  default-scheduler  Successfully assigned kube-system/fluentd-elasticsearch-stmlc to master.k8s
      Normal  Pulling    8m12s  kubelet            Pulling image "quay.io/fluentd_elasticsearch/fluentd:v2.9.0"
      Normal  Pulled     7m28s  kubelet            Successfully pulled image "quay.io/fluentd_elasticsearch/fluentd:v2.9.0" in 44.107259915s
      Normal  Created    7m28s  kubelet            Created container fluentd-elasticsearch
      Normal  Started    7m28s  kubelet            Started container fluentd-elasticsearch
    
    • 可以看到这个pod被分配到 master.k8s 节点上了,说明 DaemonSet 可以部署到 master 节点
    • 同时,也可以部署到其他节点,它 和 Deployment 的重要区别是
      • DaemonSet 在 node 节点的进程只有一个
      • Deployment 在 node 节点的进程副本可以有很多
    • 所以,每个节点部署一个 DaemonSet 用来收集node上的所有事情

3 )DaemonSet 必须字段

  • 和所有其他 K8s 配置一样,DaemonSet 需要 apiVersion 、 kind 和 metadata 字段
  • 有关配置文件的基本信息,DaemonSet 对象的名称必须是一个合法的,DaemonSet 也需要一个.spec 配置段

4 )DaemonSet Pod模板

  • .spec 中唯一必需的字段是 .spec.template
  • .spec.template 是一个Pod 模板,除了它是嵌套的,因而不具有 apiVersion或kind字段之外,它与Pod具有相同的 schema
  • 除了Pod必需字段外,在 DaemonSet中的Pod模板必须指定合理的标签
  • 在 DaemonSet 中的 Pod 模板必须具有一个值为 Always 的 RestartPolicy, 当该值未指定时,默认是 Always

5 )DaemonSet Pod选择器

  • .spec.selector 字段表示 Pod 选择器,它与 Job 的 .spec.selector 的作用是相同的
  • 从 K8s 1.8 开始,必须指定与 .spec.template 的标签匹配的 Pod 选择器
  • 用户不指定 Pod 选择器时,该字段不再有默认值
  • 选择器的默认值生成结果与 kubectl apply 不兼容
  • 此外,一旦创建了 DaemonSet,它的 .spec.selector 就不能修改
  • 修改 Pod 选择器可能导致 Pod 意外悬浮,并且这对用户来说是费解的
  • spec.selector 是一个对象,如下两个字段组成:
    • matchLabels - 与 ReplicationController的 .spec.selector 的作用相同
    • matchExpressions - 允许构建更加复杂的选择器,可以通过指定 key、value 列表以及将 key 和 value 列表关联起来的 operator
  • 当上述两个字段都指定时,结果会按逻辑与(AND)操作处理
  • 如果指定了 .spec.selector ,必须与 .spec.template.metadata.labels 相匹配
    • 如果与后者不匹配,则 DeamonSet 会被 API 拒绝
  • 另外,通常不应直接通过另一个 DaemonSet 或另一个工作负载资源(例如 ReplicaSet) 来创建其标签与该选择器匹配的任何 Pod
  • 否则,DaemonSet 控制器会认为这些 Pod 是由它创建的, K8s 不会阻止你这样做
  • 你可能要执行此操作的一种情况是,手动在节点上创建具有不同值的 Pod 进行测试

6 )DaemonSet 仅在某些节点上运行 Pod

  • 如果指定了 .spec.template.spec.nodeSelector
  • DaemonSet 控制器将在能够与 Node 选择器 匹配的节点上创建 Pod
  • 类似这种情况,可以指定 .spec.template.spec.affinity
  • 之后 DaemonSet 控制器将在能够与节点亲和性 匹配的节点上创建 Pod
  • 如果根本就没有指定,则 DaemonSet Controller 将在所有节点上创建 Pod

DaemonSet的调度


1 )概述

  • 我们知道 DaemonSet,它是确保每个节点都运行该pod的一个副本
  • 就是说节点上的后台任务,就只能一个副本, 如果我的后台任务在每个节点上跑两个副本
  • 比如说,日志收集跑两个副本,这个数据就重复,然后呢,数据库里面会多很多冗余的记录
  • 所以,这并不是用户期望的,所以DaemonSet的一个重要的任务就是保证节点上运行一个pod
  • 现在我们看下 Daemon Pods 是如何被调度的

2 )通过默认调度器调度

  • DaemonSet 确保所有符合条件的节点都运行该 Pod 的一个副本

  • 通常,运行 Pod 的节点由 K8s 调度器选择

  • 不过,DaemonSet Pods 由 DaemonSet 控制器创建和调度

  • 这就带来了以下问题:

    • Pod 行为的不一致性:
      • 正常 Pod 在被创建后等待调度时处于 Pending 状态
      • DaemonSet Pods 创建后不会处于 Pending 状态下
      • 这使用户感到困惑。
    • [Pod 抢占] 由默认调度器处理
      • 启用抢占后,DaemonSet 控制器将在不考虑 Pod 优先级
      • 和 抢占 的情况下制定调度决策
  • ScheduleDaemonSetPods 允许您使用默认调度器而不是 DaemonSet 控制器来调度 DaemonSets

  • 方法是将 NodeAffinity 条件而不是 .spec.nodeName 条件添加到 DaemonSet Pods

  • 默认调度器接下来将 Pod 绑定到目标主机, 如果 DaemonSet Pod 的节点亲和性配置已存在,则被替换

  • DaemonSet 控制器仅在创建或修改 DaemonSet Pod 时执行这些操作, 并且不会更改 DaemonSet 的 spec.template

    nodeAffinity: # 节点亲和,可以供节点选择
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchFields:
          - key: metadata.name
            operator: In
            values:
            - target-host-name
    
  • 此外,系统会自动添加 node.kubernetes.io/unschedulable:NoSchedule 容忍度到 DaemonSet Pods

  • 在调度 DaemonSet Pod 时,默认调度器会忽略 unschedulable 节点

3 )与 Daemon Pods 通信3种方式

  • 与 DaemonSet 中的 Pod 进行通信的几种可能模式如下:
    • NodeIP 和已知端口
      • DaemonSet 中的 Pod 可以使用 hostPort,从而可以通过节点 IP 访问到Pod
      • 客户端能通过某种方法获取节点 IP 列表,并且基于此也可以获取到相应的端口
    • DNS
      • 创建具有相同 Pod 选择器的无头服务通过使用 endpoints 资源
      • 或从 DNS 中检索到多个 A 记录来发现 DaemonSet
      • 比如: $ kubectl -n kube-system get pod | grep dns
        coredns-7f6cbbb7b8-4vjd6             1/1     Running   6 (17h ago)    6d5h
        coredns-7f6cbbb7b8-h895p             1/1     Running   6 (17h ago)    6d5h
        
      • 上面 coredns 服务就会记录集群中所有的A记录
    • Service
      • 创建具有相同 Pod 选择器的服务
      • 并使用该服务随机访问到某个节点上的守护进程(没有办法访问到特定节点)

4 )更新DaemonSet

  • 如果节点的标签被修改,DaemonSet 将立刻向新匹配上的节点添加Pod, 同时删除不匹配的节点上的Pod
  • 可以修改 DaemonSet 创建的 Pod,不过并非Pod的所有字段都可更新
  • 下次当某节点(即使具有相同的名称)被创建时,DaemonSet 控制器还会使用最初的模板
  • 可以删除一个 DaemonSet。如果使用 kubectl 并指定 --cascade=false 选项,则Pod将被保留在节点上
  • 接下来如果创建使用相同选择器的新 DaemonSet,新的 DaemonSet 会收养已有的 Pod
  • 如果有Pod 需要被替换,DaemonSet 会根据其 updateStrategy 来替换

5 )DaemonSet 的替代方案

  • init 脚本
    • 直接在节点上启动守护进程(例如使用 initupstartdsystemd )的做法当然是可行的
    • 不过,基于 DaemonSet 来运行这些进程有如下一些好处:
      • 像所运行的其他应用一样,DaemonSet 具备为守护进程提供监控和日志管理的能力
      • 为守护进程和应用所使用的配置语言和工具(如 Pod 模板、 kubectl )是相同的
      • 在资源受限的容器中运行守护进程能够增加守护进程和应用容器的隔离性
      • 然而,这一点也可以通过在容器中运行守护进程但却不在 Pod 中运行之来实现
      • 例如,直接基于 Docker 启动

6 )和Deployments的区别

  • DaemonSet 与 Deployments非常类似, 它们都能创建 Pod
  • 并且 Pod 中的进程都不希望被终止(例如,Web 服务器、存储服务器)
  • 建议为无状态的服务使用 Deployments,比如前端服务
  • 对这些服务而言,对副本的数量进行扩缩容、平滑升级,比精确控制 Pod 运行在某个主机上要重要得多
  • 当需要Pod 副本总是运行在全部或特定主机上,并需要它们先于其他 Pod 启动时,应该使用 DaemonSet
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐