DaemonSet 节点级服务
DaemonSet 保证每个符合条件的 Node 上运行一个 Pod。它适合日志采集、节点监控、CNI 网络组件、存储插件等"跟随节点而不是跟随业务副本"的程序。
本篇目标:能判断何时应该用 DaemonSet,会限制它的调度范围,理解它的更新策略,并对节点级权限建立安全评审意识。
1. DaemonSet 的工作原理与典型应用场景
先把问题反过来想——如果你用 Deployment 跑日志采集 Agent,副本数固定为 3,跑在 10 个节点上:
- Agent Pod 会被调度到其中 3 个节点,剩下 7 个节点的日志没人采集;
- 把副本数调到 10 看似"覆盖所有节点",但调度器不保证每个节点恰好一个 Pod,可能有的节点挤了两个、有的一个都没有。
DaemonSet 让每个目标节点恰好运行一个 Pod 副本。节点加入集群时自动创建,节点移除后对应 Pod 也随之清理。
| 典型用途 | 具体组件举例 | 为什么必须是 DaemonSet |
|---|---|---|
| CNI 网络插件 | Calico node、Cilium agent、Flannel | 每个节点都要有网络 Agent 才能转发 Pod 流量 |
| 日志采集 | Fluent Bit、Fluentd、Filebeat | 每个节点的容器日志目录(/var/log/containers)必须被采集 |
| 节点监控 | node-exporter、datadog-agent | 每个节点的 CPU、内存、磁盘指标需要独立暴露 |
| 存储插件 | CSI node-plugin | 每个节点都要运行 CSI 驱动才能挂载远程卷 |
| 安全 Agent | falco、auditbeat | 每个节点的系统调用和审计日志需要独立监控 |
反过来,普通应用 API 不应用 DaemonSet——节点数变化会意外改变业务副本数,扩缩容逻辑完全不可控。
2. DaemonSet 的调度与污点容忍
DaemonSet 默认对所有节点生效。生产环境中你可能只希望在部分节点运行(如仅 worker 节点、仅 GPU 节点),有三种方式限制范围:
2.1 nodeSelector(最简单)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-agent
namespace: observability
spec:
selector:
matchLabels:
app.kubernetes.io/name: node-agent
template:
metadata:
labels:
app.kubernetes.io/name: node-agent
spec:
nodeSelector:
node-role.kubernetes.io/worker: ""
containers:
- name: agent
image: registry.example.com/node-agent:1.2.0
kubectl label node <node-name> node-role.kubernetes.io/worker=""
2.2 Tolerations:容忍污点
控制面节点通常带有 node-role.kubernetes.io/control-plane:NoSchedule 污点,普通 Pod 不会被调度上去。如果你希望 DaemonSet 也覆盖控制面节点(例如 node-exporter 监控控制面),需要添加对应的 toleration:
spec:
template:
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: dedicated
operator: Equal
value: gpu
effect: NoSchedule
containers:
- name: agent
image: registry.example.com/node-agent:1.2.0
2.3 nodeAffinity(更精细的控制)
spec:
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- ap-guangzhou-3
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssd
requiredDuringScheduling 是硬约束,不满足就不调度;preferredDuringScheduling 是软偏好,尽量满足但不强制。更多选择器语法见第 18 章。
3. DaemonSet 的更新策略
| 策略 | 行为 | 适用场景 |
|---|---|---|
RollingUpdate(默认) | 逐个节点替换旧 Pod | 绝大多数场景,保证服务不中断 |
OnDelete | 只有手动删除旧 Pod 后才创建新的 | 需要手工控制更新节奏,或 Agent 有状态依赖 |
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
# 同时最多有几个节点在更新;对于关键 Agent 建议设为 1
maxUnavailable: 1
maxUnavailable 的语义与 Deployment 不同:它表示同时不可用的节点数上限(不是 Pod 数)。设为 1 表示一次只更新一个节点上的 DaemonSet Pod,最安全。节点数很多时可适当调大以加快更新速度,但应确保每个节点的 Agent 中断不影响整体可观测性。
kubectl rollout status daemonset/node-agent -n observability
kubectl get pods -n observability -o wide
4. 高权限与宿主机访问(安全基线)
节点 Agent 经常需要读取宿主机日志目录、网络接口或 kubelet 数据,因此可能申请 hostPath、hostNetwork、privileged 或宽 RBAC 权限。这些能力接近节点级权限,必须逐项评审。
4.1 常见宿主能力与风险
| 能力 | 用途 | 风险 |
|---|---|---|
hostPath | 读取宿主机文件(日志、配置、容器运行时数据) | 可读写宿主机任意路径,可能破坏节点文件系统 |
hostNetwork: true | 使用宿主机网络栈(如 node-exporter 抓取宿主机网络指标) | 可监听和访问节点上所有端口 |
hostPID: true | 看到宿主机所有进程(如 Falco 监控系统调用) | 可查看甚至操作其他容器的进程 |
privileged: true | 获得几乎所有宿主机能力 | 等同于节点 root,是最危险的权限 |
4.2 最小化权限的原则
spec:
template:
spec:
containers:
- name: agent
image: registry.example.com/node-agent:1.2.0
securityContext:
capabilities:
add:
- NET_ADMIN # 仅网络管理
- SYS_PTRACE # 仅进程追踪(如 Falco)
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
type: Directory
安全基线:
- 只挂载必需的宿主机路径,并设为只读;
- 禁止不必要的
privileged: true,用capabilities.add按需添加; - 独立 Namespace、专属 ServiceAccount 和最小 RBAC(第 17 章);
- 限制镜像来源、版本与准入策略,禁止
latest; - 开启
readOnlyRootFilesystem: true和runAsNonRoot: true减少攻击面。
5. 日常检查
kubectl get daemonset -A
kubectl get pods -n observability -o wide
kubectl describe daemonset node-agent -n observability
kubectl rollout status daemonset/node-agent -n observability
关键字段解读:
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR
node-agent 5 5 5 5 5 node-role=worker
- DESIRED:应运行此 DaemonSet 的节点数。小于集群节点总数时,检查 nodeSelector、affinity 和 tolerations 是否排除了部分节点;
- CURRENT:已创建 Pod 的节点数。与 DESIRED 有差异说明正在创建或调度中;
- READY:Pod 进入 Ready 状态的节点数。小于 CURRENT 说明 Pod 启动了但未通过 readinessProbe;
- UP-TO-DATE:已更新到最新 Pod 模板的节点数。更新期间会短暂小于 DESIRED;
- AVAILABLE:就绪且达到
minReadySeconds的节点数。
如果 Pod 在部分节点上 Pending 或 CrashLoopBackOff,先 describe 对应 Pod 查看事件,再检查该节点的资源、镜像拉取权限和挂载路径是否存在。
6. 练习
练习 A
- 在 kind 集群中给一个节点打标签
agent=enabled:kubectl label node <node-name> agent=enabled - 创建一个 DaemonSet,使用
nodeSelector: agent: enabled,镜像用nginx:1.27-alpine - 观察 DaemonSet 的
DESIRED是否为 1(仅带标签的节点) - 给另一个节点也打上同样标签,观察
DESIRED变为 2 - 去掉一个节点的标签,观察对应 Pod 被自动删除
练习 B
- 给 DaemonSet 添加一个
toleration,容忍控制面污点node-role.kubernetes.io/control-plane - 观察 DaemonSet 的
DESIRED是否增加了控制面节点 - 移除该 toleration,
kubectl rollout status确认 Pod 被清理
下一篇:Job 与 CronJob 任务管理。