尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【Kubernetes从入门到精通】第23篇:DaemonSet——每个节点都要有的“守护者“

【Kubernetes从入门到精通】第23篇:DaemonSet——每个节点都要有的“守护者“ 上一篇【第22篇】StatefulSet——有状态应用的“私人管家“下一篇【第24篇】Job和CronJob——一次性任务和定时任务摘要你有没有想过一个问题——10个Node的K8s集群怎么保证每个Node上都跑着日志收集器Filebeat、监控AgentPrometheus node_exporter、网络插件Calico用Deployment设replicas10万一调度器把两个Pod塞到同一台机器上呢万一有台机器没分到呢DaemonSet就是这种场景的标准答案——它保证每个符合条件的Node上都恰好运行一个Pod副本。这篇文章从DaemonSet的典型应用场景切入讲清楚它跟Deployment/StatefulSet的本质区别绕过Scheduler直通kubelet拆解RollingUpdate和OnDelete两种更新策略的差异用NodeSelector和Toleration控制部署范围最后实战部署一个日志收集DaemonSet和监控Agent。一、DaemonSet的典型场景——“哪里需要哪里搬”1.1 三大主力场景【DaemonSet的经典应用场景】 ┌─────────────────────────────────────────────────────────────┐ │ K8s 集群3个Node │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Node-1 │ │ Node-2 │ │ Node-3 │ │ │ │ │ │ │ │ │ │ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │ │ │Filebeat │ │ │ │Filebeat │ │ │ │Filebeat │ │ ← 日志收集 │ │ │收集本机日志│ │ │ │收集本机日志│ │ │ │收集本机日志│ │ │ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │ │ │ │ │ │ │ │ │ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │ │ │node_exprt│ │ │ │node_exprt│ │ │ │node_exprt│ │ ← 监控Agent │ │ │采集本机指标│ │ │ │采集本机指标│ │ │ │采集本机指标│ │ │ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │ │ │ │ │ │ │ │ │ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │ │ │ CNI 插件 │ │ │ │ CNI 插件 │ │ │ │ CNI 插件 │ │ ← 网络插件 │ │ │(Calico) │ │ │ │(Calico) │ │ │ │(Calico) │ │ │ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ 共同特点每个Node一个不需要更多不能更少1.2 具体场景分析场景类别典型应用为什么必须是DaemonSet日志收集Filebeat/Fluentd每个Node上所有Pod的日志都写到Node本地磁盘需要本地Agent把日志推送到ES/Kafka监控AgentPrometheus node_exporter / Datadog Agent采集Node级别的指标CPU/内存/磁盘只能在Node本机上采集网络插件Calico/Flannel/Cilium每个Node都需要网络代理来处理Pod网络流量存储插件CSI Node Plugin每个Node上需要存储驱动来挂载远程存储卷安全审计Falco/Sysdig每个Node上需要内核级事件监控GPU管理NVIDIA device plugin有GPU的Node需要安装GPU驱动和管理插件要点DaemonSet的本质是守护进程的K8s版本。在传统运维里你登录每台机器手动安装filebeat、node_exporter在K8s里一个DaemonSet YAML搞定——节点自动加入自动部署节点离开自动清理。二、DaemonSet的工作原理——“不走Scheduler直通kubelet”2.1 调度差异——DaemonSet为什么不经过Scheduler【Deployment调度 vs DaemonSet调度】 Deployment Pod 创建流程 DaemonSet Pod 创建流程 ┌────────────────────┐ ┌────────────────────┐ │ Deployment │ │ DaemonSet │ │ Controller │ │ Controller │ └────────┬───────────┘ └────────┬───────────┘ │ 创建Pod │ 直接指定Node ▼ ▼ ┌────────────────────┐ ┌────────────────────┐ │ Scheduler │ │ 每个Node的 │ │ ┌──────────────┐ │ │ kubelet │ │ │ 打分 → 选出 │ │ ← 跳过 │ │ │ │ 最优Node │ │ │ 来了个DaemonSet │ │ └──────────────┘ │ │ Pod跑在我身上 │ └────────┬───────────┘ └────────────────────┘ │ 分配给某Node ▼ ┌────────────────────┐ │ kubelet 启动Pod │ └────────────────────┘ 区别 • DeploymentPod创建后Scheduler挑一个合适的Node • DaemonSet每个Node上都跑一个不需要挑——每个Node都要# DaemonSet——基本YAMLapiVersion:apps/v1kind:DaemonSetmetadata:name:filebeatlabels:app:filebeatspec:selector:matchLabels:app:filebeattemplate:metadata:labels:app:filebeatspec:containers:-name:filebeatimage:docker.elastic.co/beats/filebeat:8.10.0volumeMounts:-name:varlogmountPath:/var/log/containersreadOnly:true-name:varlibdockercontainersmountPath:/var/lib/docker/containersreadOnly:true-name:configmountPath:/usr/share/filebeat/filebeat.ymlsubPath:filebeat.ymlvolumes:-name:varloghostPath:path:/var/log/containers-name:varlibdockercontainershostPath:path:/var/lib/docker/containers-name:configconfigMap:name:filebeat-config2.2 新Node加入——自动部署【DaemonSet的自动化能力】 时刻T1集群只有2个Node ┌──────────────┐ ┌──────────────┐ │ Node-1 │ │ Node-2 │ │ [filebeat] │ │ [filebeat] │ └──────────────┘ └──────────────┘ 时刻T2新Node加入集群 ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Node-1 │ │ Node-2 │ │ Node-3 │ │ [filebeat] │ │ [filebeat] │ │ ???? │ ← 新Node自动部署 └──────────────┘ └──────────────┘ └──────────────┘ │ 几秒钟后自动完成 ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Node-1 │ │ Node-2 │ │ Node-3 │ │ [filebeat] │ │ [filebeat] │ │ [filebeat] │ ← 自动部署完成 └──────────────┘ └──────────────┘ └──────────────┘要点这是DaemonSet最让人省心的地方——不用操心节点扩容。加一个NodeDaemonSet Controller自动检测到新Node秒级创建一个Pod上去。反过来也一样——Node下线/驱逐DaemonSet Pod跟着清理。三、精准控制部署范围——“不是所有Node都要”3.1 NodeSelector——指定Node标签apiVersion:apps/v1kind:DaemonSetmetadata:name:gpu-monitorspec:selector:matchLabels:app:gpu-monitortemplate:metadata:labels:app:gpu-monitorspec:nodeSelector:# ← 只在有GPU的Node上部署accelerator:nvidia-tesla-v100containers:-name:gpu-monitorimage:nvidia/dcgm-exporter:latest# 给Node打标签kubectl label nodes node-1acceleratornvidia-tesla-v100 kubectl label nodes node-2acceleratornvidia-tesla-v100# 看效果——只有打了标签的Node上才会跑kubectl get pods-lappgpu-monitor-owide# NAME NODE STATUS# gpu-monitor-xxxxx node-1 Running# gpu-monitor-yyyyy node-2 Running# node-3没有这个Pod没打标签3.2 NodeAffinity——更精细的调度策略【NodeSelector vs NodeAffinity】 NodeSelector简单 NodeAffinity灵活 ┌──────────────────┐ ┌──────────────────────────┐ │ 只支持等于匹配 │ │ 支持 In/NotIn/Exists/ │ │ acceleratornvidia │ │ DoesNotExist/Gt/Lt │ │ │ │ │ │ 硬性要求必须满足 │ │ 硬性软性尽量满足 │ └──────────────────┘ └──────────────────────────┘spec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:# 硬性要求nodeSelectorTerms:-matchExpressions:-key:node-role.kubernetes.io/workeroperator:Invalues:-true-key:disktypeoperator:Invalues:-ssdpreferredDuringSchedulingIgnoredDuringExecution:# 软性偏好-weight:100preference:matchExpressions:-key:zoneoperator:Invalues:-zone-a3.3 Tolerations——“容忍污点”有些Node打了污点Taint普通Pod不调度上去——但DaemonSet Pod可以通过Toleration容忍这些污点。# Master/Control-Plane节点默认有污点kubectl describenodemaster-node|grepTaints# Taints: node-role.kubernetes.io/control-plane:NoSchedule# DaemonSet想跑在Master上加Tolerationspec:template:spec:tolerations:-key:node-role.kubernetes.io/control-planeoperator:Existseffect:NoSchedule# 容忍这个污点# 监控Agent需要跑在所有Node上——包括Master【Taint Toleration —— 你不嫌我脏我就让你上】 Node打上污点Taint Pod声明容忍Toleration ┌────────────────────────┐ ┌────────────────────────┐ │ Node: master-node │ │ Pod: monitoring-agent │ │ Taint: │ │ Toleration: │ │ control-plane: │ ←→ │ control-plane: │ │ NoSchedule │ 匹配 │ Exists │ │ │ │ │ │ 我是控制节点 │ │ 我不介意你是控制节点 │ │ 别随便往我身上调度 │ │ │ └────────────────────────┘ └────────────────────────┘要点Toleration是让DaemonSet全身覆盖的关键手段。监控Agent、日志收集器、网络插件这些基础设施通常需要跑在所有Node上——包括打了污点的Master/Control-Plane节点。如果不加TolerationMaster节点就成盲区了。3.4 部署范围控制总结机制作用场景举例nodeSelector简单标签匹配只在GPU节点部署GPU监控nodeAffinity复杂条件匹配只在SSD节点的Zone-A部署tolerations容忍Node污点让DaemonSet跑在Master节点上四、更新策略——“怎么安全地升级所有Node”4.1 RollingUpdate——滚动更新默认【DaemonSet RollingUpdate 流程——一个Node一个Node更新】 Node-1 [Filebeat v1] ─┬─ 删除 v1 ─┬─ 创建 v2 ─┬─ v2 Ready │ │ │ Node-2 [Filebeat v1] ─│── 等待… ──│── 删除v1 ─│── 创建v2 ─┬─ v2 Ready │ │ │ │ Node-3 [Filebeat v1] ─│── 等待… ──│── 等待… ──│── 等待… ──│── 删除v1 ─┬─ v2 Ready │ │ │ │ │ ────────────────────────┴───────────┴───────────┴───────────┴───────────┘ 一次只更新一个Node等新Pod Ready再更新下一个apiVersion:apps/v1kind:DaemonSetmetadata:name:filebeatspec:updateStrategy:type:RollingUpdaterollingUpdate:maxUnavailable:1# 最多一个Node不可用maxSurge:0# DaemonSet不支持surge跟Deployment不同template:spec:containers:-name:filebeatimage:docker.elastic.co/beats/filebeat:8.11.0# 最新版4.2 OnDelete——手动挡更新spec:updateStrategy:type:OnDelete# 不自动更新——等你手动删Pod才更新# OnDelete策略下更新DaemonSet模板后已有的Pod不会变kubectl edit daemonset filebeat# 改镜像版本# Pod还是旧版本kubectl get pods-lappfilebeat-ojsonpath{.items[*].spec.containers[*].image}# filebeat:8.10.0 filebeat:8.10.0 filebeat:8.10.0# 手动删一个Pod——它重建后变成新版本kubectl delete pod filebeat-xxxxx# 删Node-1上的# 重建后filebeat:8.11.0# 一个一个手动删除逐个更新——完全由你掌控节奏策略自动化风险控制适用场景RollingUpdate✅ 自动逐个更新maxUnavailable控制常规更新——推荐OnDelete❌ 手动删除Pod才更新完全手动控制谨慎场景——更新前需要手动验证要点DaemonSet的RollingUpdate有一个重要限制——不支持maxSurge跟Deployment不同。原因很简单每个Node上只能跑一个DaemonSet Pod不能像Deployment那样先创一个新的再删旧的。所以DaemonSet的更新是删除旧Pod→创建新Pod中间有短暂的空窗期。五、实战部署一个完整的日志收集DaemonSet5.1 Filebeat DaemonSet——收集所有Pod日志# ConfigMap——Filebeat配置apiVersion:v1kind:ConfigMapmetadata:name:filebeat-confignamespace:kube-systemdata:filebeat.yml:|filebeat.inputs: - type: container paths: - /var/log/containers/*.log processors: - add_kubernetes_metadata: host: ${NODE_NAME} matchers: - logs_path: logs_path: /var/log/containers/output.elasticsearch:hosts:[${ELASTICSEARCH_HOST:elasticsearch:9200}]index:filebeat-%{[agent.version]}-%{yyyy.MM.dd}logging.level:infologging.to_files:truelogging.files:path:/var/log/filebeatname:filebeatkeepfiles:7---# DaemonSetapiVersion:apps/v1kind:DaemonSetmetadata:name:filebeatnamespace:kube-systemlabels:app:filebeatspec:selector:matchLabels:app:filebeatupdateStrategy:type:RollingUpdaterollingUpdate:maxUnavailable:1template:metadata:labels:app:filebeatspec:serviceAccountName:filebeatterminationGracePeriodSeconds:30hostNetwork:truednsPolicy:ClusterFirstWithHostNettolerations:-key:node-role.kubernetes.io/control-planeoperator:Existseffect:NoSchedulecontainers:-name:filebeatimage:docker.elastic.co/beats/filebeat:8.10.0args:--c-/usr/share/filebeat/filebeat.yml--eenv:-name:ELASTICSEARCH_HOSTvalue:elasticsearch.logging.svc.cluster.local:9200-name:NODE_NAMEvalueFrom:fieldRef:fieldPath:spec.nodeNameresources:requests:memory:200Micpu:100mlimits:memory:500Micpu:200msecurityContext:runAsUser:0volumeMounts:-name:configmountPath:/usr/share/filebeat/filebeat.ymlsubPath:filebeat.yml-name:varlogmountPath:/var/log/containersreadOnly:true-name:varlibdockercontainersmountPath:/var/lib/docker/containersreadOnly:true-name:datamountPath:/usr/share/filebeat/datavolumes:-name:configconfigMap:name:filebeat-configdefaultMode:0644-name:varloghostPath:path:/var/log/containers-name:varlibdockercontainershostPath:path:/var/lib/docker/containers-name:datahostPath:path:/var/lib/filebeat-datatype:DirectoryOrCreate---# RBAC——Filebeat需要读取K8s API获取Pod元数据apiVersion:v1kind:ServiceAccountmetadata:name:filebeatnamespace:kube-system---apiVersion:rbac.authorization.k8s.io/v1kind:ClusterRolemetadata:name:filebeatrules:-apiGroups:[]resources:[pods,namespaces]verbs:[get,list,watch]---apiVersion:rbac.authorization.k8s.io/v1kind:ClusterRoleBindingmetadata:name:filebeatsubjects:-kind:ServiceAccountname:filebeatnamespace:kube-systemroleRef:kind:ClusterRolename:filebeatapiGroup:rbac.authorization.k8s.io5.2 Prometheus node_exporter——每个Node的监控AgentapiVersion:apps/v1kind:DaemonSetmetadata:name:node-exporternamespace:monitoringlabels:app:node-exporterspec:selector:matchLabels:app:node-exporterupdateStrategy:type:RollingUpdaterollingUpdate:maxUnavailable:1template:metadata:labels:app:node-exporterspec:hostNetwork:true# 直接使用Node网络hostPID:true# 访问Node的进程信息tolerations:-operator:Exists# 容忍所有污点containers:-name:node-exporterimage:prom/node-exporter:v1.7.0args:---path.procfs/host/proc---path.sysfs/host/sys---path.rootfs/host/root---collector.filesystem.mount-points-exclude^/(dev|proc|sys|var/lib/docker/.)($|/)---collector.filesystem.fs-types-exclude^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefds|securityfs|sysfs|tracefs)$ports:-containerPort:9100hostPort:9100name:metricsvolumeMounts:-name:procmountPath:/host/procreadOnly:true-name:sysmountPath:/host/sysreadOnly:true-name:rootmountPath:/host/rootreadOnly:truemountPropagation:HostToContainervolumes:-name:prochostPath:path:/proc-name:syshostPath:path:/sys-name:roothostPath:path:/# 部署验证kubectl apply-fnode-exporter-daemonset.yaml# 看——每个Node一个Podkubectl get pods-nmonitoring-lappnode-exporter-owide# NAME NODE STATUS# node-exporter-abc12 master-node Running# node-exporter-def34 worker-node-1 Running# node-exporter-ghi56 worker-node-2 Running# 测试metrics接口kubectlexec-nmonitoring node-exporter-abc12 --wget-qO- http://localhost:9100/metrics|head要点node_exporter需要直接访问Node的/proc和/sys文件系统——这些是宿主机的数据不是Pod内部的。所以用hostPath挂载而且设置了hostNetwork: true和hostPID: true让Pod直接使用宿主机的网络和进程命名空间。securityContext里需要privileged: true或者特定的capabilities才能访问内核数据。六、DaemonSet常用命令和调试# 查看所有DaemonSetkubectl get daemonset --all-namespaces# 查看某个DaemonSet详情kubectl describe daemonset filebeat-nkube-system# 看每个Node上的DaemonSet Pod分布kubectl get pods-nkube-system-lappfilebeat-owide# 查看DaemonSet状态——关键字段kubectl get daemonset filebeat-nkube-system# NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR# filebeat 3 3 3 3 3 none## DESIRED: 应该运行Pod的Node数符合条件的所有Node# CURRENT: 当前运行的Pod数# READY: 就绪的Pod数# 滚动更新kubectlsetimage daemonset/filebeatfilebeatfilebeat:8.11.0-nkube-system# 查看更新进度kubectl rollout status daemonset/filebeat-nkube-system# 回滚到上一个版本kubectl rollout undo daemonset/filebeat-nkube-system# 查看更新历史kubectl rollouthistorydaemonset/filebeat-nkube-system# 调试——为什么某些Node上没有DaemonSet Podkubectl get nodes --show-labels# 检查Node标签是否匹配nodeSelectorkubectl describenodenode-name|grepTaints# 检查Taint是否被Toleration覆盖kubectl get events-nkube-system --field-selectorinvolvedObject.namedaemonset-pod-name# 看事件日志定位调度失败原因本篇小结DaemonSet是K8s里最自动化的资源控制器核心价值保证每个符合条件的Node上正好跑一个Pod不多不少——Node加入自动部署Node离开自动清理三大场景日志收集Filebeat/Fluentd、监控Agentnode_exporter/Datadog、基础设施CNI/CSI调度差异绕过Scheduler直通kubelet——因为不需要选Node每个Node都要部署控制NodeSelector简单筛选NodeAffinity复杂匹配Toleration容忍污点覆盖Master节点更新策略RollingUpdate逐个替换不支持maxSurgeOnDelete手动掌控节奏安全要点DaemonSet Pod通常需要特权访问hostPath/hostNetworkRBAC和securityContext要配好下一篇咱们聊Job和CronJob——一次性任务和定时任务。数据库备份、数据清洗、批量处理这些跑完就停的任务在K8s里怎么搞上一篇【第22篇】StatefulSet——有状态应用的“私人管家“下一篇【第24篇】Job和CronJob——一次性任务和定时任务
返回列表