k8s DaemonSet 介绍与实例

我们之前说k8s中使用deployment、statefulset工作负载资源来分别维护无状态和有状态应用。这篇小作文我们会学习如何使用DaemonSet来维护一个守护进程(应用)。


一、DaemonSet是什么?

DaemonSet 是一个确保全部或者某些节点上必须运行一个 Pod的工作负载资源(守护进程),当有节点加入集群时, 也会为他们新增一个 Pod。

下面是常用的使用案例:

  • 集群守护进程,如Kurednode-problem-detector
  • 日志收集守护进程,如fluentdlogstash
  • 监控守护进程,如promethues node-exporter

通过创建DaemonSet 可以确保 守护进程pod 被调度到每个可用节点上运行。


二、DaemonSet 如何工作?

DaemonSet 是由控制器(controller manager)管理的 Kubernetes 工作资源对象。我们通过声明一个想要的daemonset状态,表明每个节点上都需要有一个特定的 Pod。协调控制回路会比较期望状态和当前观察到的状态。如果观察到的节点没有匹配的 Pod,DaemonSet controller将自动创建一个。可以参考之前《k8s工作流程详解》

在这个过程包括现有节点和所有新创建的节点。不过DaemonSet 控制器创建的 Pod 会被Kubernetes 调度器忽略,即DaemonSet Pods 由 DaemonSet 控制器创建和调度。这样带来的两个微妙的问题:

  • Pod 行为的不一致性:正常 Pod 在被创建后等待调度时处于 Pending 状态, DaemonSet Pods 创建后不会处于 Pending 状态下。
  • Pod 抢占行为由默认调度器处理。启用抢占后,DaemonSet 控制器将在不考虑 Pod 优先级和抢占 的情况下制定调度决策。

所以在k8s v1.12以后DaemonSet Controller 将会向 DaemonSet 的 Pod 添加 .spec.nodeAffinity 字段,而不是 .spec.nodeName 字段,并进一步由 kubernetes 调度器将 Pod 绑定到目标节点。如果 DaemonSet 的 Pod 已经存在了 nodeAffinity 字段,该字段的值将被替换。

nodeAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchFields:
      - key: metadata.name
        operator: In
        values:
        - target-host-name

daemonset pod的默认容忍规则如下:

image-20211021134258976

DaemonSet 默认在每个节点上创建一个 Pod。当然也可以使用节点选择器来限制可接受节点的数量。DaemonSet 控制器将仅在与 YAML 文件中预定义的nodeSelector字段匹配的节点上创建Pod。我们在下面会使用到。


三、DaemonSet实例

创建DaemonSet

我们只需要将前面deployment中的kind调整为DaemonSet 就可以创建出一个DaemonSet守护进程

apiVersion: apps/v1 
kind: DaemonSet 
metadata: 
  name: my-daemonset
spec: 
  selector: 
    matchLabels: 
      app: my-daemon
  template: 
    metadata: 
      labels: 
        app: my-daemon
    spec: 
      containers: 
        - name: daemonset-container 
          image: httpd 
          ports: 
          - containerPort : 80

通过apply应用后查看资源状态

$ kubectl get daemonset
NAME            DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
my-daemonset    1         1         1       1            1           <none>                   10m

由于我们minikube只有一个node 所以只建立了一个副本,在节点通过get查看到已创建出这个daemonset pod

$ kubectl get pod 
NAME                  READY   STATUS    RESTARTS   AGE
my-daemonset-97z2g    1/1     Running   0          10m

在daemonset资源状态中可以看到NODE SELECTOR的值为none,显然我们可以通过在pod模板中添加nodeSelector使DaemonSet 控制器仅在与Node 选择算符匹配的节点上创建出pod,接下来我们添加一个nodeSelector

apiVersion: apps/v1 
kind: DaemonSet 
metadata: 
  name: my-daemonset
spec: 
  selector: 
    matchLabels: 
      app: my-daemon
  template: 
    metadata: 
      labels: 
        app: my-daemon
    spec: 
      containers: 
        - name: daemonset-container 
          image: httpd 
          ports: 
          - containerPort : 80
      nodeSelector:
       kubernetes.io/hostname: minikube 

这样我们的pod只会在hostname为minikube的Node上创建DaemonSet守护进程的pod

