Skip to content

Kubernetes-DaemonSet

DaemonSet 是 Kubernetes 中一种特殊的工作负载控制器,它确保集群中的所有(或部分)节点上都运行一个指定的 Pod 副本。本文将全面解析 DaemonSet 的核心概念、工作原理、典型应用场景以及高级管理技巧,帮助您掌握这一强大的 Kubernetes 资源。

DaemonSet 核心概念与工作原理

基本定义与特性

DaemonSet 是 Kubernetes 的一个 API 对象,它负责在 Kubernetes 集群中运行和管理一个 Pod 的副本,并确保所有节点或选定节点上都运行着一个 Pod 实例。与 Deployment 不同,DaemonSet 不是通过副本数(replicas)来控制 Pod 数量,而是根据集群中的节点数量自动调整。

关键特性包括

  • 节点全覆盖:确保在集群中的所有节点或选定节点上运行一个 Pod 实例
  • 自动恢复:如果节点失败或重启,DaemonSet 会自动在该节点上重新创建 Pod
  • 滚动更新:当 Pod 模板更新时,支持滚动更新策略逐步替换旧的 Pod 实例
  • 灵活调度:通过节点标签选择器(nodeSelector)控制 Pod 在哪些节点上运行
  • 资源控制:支持为 Pod 指定 CPU 和内存资源请求与限制

工作原理深度解析

DaemonSet 控制器通过监听 Kubernetes 集群中的三种对象状态来工作:DaemonSet 对象本身、Pod 对象和 Node 对象。当这些被监听的对象发生变动时,就会触发 syncLoop 循环让 Kubernetes 集群朝着 DaemonSet 对象描述的状态进行演进。

具体工作流程

  1. 定义对象:用户定义一个 DaemonSet 对象,指定 Pod 模板和其他配置选项
  2. 调度 Pod:控制器接收到创建请求后,在每个匹配的节点上创建一个 Pod 实例
  3. 监控 Pod:控制器监视 Pod 状态,确保每个节点上都有 Pod 实例
  4. 处理节点变更
    • 新节点加入:自动在新节点上创建 Pod
    • 节点移除:自动删除该节点上的 Pod
  5. 更新管理:当 Pod 模板更新时,控制器负责在所有节点上滚动更新 Pod 实例

DaemonSet 通过节点亲和性(nodeAffinity)和容忍度(Toleration)这两个调度器参数的功能,保证了每个节点上有且只有一个 Pod。

DaemonSet 典型应用场景

DaemonSet 适用于每个 node 节点均需要部署一个守护进程的场景,以下是其主要应用领域:

日志收集系统

典型组件:Fluentd、Logstash、Filebeat 等

实现方式:在每个节点上运行日志收集器,收集节点的日志数据并将其发送到中心日志服务器进行存储和分析。由于 Kubernetes 中 Pod 都是短暂的,把日志存储在容器内部毫无意义,容器重启后日志就丢失了,因此有必要从每个节点收集日志。

示例配置:

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        volumeMounts:
        - name: varlog
          mountPath: /var/log
      volumes:
      - name: varlog
        hostPath:
          path: /var/log

监控数据采集

典型组件:Prometheus Node Exporter、Sysdig Agent、Ganglia gmond 等

实现方式:在每个节点上部署监控代理,收集节点的运行状态数据(如 CPU、内存、磁盘、网络等),并将其发送到监控服务器进行分析和展示。

示例配置:

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
spec:
  selector:
    matchLabels:
      name: node-exporter
  template:
    metadata:
      labels:
        name: node-exporter
    spec:
      containers:
      - name: node-exporter
        image: prom/node-exporter:latest
        ports:
        - containerPort: 9100

网络与存储组件

典型组件

  • 网络插件:flannel、weave、calico、kube-proxy 等
  • 存储系统:Ceph MON、Ceph OSD、glusterd 等

实现方式:这些分布式系统组件需要在每个节点上运行特定服务来管理网络规则或存储资源。

特定硬件利用

场景示例

  • 在配备 SSD 存储的节点上运行高性能缓存服务
  • 在 GPU 节点上运行机器学习推理服务
  • 在特定区域的节点上运行监控代理

实现方式:通过节点标签选择器(nodeSelector)定向部署到特定硬件节点上。

DaemonSet 资源清单

DaemonSet 的 YAML 配置文件包含多个关键字段,以下是必须字段和说明:

yaml
apiVersion: apps/v1  # 必须使用 apps/v1 API 版本(v1.16+)
kind: DaemonSet
metadata:
  name: node-log-collector  # 推荐命名格式:<功能>-<组件类型>
  namespace: monitoring    # 通常部署在独立的监控命名空间
