跳到主要内容

为节点运行系统 Agent

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 驱动才能挂载远程卷
安全 Agentfalco、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:
# 容忍控制面污点,让 Agent 也能跑到控制面节点上
- 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
不要盲目添加所有 toleration

污点通常是平台团队用来保护专用节点池的(控制面、GPU、内存优化节点)。把所有 toleration 都加上等于绕过平台隔离策略,可能导致监控 Agent 抢占关键业务节点的资源。只为你确认需要覆盖的节点类型添加 toleration。

2.3 nodeAffinity(更精细的控制)​

spec:
template:
spec:
affinity:
nodeAffinity:
# 必须满足:只在可用区 ap-guangzhou-3
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- ap-guangzhou-3
# 尽量满足:优先选 SSD 节点
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssd

requiredDuringScheduling 是硬约束,不满足就不调度;preferredDuringScheduling 是软偏好,尽量满足但不强制。


3. 更新策略​

DaemonSet 的更新策略决定 Pod 模板变更后如何滚动到每个节点:

策略行为适用场景
RollingUpdate(默认)逐个节点替换旧 Pod绝大多数场景,保证服务不中断
OnDelete只有手动删除旧 Pod 后才创建新的需要手工控制更新节奏,或 Agent 有状态依赖
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
# 同时最多有几个节点在更新;对于关键 Agent 建议设为 1
maxUnavailable: 1

maxUnavailable 的语义与 Deployment 不同:它表示同时不可用的节点数上限(不是 Pod 数)。设为 1 表示一次只更新一个节点上的 DaemonSet Pod,最安全。节点数很多时可适当调大以加快更新速度,但应确保每个节点的 Agent 中断不影响整体可观测性。

先更新测试节点池

给少数节点打上特定标签,用 nodeSelector 创建第二个"金丝雀 DaemonSet"提前验证新版本。确认无问题后再更新生产 DaemonSet 的镜像版本。这比直接修改主 DaemonSet 安全得多。


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:
# 替代 privileged: true,按需添加具体能力
capabilities:
add:
- NET_ADMIN # 仅网络管理
- SYS_PTRACE # 仅进程追踪(如 Falco)
# 拒绝所有默认挂载的 capabilities
drop:
- ALL
# 根文件系统只读
readOnlyRootFilesystem: true
# 不以 root 运行(如果 Agent 支持)
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;
  • 限制镜像来源、版本与准入策略,禁止 latest;
  • 开启 readOnlyRootFilesystem: true 和 runAsNonRoot: true 减少攻击面。

5. 日常检查​

# 查看所有 DaemonSet 与基础状态
kubectl get daemonset -A

# 查看 DaemonSet Pod 分布在哪些节点
kubectl get pods -n observability -o wide

# 查看某个 DaemonSet 的详细配置和事件
kubectl describe daemonset node-agent -n observability

# 查看 DaemonSet 的更新状态
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

  1. 在 kind 集群中给一个节点打标签 agent=enabled:kubectl label node <node-name> agent=enabled
  2. 创建一个 DaemonSet,使用 nodeSelector: agent: enabled,镜像用 nginx:1.27-alpine
  3. 观察 DaemonSet 的 DESIRED 是否为 1(仅带标签的节点)
  4. 给另一个节点也打上同样标签,观察 DESIRED 变为 2
  5. 去掉一个节点的标签,观察对应 Pod 被自动删除

练习 B

  1. 给 DaemonSet 添加一个 toleration,容忍控制面污点 node-role.kubernetes.io/control-plane
  2. 观察 DaemonSet 的 DESIRED 是否增加了控制面节点
  3. 移除该 toleration,kubectl rollout status 确认 Pod 被清理

验收清单

  • 知道 DaemonSet 的副本数由目标节点数决定
  • 能说清楚为什么日志/监控/CNI Agent 不能用 Deployment 替代
  • 会使用 nodeSelector / affinity / tolerations 控制运行范围
  • 理解 RollingUpdate 和 OnDelete 的区别及 maxUnavailable 的含义
  • 知道节点 Agent 的 hostPath、hostNetwork、privileged 需要专项评审
  • 知道用 capabilities.add 和 readOnlyRootFilesystem 替代 privileged
  • 会比较 DaemonSet 的 DESIRED、CURRENT、READY、UP-TO-DATE

下一篇:运行一次性与定时任务。