Kubernetes 控制器深度解析:DaemonSet
在 Kubernetes 集群中,DaemonSet 是专门用于管理 “节点级服务” 的核心控制器,其核心目标是确保集群中所有(或指定)节点运行且仅运行一个 Pod 副本。无论是日志收集、节点监控,还是存储服务,只要需要在每个节点部署统一组件,DaemonSet 都是最优选择。本文将从概念原理、资源清单、实战案例到运维操作,全面拆解 DaemonSet 的核心能力。
一、DaemonSet 核心概念与原理
1.1 什么是 DaemonSet?
DaemonSet控制器的设计理念是“节点伴随式部署”:当集群新增节点时,Daemonset会自动在新节点上创建指定Pod;当节点从集群移除时,对应的Pod也会被自动删除;若删除DaemonSet,其管理的所有Pod会被批量清理。
核心特征:
单节点单副本:每个节点最多运行一个 DaemonSet Pod(避免资源浪费与功能冲突);
节点自动适配:无需手动调度,DaemonSet 自动感知节点变化并同步 Pod 状态;
节点级资源绑定:Pod 通常需挂载节点本地资源(如
/var/log
、/dev
设备),实现对节点的监控或控制。
1.2 DaemonSet 工作原理
DaemonSet控制器通过“监听-调和”机制维护集群状态,核心流程如下:
1.监听对象:
DaemonSet 对象:获取用户定义的 “期望状态”(如 Pod 模板、节点选择器);
Node 对象:监控节点的新增、删除、状态变更(如
Ready
/NotReady
);Pod 对象:检查每个节点上是否存在符合条件的 Pod,避免重复创建或遗漏。
2.调和逻辑(syncLoop 循环):
对每个 “健康节点”(如
Ready
状态且匹配节点选择器),检查是否存在 DaemonSet Pod:若不存在:根据 Pod 模板创建 Pod,并调度到该节点;
若已存在:检查 Pod 镜像、配置是否与 DaemonSet 一致,不一致则触发更新;
对 “非健康节点”(如
NotReady
或已删除),清理对应的 DaemonSet Pod,避免无效资源占用。
1.3 DaemonSet 典型应用场景
DaemonSet专为“节点级服务”设计,以下是常见的三类场景:
场景类型 | 典型组件 | 核心需求 |
---|---|---|
日志收集 | Fluentd、Filebeat、Logstash | 每个节点需收集本地日志(如 /var/log 、容器日志),统一上报至日志平台 |
节点监控 | Prometheus Node Exporter、Collectd | 每个节点需暴露硬件 / 系统指标(如 CPU 使用率、内存占用),供监控系统采集 |
存储服务 | Glusterd、Ceph OSD | 每个节点需提供存储能力(如本地磁盘管理),构建分布式存储集群 |
网络插件 | Calico、Flannel(agent 组件) | 每个节点需运行网络代理,实现 Pod 网络互通 |
1.4 DaemonSet vs Deployment:核心区别
对比维度 | DaemonSet | Deployment |
---|---|---|
副本调度逻辑 | 按节点分配,每个节点 1 个副本 | 按 “副本数” 分配,节点可运行多个副本 |
调度方式 | 自动绑定节点,无需 nodeSelector 也能全量部署 | 依赖调度器随机分配,需 nodeSelector 才定向到指定节点 |
适用服务类型 | 节点级服务(如监控、日志) | 集群级服务(如 Web、API) |
资源依赖 | 通常挂载节点本地资源(hostPath ) | 多依赖共享存储(如 PVC、ConfigMap) |
更新策略 | 支持 RollingUpdate (默认)、OnDelete | 支持 RollingUpdate (默认)、Recreate |
二、DaemonSet 资源清单编写技巧
DaemonSet 资源清单的核心是 “Pod 模板定义” 与 “节点选择逻辑”,需严格遵循 Kubernetes API 规范。以下从字段解析到完整示例,拆解清单编写要点。
2.1 核心字段解析
通过 kubectl explain ds
和 kubectl explain ds.spec
可查看所有字段,重点关注以下必填 / 核心配置:
字段层级 | 字段名 | 作用说明 |
---|---|---|
apiVersion | - | 固定为 apps/v1 (K8s 1.9+ 版本统一) |
kind | - | 固定为 DaemonSet ,声明资源类型 |
metadata | name | DaemonSet 名称(如 fluentd-elasticsearch ) |
metadata | namespace | 命名空间(建议将节点级组件放在 kube-system ,如监控、日志组件) |
metadata | labels | DaemonSet 自身标签,用于资源筛选(如 k8s-app: fluentd-logging ) |
spec.selector (必填) | matchLabels | 标签选择器,需与 spec.template.metadata.labels 完全匹配(否则无法关联 Pod) |
spec.template (必填) | - | Pod 模板,定义 DaemonSet 管理的 Pod 配置(与 Deployment 的 Pod 模板格式一致) |
spec.updateStrategy | type | 更新策略:RollingUpdate (默认,滚动更新)、OnDelete (手动删除后更新) |
spec.updateStrategy.rollingUpdate | maxUnavailable | 滚动更新时允许不可用的最大 Pod 数(默认 1,支持百分比如 10% ) |
spec.revisionHistoryLimit | - | 保留的历史版本数(默认 10,可减少以节省资源) |
spec.template.spec | tolerations | 污点容忍(关键!用于在有污点的节点部署,如控制节点) |
spec.template.spec | containers | 容器配置(镜像、端口、资源、挂载等) |
spec.template.spec | volumes | 卷配置(常用 hostPath 挂载节点本地目录) |
2.2 完整资源清单示例(部署 Fluentd 日志收集)
以 “在所有节点部署 Fluentd 收集日志” 为例,编写 DaemonSet 清单(daemonset-fluentd.yaml
),包含节点容忍、本地目录挂载、资源限制等关键配置:
apiVersion: apps/v1
kind: DaemonSet
metadata:name: fluentd-elasticsearch # DaemonSet 名称namespace: kube-system # 部署在系统命名空间,便于管理节点级组件labels:k8s-app: fluentd-logging # 自定义标签,用于筛选资源
spec:# 标签选择器:关联符合条件的 Pod(必须与 template.metadata.labels 一致)selector:matchLabels:name: fluentd-elasticsearch# 更新策略:默认 RollingUpdate,maxUnavailable=1(每次更新 1 个节点,避免集群日志断流)updateStrategy:rollingUpdate:maxUnavailable: 1type: RollingUpdate# 保留 5 个历史版本,便于回滚(默认 10,可根据需求调整)revisionHistoryLimit: 5# Pod 模板:定义每个节点上运行的 Pod 配置template:metadata:labels:name: fluentd-elasticsearch # 与 selector.matchLabels 一致spec:# 污点容忍:允许在控制节点(如 master/control-plane)部署# 控制节点默认有 node-role.kubernetes.io/control-plane 污点,需容忍才能调度tolerations:- key: node-role.kubernetes.io/control-planeeffect: NoSchedule # 容忍策略:不调度新 Pod 到该节点,但已存在的 Pod 不驱逐- key: node-role.kubernetes.io/master # 兼容旧版本 K8s(master 节点标签)effect: NoSchedule# 容器配置:Fluentd 核心配置containers:- name: fluentd-elasticsearch # 容器名称image: docker.io/library/fluentd:v2.5.1 # 容器镜像(建议提前拉取到所有节点)imagePullPolicy: IfNotPresent # 镜像拉取策略:本地有则不拉取# 资源限制:避免 Fluentd 占用过多节点资源resources:limits:memory: 200Mi # 最大内存限制requests:cpu: 100m # 最小 CPU 请求memory: 200Mi # 最小内存请求# 卷挂载:挂载节点本地目录,用于收集日志volumeMounts:- name: varlog # 卷名称,与 spec.volumes.name 一致mountPath: /var/log # 容器内挂载路径:收集节点系统日志- name: varlibdockercontainers # 卷名称mountPath: /var/lib/docker/containers # 容器内挂载路径:收集容器日志readOnly: true # 只读挂载,避免修改节点容器数据# 优雅关闭:停止前等待 30 秒,确保日志完全上传terminationGracePeriodSeconds: 30# 卷定义:关联节点本地资源volumes:- name: varlog # 与 volumeMounts.name 一致hostPath:path: /var/log # 节点本地目录:系统日志路径- name: varlibdockercontainers # 与 volumeMounts.name 一致hostPath:path: /var/lib/docker/containers # 节点本地目录:容器日志路径
2.3 关键配置说明
(1)污点容忍(tolerations)
Kubernetes 控制节点(如 control-plane
)默认会打上污点(node-role.kubernetes.io/control-plane:NoSchedule
),阻止普通 Pod 调度。若需在控制节点部署 DaemonSet(如监控控制节点),必须添加对应的容忍配置,否则 Pod 会处于 Pending
状态。
(2)hostPath 卷挂载
DaemonSet 通常需访问节点本地资源,hostPath
是最常用的卷类型,需注意:
- 路径一致性:确保所有节点的本地路径存在(如
/var/log
是所有 Linux 节点的默认日志路径); - 权限控制:若挂载目录需写入权限(如日志采集),需确保容器内用户有对应权限(可通过
securityContext
配置runAsUser
); - containerd 日志路径:若集群使用 containerd 容器运行时,容器日志路径可能为
/var/log/containers/
或/var/log/pods/
,需根据实际路径调整挂载配置。
(3)资源限制(resources)
节点级组件(如 Fluentd、Node Exporter)若不限制资源,可能因日志量突增或内存泄漏占用过多节点资源,导致节点不可用。建议根据节点配置合理设置 limits
和 requests
(如 100m CPU + 200Mi 内存)。
三、DaemonSet 实战:部署与验证
以 “部署 Fluentd 日志收集” 为例,完整演示 DaemonSet 的创建、验证与核心功能验证。
3.1 前置准备
1.镜像预处理:由于 DaemonSet 需在所有节点部署 Pod,建议提前将镜像拉取到每个节点(避免多节点同时拉取导致网络压力):
# 在所有节点执行(以 containerd 为例)
ctr -n k8s.io images pull docker.io/library/fluentd:v2.5.1
# 或导入本地镜像(若镜像已下载到本地)
ctr -n k8s.io images import fluentd-v2.5.1.tar.gz
2.节点状态检查:确保所有节点处于 Ready
状态:
kubectl get nodes
# 输出示例(3 个节点均为 Ready)
# NAME STATUS ROLES AGE VERSION
# hd1 Ready control-plane 30d v1.27.0
# hd2 Ready <none> 30d v1.27.0
# hd3 Ready <none> 30d v1.27.0
3.2 创建 DaemonSet
# 应用资源清单
kubectl apply -f daemonset-fluentd.yaml# 验证 DaemonSet 创建结果
kubectl get ds -n kube-system -l k8s-app=fluentd-logging
# 输出示例(DESIRED=3,CURRENT=3,READY=3 表示所有节点部署成功)
# NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE AGE
# fluentd-elasticsearch 3 3 3 3 3 2m
3.3 验证 Pod 部署状态
DaemonSet 会在每个节点创建一个 Pod,Pod 名称格式为 DaemonSet 名-随机字符串
(如 fluentd-elasticsearch-7xq2z
):
# 查看所有 DaemonSet Pod,包含节点信息
kubectl get pods -n kube-system -l name=fluentd-elasticsearch -o wide
# 输出示例(3 个 Pod 分别运行在 hd1、hd2、hd3 节点)
# NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
# fluentd-elasticsearch-7xq2z 1/1 Running 0 3m 10.244.0.5 hd1 <none>
# fluentd-elasticsearch-9f2k3 1/1 Running 0 3m 10.244.1.6 hd2 <none>
# fluentd-elasticsearch-p4s7x 1/1 Running 0 3m 10.244.2.7 hd3 <none>
3.4 验证核心功能(日志收集)
Fluentd 挂载了节点的 /var/log
目录,可进入 Pod 验证是否成功读取节点日志:
# 进入其中一个 Fluentd Pod(替换为实际 Pod 名称)
kubectl exec -it -n kube-system fluentd-elasticsearch-7xq2z -- sh# 在 Pod 内查看挂载的节点日志(如 /var/log/messages)
cat /var/log/messages
# 若能看到节点系统日志,说明卷挂载成功,日志收集功能正常
3.5 验证节点自动适配
新增一个节点(如 hd4),验证 DaemonSet 是否自动在新节点部署 Pod:
# 1. 新增节点 hd4 并加入集群(过程略)
# 2. 查看节点状态(确保 hd4 为 Ready)
kubectl get nodes# 3. 查看 DaemonSet Pod(hd4 节点会自动创建 Pod)
kubectl get pods -n kube-system -l name=fluentd-elasticsearch -o wide
# 输出会新增一行:fluentd-elasticsearch-xxxxxx 1/1 Running 0 1m 10.244.3.8 hd4 <none>
四、DaemonSet 运维操作:更新、回滚与删除
DaemonSet 的运维需围绕 “节点级服务稳定性” 展开,重点关注更新策略、版本回滚与资源清理。
4.1 镜像更新
DaemonSet 支持两种更新策略:RollingUpdate
(默认)和 OnDelete
,需根据业务场景选择。
4.1.1 RollingUpdate(滚动更新)
默认策略,按 maxUnavailable
配置批量更新 Pod(如 maxUnavailable=1
表示每次更新 1 个节点),避免所有节点同时离线导致服务中断。
操作步骤:
# 方式 1:kubectl set image(推荐,直接指定新镜像)
# 格式:kubectl set image daemonset/<DaemonSet名> <容器名>=<新镜像> -n <命名空间>
kubectl set image daemonset/fluentd-elasticsearch fluentd-elasticsearch=docker.io/library/fluentd:v2.6.0 -n kube-system# 方式 2:编辑 DaemonSet 配置(适合需修改多字段场景)
kubectl edit daemonset fluentd-elasticsearch -n kube-system
# 在编辑器中修改 spec.template.spec.containers[0].image 为 fluentd:v2.6.0,保存退出# 查看更新进度(-w 实时监控 Pod 重建过程)
kubectl get pods -n kube-system -l name=fluentd-elasticsearch -w
# 输出示例(Pod 按节点分批重建,旧 Pod 先删除,新 Pod 再创建):
# NAME READY STATUS RESTARTS AGE
# fluentd-elasticsearch-7xq2z 1/1 Terminating 0 10m # 旧 Pod 终止
# fluentd-elasticsearch-9x8y7 0/1 Pending 0 0s # 新 Pod 创建
# fluentd-elasticsearch-9x8y7 0/1 ContainerCreating 0 2s
# fluentd-elasticsearch-9x8y7 1/1 Running 0 5s # 新 Pod 就绪
# ...(其他节点 Pod 依次更新)
4.1.2 OnDelete(手动触发更新)
若服务对中断敏感(如存储节点代理),可将更新策略改为 OnDelete
:只有手动删除旧 Pod,DaemonSet 才会创建新镜像的 Pod,便于逐个节点验证稳定性。
操作步骤:
修改更新策略:
kubectl edit daemonset fluentd-elasticsearch -n kube-system
# 将 spec.updateStrategy.type 改为 OnDelete,保存退出:
# spec:
# updateStrategy:
# type: OnDelete # 替换默认的 RollingUpdate
2.更新镜像配置:
kubectl set image daemonset/fluentd-elasticsearch fluentd-elasticsearch=fluentd:v2.6.0 -n kube-system
# 此时 Pod 不会自动更新,需手动删除
3.手动触发更新(逐个节点验证):
# 1. 删除第一个节点的旧 Pod(如 hd2 节点的 Pod)
kubectl delete pod -n kube-system fluentd-elasticsearch-9f2k3
# 2. 验证新 Pod 启动并使用新镜像
kubectl get pod -n kube-system fluentd-elasticsearch-xxxxxx -o jsonpath='{.spec.containers[0].image}'
# 输出:docker.io/library/fluentd:v2.6.0(确认新镜像生效)
# 3. 重复步骤 1-2,更新其他节点 Pod
4.2 版本回滚
若更新后出现问题(如镜像崩溃、功能异常),可通过 DaemonSet 的 “历史版本” 回滚到上一个稳定状态。DaemonSet 会保留 revisionHistoryLimit
(默认 10)个历史版本,通过 kubectl rollout
命令管理。
操作步骤:
查看历史版本:
# 查看 DaemonSet 的所有历史版本
kubectl rollout history daemonset fluentd-elasticsearch -n kube-system
# 输出示例:
# daemonsets "fluentd-elasticsearch"
# REVISION CHANGE-CAUSE
# 1 kubectl apply --filename=daemonset-fluentd.yaml --record=true # 初始版本(v2.5.1)
# 2 kubectl set image daemonset/fluentd-elasticsearch ... # 更新后的版本(v2.6.0)
2.查看指定版本的详细配置(可选,确认回滚目标):
# 查看版本 1 的详细配置(如镜像、资源限制)
kubectl rollout history daemonset fluentd-elasticsearch -n kube-system --revision=1
3.执行回滚:
# 方式 1:回滚到上一个版本(快捷命令,适合从最新版回滚)
kubectl rollout undo daemonset fluentd-elasticsearch -n kube-system# 方式 2:回滚到指定版本(如回滚到版本 1)
kubectl rollout undo daemonset fluentd-elasticsearch -n kube-system --to-revision=1
4.验证回滚结果:
# 查看 Pod 镜像是否恢复为旧版本(v2.5.1)
kubectl get pods -n kube-system -l name=fluentd-elasticsearch -o jsonpath='{range .items[*]}{.metadata.name}{": "}{.spec.containers[0].image}{"\n"}{end}'
# 输出示例(所有 Pod 均恢复为 v2.5.1):
# fluentd-elasticsearch-7xq2z: docker.io/library/fluentd:v2.5.1
# fluentd-elasticsearch-9f2k3: docker.io/library/fluentd:v2.5.1
4.3 Pod 清理与 DaemonSet 删除
4.3.1 临时清理单个节点的 Pod(保留 DaemonSet)
若需在某个节点临时停止 DaemonSet 服务(如节点维护),可直接删除该节点的 Pod,且禁止 DaemonSet 自动重建(需通过 kubectl cordon
或节点污点实现):
# 1. 标记节点为“不可调度”(避免 DaemonSet 重新创建 Pod)
kubectl cordon hd3 # hd3 为目标节点名# 2. 删除该节点的 DaemonSet Pod
kubectl delete pod -n kube-system $(kubectl get pods -n kube-system -l name=fluentd-elasticsearch -o jsonpath='{range .items[?(@.spec.nodeName=="hd3")]}{.metadata.name}{"\n"}{end}')# 3. 验证 Pod 未重建(hd3 节点无对应 Pod)
kubectl get pods -n kube-system -l name=fluentd-elasticsearch -o wide | grep hd3# 4. 节点维护完成后,恢复调度(DaemonSet 会自动重建 Pod)
kubectl uncordon hd3
4.3.2 彻底删除 DaemonSet(含所有 Pod)
若不再需要 DaemonSet 服务,删除 DaemonSet 会自动清理其管理的所有 Pod,但不会删除挂载的节点本地资源(如 /var/log
目录):
# 删除 DaemonSet(会批量删除所有 Pod)
kubectl delete daemonset fluentd-elasticsearch -n kube-system# 验证删除结果(DaemonSet 和 Pod 均不存在)
kubectl get ds -n kube-system | grep fluentd-elasticsearch # 无输出
kubectl get pods -n kube-system | grep fluentd-elasticsearch # 无输出
4.4 节点包含 / 排除:指定节点部署 DaemonSet
默认情况下,DaemonSet 会在所有健康节点部署 Pod。若需仅在部分节点部署(如 “只在存储节点部署 Glusterd”),可通过 节点选择器(nodeSelector) 或 节点亲和性(nodeAffinity) 实现。
4.4.1 方式 1:nodeSelector(简单匹配,适合固定标签)
为目标节点打标签(如为所有 “监控节点” 打
node-role=monitor
标签):
kubectl label nodes hd2 hd3 node-role=monitor # hd2、hd3 为目标节点
2.编辑 DaemonSet,添加 nodeSelector:
kubectl edit daemonset fluentd-elasticsearch -n kube-system
# 在 spec.template.spec 下添加 nodeSelector:
# spec:
# template:
# spec:
# nodeSelector:
# node-role: monitor # 仅在带该标签的节点部署
# tolerations: # 保留原有容忍配置
# - key: node-role.kubernetes.io/control-plane
# effect: NoSchedule
3.验证结果(仅 hd2、hd3 节点有 Pod,hd1 节点无 Pod):
kubectl get pods -n kube-system -l name=fluentd-elasticsearch -o wide
4.4.2 方式 2:nodeAffinity(复杂匹配,适合多条件)
若需更灵活的节点选择(如 “排除标签为 node-role=control-plane
的节点”),可使用 nodeAffinity
的 requiredDuringSchedulingIgnoredDuringExecution
(调度时必须满足,运行中忽略标签变化)
kubectl edit daemonset fluentd-elasticsearch -n kube-system
# 添加节点亲和性配置:
spec:template:spec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: node-role.kubernetes.io/control-planeoperator: DoesNotExist # 排除控制节点(无该标签的节点才会被调度)tolerations:- key: node-role.kubernetes.io/control-planeeffect: NoSchedule
五、DaemonSet 关键注意事项与最佳实践
5.1 避免常见问题
控制节点部署失败:控制节点(如
control-plane
)默认有node-role.kubernetes.io/control-plane
污点,需在tolerations
中添加对应容忍,否则 Pod 会处于Pending
状态。镜像拉取超时:DaemonSet 需在所有节点拉取镜像,建议提前通过
ctr images pull
或docker pull
将镜像同步到每个节点,避免多节点同时拉取导致网络拥堵。hostPath 权限问题:若挂载的节点目录(如
/var/log
)权限为root:root
,需在容器securityContext
中配置runAsUser: 0
(以 root 用户运行),否则会因权限不足无法读取日志:
spec:template:spec:containers:- name: fluentd-elasticsearchimage: fluentd:v2.5.1securityContext:runAsUser: 0 # 以 root 用户运行容器
5.2 最佳实践
命名空间选择:节点级组件(如日志、监控、网络插件)建议部署在
kube-system
命名空间,便于集中管理和权限控制。资源限制必须配置:严禁不设置
resources.limits
,避免 DaemonSet Pod 因内存泄漏或日志量突增占用节点全部资源(如 Fluentd 建议设置limits: { memory: 200Mi }
)。历史版本保留精简:若集群节点多(如 100+ 节点),
revisionHistoryLimit
建议设为3-5
(默认 10),减少 etcd 存储占用。更新前先验证:对敏感服务(如存储代理),更新前先通过
OnDelete
策略在 1-2 个非核心节点验证新镜像稳定性,确认无误后再批量更新。
六、总结
DaemonSet 是 Kubernetes 管理 “节点级服务” 的核心控制器,其核心价值在于 “自动适配节点、单节点单副本、绑定本地资源”,完美解决日志收集、节点监控、存储代理等场景的部署需求。
掌握 DaemonSet 需重点关注:
- 配置核心:
tolerations
(控制节点部署)、hostPath
(本地资源挂载)、nodeSelector
(节点筛选); - 运维关键:滚动更新(避免中断)、版本回滚(故障恢复)、节点包含 / 排除(灵活部署);
- 最佳实践:提前同步镜像、配置资源限制、精简历史版本。
通过 DaemonSet 与 StatefulSet、Deployment 的配合,可实现 Kubernetes 集群中 “无状态服务、有状态服务、节点级服务” 的全场景覆盖,构建稳定、高效的容器化平台。
点赞+收藏+关注,下期我们不见不散