spec:
  # 更新策略配置(关键生产参数)
  updateStrategy:
    type: RollingUpdate     # 可选值:RollingUpdate(默认)/OnDelete(需手动删除Pod触发更新)
    rollingUpdate:
      maxUnavailable: 1    # 允许不可用Pod的最大数量,可设置为百分比(如"20%")
                          # 生产建议:小集群设为1,大集群设为10-20%节点数
  minReadySeconds: 30      # 新Pod就绪后等待时间(秒),用于避免更新过速
  
  # 标签选择器(必须与template.metadata.labels匹配)
  selector:
    matchLabels:
      app: log-agent      # 推荐使用标准标签:app.kubernetes.io/name=log-agent

  # Pod模板(核心配置区域)
  template:
    metadata:
      labels:
        app: log-agent    # 必须与selector.matchLabels一致
    spec:
      # 污点容忍配置(允许调度到特定节点)
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"  # 匹配存在该key的污点(无论value为何值)
        effect: "NoSchedule" # 处理NoSchedule类型的污点
        # 生产建议:关键组件才容忍master污点,避免master节点过载

      # 节点亲和性规则(比nodeSelector更灵活)
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:  # 硬性要求
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values: [linux]  # 限定只部署在Linux节点
                # 其他常用筛选条件:
                # - beta.kubernetes.io/arch: amd64/arm64
                # - topology.kubernetes.io/zone

      # 容器定义(DaemonSet核心业务逻辑)
      containers:
      - name: log-agent    # 容器名称应明确功能
        image: your-log-agent:1.2.0  # 实际使用时需替换为具体镜像
        resources:
          limits:
            memory: "200Mi"  # 必须设置内存限制(防止OOM)
            cpu: "500m"      # 限制CPU使用(1核心=1000m)
          # 生产建议:
          # 1. 根据实际负载设置requests(通常=limits的70%)
          # 2. 启用Vertical Pod Autoscaler自动调整资源
        
        # 应补充的常用配置(根据需求添加):
        # securityContext:
        #   runAsNonRoot: true
        #   readOnlyRootFilesystem: true
        # livenessProbe:
        #   exec:
        #     command: ["/bin/check-health"]
        # volumeMounts:
        # - name: varlog
        #   mountPath: /var/log

      # 建议添加的Pod级配置:
      # priorityClassName: system-node-critical  # 关键组件优先级
      # terminationGracePeriodSeconds: 60       # 优雅终止等待时间
      # volumes:
      # - name: varlog
      #   hostPath:
      #     path: /var/log

重要注意事项

  1. .spec.selector 必须与 .spec.template.metadata.labels 匹配
  2. DaemonSet 对象的名称必须是一个合法的 DNS 子域名:
    • 不能超过 253 个字符
    • 只能包含小写字母、数字,以及 '-' 和 '.'
    • 必须以字母数字开头和结尾

关键配置说明表

参数类型说明生产建议
updateStrategyObject更新策略关键服务建议用OnDelete
maxUnavailableInt/Str最大不可用Pod数大集群建议百分比控制
tolerationsList污点容忍规则仅容忍必要节点的污点
nodeAffinityObject节点选择逻辑比nodeSelector更灵活
resources.limitsObject资源上限必须设置防止资源耗尽

DaemonSet 操作指令

创建 DaemonSet

bash
kubectl create -f daemonset.yaml
# 或
kubectl apply -f daemonset.yaml

查看 DaemonSet

bash
# 查看实例列表
kubectl get daemonset -A  # 查看所有命名空间
kubectl get ds -n <namespace> -o wide  # 查看指定命名空间(带节点分布信息)

# 查看详细信息
kubectl describe daemonset <name> -n <namespace>

关键输出字段解析

bash
Desired Number of Nodes Scheduled: 5  # 应运行的Pod数
Current Number of Nodes Scheduled: 5  # 实际运行数
Number of Nodes Misscheduled: 0       # 调度到错误节点的Pod数
Number Ready: 5                      # 就绪Pod数
Update Strategy: RollingUpdate       # 更新策略
  Max Unavailable: 1                 # 滚动更新参数
Events:                              # 关键事件记录

查看 Yaml 定义(带状态)

bash
kubectl get daemonset <name> -n <namespace> -o yaml

检查 Pod 分布情况

bash
# 查看Pod在节点的分布
kubectl get pods -l <label-selector> -n <namespace> -o wide