$ kubectl get daemonset
NAME            DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                     AGE
my-daemonset    1         1         1       1            1           kubernetes.io/hostname=minikube   30m

除了通过nodeSelector来控制节点调度外,还可以通过上面提到的容忍策略即tolerations使daemonset pod 调度到“非正常“Node。

我们可以来看一个fluentd的官方elasticsearch daemonset

源文件地址:fluentd-daemonset-elasticsearch.yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
    version: v1
spec:
  selector:
    matchLabels:
      k8s-app: fluentd-logging
      version: v1
  template:
    metadata:
      labels:
        k8s-app: fluentd-logging
        version: v1
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset:v1-debian-elasticsearch
        env:
          - name:  FLUENT_ELASTICSEARCH_HOST
            value: "elasticsearch-logging"
          - name:  FLUENT_ELASTICSEARCH_PORT
            value: "9200"
          - name: FLUENT_ELASTICSEARCH_SCHEME
            value: "http"
          # Option to configure elasticsearch plugin with self signed certs
          # ================================================================
          - name: FLUENT_ELASTICSEARCH_SSL_VERIFY
            value: "true"
          # Option to configure elasticsearch plugin with tls
          # ================================================================
          - name: FLUENT_ELASTICSEARCH_SSL_VERSION
            value: "TLSv1_2"
          # X-Pack Authentication
          # =====================
          - name: FLUENT_ELASTICSEARCH_USER
            value: "elastic"
          - name: FLUENT_ELASTICSEARCH_PASSWORD
            value: "changeme"
          # Logz.io Authentication
          # ======================
          - name: LOGZIO_TOKEN
            value: "ThisIsASuperLongToken"
          - name: LOGZIO_LOGTYPE
            value: "kubernetes"
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        # When actual pod logs in /var/lib/docker/containers, the following lines should be used.
        # - name: dockercontainerlogdirectory
        #   mountPath: /var/lib/docker/containers
        #   readOnly: true
        # When actual pod logs in /var/log/pods, the following lines should be used.
        - name: dockercontainerlogdirectory
          mountPath: /var/log/pods
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      # When actual pod logs in /var/lib/docker/containers, the following lines should be used.
      # - name: dockercontainerlogdirectory
      #   hostPath:
      #     path: /var/lib/docker/containers
      # When actual pod logs in /var/log/pods, the following lines should be used.
      - name: dockercontainerlogdirectory
        hostPath:
          path: /var/log/pods

特别之处在于,为了收集master节点上的pod日志,将会容忍fluentd调度到master节点。其中tolerations如下

image-20211021114701977

Daemon Pods 通信

与 DaemonSet 中的 Pod 进行通信的几种模式如下:

  • 推送(Push):配置 DaemonSet 中的 Pod,将更新发送到另一个服务,例如统计数据库。
  • NodeIP 和已知端口:DaemonSet 中的 Pod 可以使用 hostPort,从而可以通过节点 IP 访问到 Pod。客户端能通过某种方法获取节点 IP 列表,并且基于此也可以获取到相应的端口。比如prometheus的node-exporter。
  • DNS:创建具有相同 Pod 选择算符的 无头服务 通过使用 endpoints 资源或从 DNS 中检索到多个 A 记录来发现 DaemonSet。

DaemonSet 更新

如果节点的标签被修改,DaemonSet 将立刻向新匹配上的节点添加 Pod, 同时删除不匹配的节点上的 Pod。

可以删除一个 DaemonSet。如果使用 kubectl 指定 --cascade=orphan 选项, 则 Pod 将被保留在节点上。接下来如果创建使用相同选择算符的新 DaemonSet, 新的 DaemonSet 会收养已有的 Pod。 如果有 Pod 需要被替换,DaemonSet 会根据其 updateStrategy 来替换。

比如prometheus中的node-exporter

image-20211021121548126

以上是关于k8s中的DaemonSet相关内容。


参考:

daemonset

node-affinity

node-exporter-daemonset

希望小作文对你有些许帮助,如果内容有误请指正。

您可以随意转载、修改、发布本文,无需经过本人同意。blogiqsing.github.io

posted @ 2021-10-21 17:29  justtest1  阅读(977)  评论(0编辑  收藏  举报