# 检查未调度的Pod(需替换selector)
kubectl get pods -l app=log-agent -n monitoring \
  --field-selector status.phase!=Running

查看更新历史

bash
kubectl rollout history daemonset/<name> -n <namespace>

DaemonSet 自定义查询

bash
# 查看 DaemonSet 分布概况
kubectl get daemonsets -A -o custom-columns="NAMESPACE:.metadata.namespace,NAME:.metadata.name,DESIRED:.status.desiredNumberScheduled,CURRENT:.status.currentNumberScheduled,READY:.status.numberReady,MISSCHEDULED:.status.numberMisscheduled,NODE-SELECTOR:.spec.template.spec.nodeSelector"

#  检查更新状态
kubectl get ds -n <namespace> -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.updatedNumberScheduled}{"/"}{.status.desiredNumberScheduled}{"\n"}{end}'

# 计算 CPU/内存总请求量
kubectl get ds -n <namespace> -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{"  CPU Requests: "}{.spec.template.spec.containers[0].resources.requests.cpu}{"\n"}{"  Memory Requests: "}{.spec.template.spec.containers[0].resources.requests.memory}{"\n"}{end}'

# 检查 Pod 资源使用(需 metrics-server)
kubectl top pod -n <namespace> -l <daemonset-label-selector> --containers

# 检查节点调度限制原因
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.taints}{"\n"}{end}'

# 获取更新策略详情
kubectl get ds <name> -n <namespace> -o jsonpath='{.spec.updateStrategy.type}{"\tMaxUnavailable: "}{.spec.updateStrategy.rollingUpdate.maxUnavailable}{"\n"}'

# 检查容忍度配置
kubectl get ds <name> -n <namespace> -o jsonpath='{.spec.template.spec.tolerations[*].key}'

# 查看 DaemonSet 相关事件
kubectl get events -n <namespace> --field-selector involvedObject.kind=DaemonSet --sort-by='.lastTimestamp'

# 收集所有 Pod 的启动日志
kubectl logs -n <namespace> -l <label-selector> --all-containers=true --prefix --tail=50

更新 DaemonSet

bash
# 方式一:更新yaml文件后执行
kubectl apply -f daemonset.yaml

# 方式二:更新镜像
kubectl set image daemonset <daemonset-name> <container-name>=<new-image>

# 方式三:更新环境变量
kubectl set env daemonset <daemonset-name> <key>=<value>

回滚 DaemonSet

bash
# 默认回滚至上一版本
kubectl rollout undo daemonset <daemonset-name>

# 回滚至指定版本
kubectl rollout undo daemonset <daemonset-name> --to-revision=3

# 查看可回滚版本
kubectl rollout history daemonset <daemonset-name>

删除 DaemonSet

bash
# 方式一:命令行删除
kubectl delete daemonset <daemonset-name>

# 方式二:通过资源清单删除
kubectl delete -f daemonset.yaml

在特定节点上运行 Pod

通过节点选择器(nodeSelector)可以限制 DaemonSet Pod 只在特定节点上运行:

步骤1:标记目标节点

bash
kubectl label nodes <node1> <node2> ssd=true

步骤2:配置 DaemonSet 使用节点选择器

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: ssd-cache-daemon
spec:
  selector:
    matchLabels:
      name: ssd-cache
  template:
    metadata:
      labels:
        name: ssd-cache
    spec:
      nodeSelector:
        ssd: "true"  # 只会在带有ssd=true标签的节点上运行
      containers:
      - name: cache-container
        image: registry.example.com/cache:latest

高级配置与优化策略

调度控制:亲和性与容忍度

除了基本的节点选择器,DaemonSet 还支持更复杂的调度策略:

节点亲和性(nodeAffinity)

yaml
spec:
  template:
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values:
                - linux

污点与容忍度(Tolerations)

允许 Pod 被调度到带有特定污点的节点上,常见于控制平面节点:

yaml
spec:
  template:
    spec:
      tolerations:
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule

更新策略配置

DaemonSet 支持两种更新策略:

滚动更新(RollingUpdate) - 默认策略

yaml
spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1  # 更新过程中最多允许不可用的Pod数量

删除时更新(OnDelete)

yaml
spec:
  updateStrategy:
    type: OnDelete  # 只有手动删除Pod时才会创建新版本的Pod

资源管理与优化

由于 DaemonSet 会在每个节点上运行 Pod,需要特别注意资源管理:

  1. 设置合理的资源限制:避免 DaemonSet Pod 占用过多节点资源影响其他工作负载

    yaml
    resources:
      limits:
        memory: 200Mi
      requests:
        cpu: 100m
        memory: 200Mi
  2. 优先级设置:为关键的系统级 DaemonSet 设置适当的 Pod 优先级

    yaml
    spec:
      template:
        spec:
          priorityClassName: system-node-critical
  3. 优雅终止:配置适当的终止宽限期,确保守护进程能够优雅关闭

    yaml
    terminationGracePeriodSeconds: 30

与其它控制器的对比

特性DaemonSetDeploymentStatefulSet
应用场景每个节点上都需要运行一个Pod,如日志收集、监控、代理等无状态应用,自动扩容、滚动更新有状态应用,保证Pod顺序和持久化存储
Pod副本管理每个节点一个Pod,自动为新节点创建Pod,删除节点时自动清理控制副本数,自动扩缩容保证Pod顺序启动和停止,适合有状态服务
更新机制滚动更新或手动删除Pod时更新,适合后台进程支持滚动更新、回滚等顺序更新,保证有状态服务的一致性
典型用例日志收集、监控代理、网络插件等集群级别服务Web应用、API服务等无状态服务数据库、缓存等需要持久化和顺序管理的服务

经典案例

yaml
---
# 命名空间
apiVersion: v1
kind: Namespace
metadata:
  name: demo-study

---
# DaemonSet 资源定义(确保每个节点运行一个 Pod 副本)
apiVersion: apps/v1        # Kubernetes API 版本
kind: DaemonSet            # 资源类型为 DaemonSet
metadata: 
  name: demo-daemonset     # DaemonSet 名称
  namespace: demo-study    # 所属命名空间
  labels:
    app: demo-study        # 应用标签
    name: node-exporter    # 组件名称标签
spec:
  selector:                # 标签选择器(匹配 Pod)
    matchLabels:
      app: demo-study      # 必须匹配 template.metadata.labels
      name: node-exporter
  template:                # Pod 模板定义
    metadata:
      name: demo-daemonset # Pod 名称(可选)
      namespace: demo-study
      labels:              # Pod 标签(必须匹配 selector)
        app: demo-study
        name: node-exporter
    spec:
      containers:
        - name: node-exporter  # 容器名称
          image: prom/node-exporter:latest  # 官方 node-exporter 镜像
          imagePullPolicy: IfNotPresent    # 镜像拉取策略(本地有则用本地)
          ports:
            - containerPort: 9100  # 容器暴露的指标采集端口
          resources:              # 资源限制配置
            limits:               # 资源上限
              cpu: 200m          # CPU 限制(200 毫核 = 0.2 核)
              memory: 200Mi      # 内存限制(200 兆字节)
            requests:            # 资源请求(调度依据)
              cpu: 200m
              memory: 200Mi
          # 启动探针配置(检查容器是否完成启动)
          startupProbe:
            httpGet:             # 使用 HTTP GET 检查
              port: 9100        # 检查的端口
              scheme: HTTP      # 使用 HTTP 协议
            initialDelaySeconds: 3  # 容器启动后等待时间(秒)
            periodSeconds: 1        # 检查间隔(秒)
            timeoutSeconds: 1       # 检查超时时间(秒)
            failureThreshold: 3     # 连续失败次数达到阈值后重启
          # 存活探针配置(检查容器是否健康运行)
          livenessProbe:
            httpGet:               # 同样使用 HTTP 检查
              port: 9100
              scheme: HTTP
            initialDelaySeconds: 3  # 首次检查延迟(避免过早判定失败)
            periodSeconds: 3        # 常规检查间隔
            timeoutSeconds: 3       # 单次检查超时时间
            failureThreshold: 3     # 连续失败 3 次后重启容器

故障排查指南

Pod未在全节点运行

bash
# 检查节点选择器是否匹配
kubectl get nodes --show-labels | grep -E '<label-key>'

# 检查污点配置
kubectl describe node <node-name> | grep Taints

# 检查资源是否充足
kubectl describe node | grep -A 3 "Allocated resources"

更新卡住处理

bash
# 查看阻塞原因
kubectl get pods -l <selector> -n <namespace> | grep -Ev "Running|Completed"

# 强制删除卡住的Pod(谨慎使用)
kubectl delete pod <pod-name> -n <namespace> --grace-period=0 --force

性能问题诊断

bash
# 查看Pod资源使用
kubectl top pod -l <selector> -n <namespace>

# 检查节点级资源压力
kubectl describe node | grep -E 'MemoryPressure|DiskPressure|PIDPressure'