简介
有些软件需要在每个符合条件的节点上运行一个实例,而不是按照固定的副本数量运行。日志收集器、监控代理和网络组件通常会使用 DaemonSet,以便按照节点进行部署。
你将为准备好的节点添加标签,部署节点信息报告器,追踪其归属关系,修改节点是否符合部署条件,并执行一次滚动更新。这个单节点实验便于观察部署规则,同时保留大型集群中使用的相同控制器行为。
贯穿本实验需要关注的重点不是「应该运行多少个副本」,而是「哪些节点符合条件」。DaemonSet 会持续回答这个问题,并在每个匹配的节点上创建一个 Pod。在当前环境中只有一个节点,因此每次资格变化都会清晰地表现为从一个 Pod 变为零个,或从零个变为一个。
标记符合条件的节点
在这一步中,你将为节点添加一个特定用途的标签。标签用于描述节点属性,控制器可以根据这些属性选择节点。
先查看节点及其现有标签。输出内容较宽,不需要记住所有信息;你只需理解,标签是附加在 Node 对象上的键值信息。
cd /home/labex/project/daemonset-workloads
kubectl get nodes --show-labels
添加新标签。这里不需要使用 --overwrite,因为初始化过程已经删除了旧值。不过,如果已存在冲突的值,kubectl label 会向你发出提示:
kubectl label node labex-v135 practice-tier=operations
kubectl get node labex-v135 -L practice-tier
-L practice-tier 会将该标签作为一列添加到表格中。现在,这个节点已经声明自己符合练习用运维代理的部署条件。
第二条命令会输出聚焦后的结果,不会像 --show-labels 那样显示一整行很长的标签信息:
NAME STATUS ROLES VERSION PRACTICE-TIER
labex-v135 Ready control-plane v1.35.5 operations
PRACTICE-TIER=operations 就是你需要确认的证据。标签本身不会运行任何程序;只有当工作负载在下一步选择这个标签时,它才会发挥作用。
部署 DaemonSet
在这一步中,你将创建一个 DaemonSet,使其选择带有新标签的节点。
在输入内容之前,先把这个清单理解为三个相互关联的决策。选择器和 Pod 标签都使用 app: node-reporter,这样控制器就能识别自己管理的 Pod。接着,nodeSelector 将符合条件的节点限制为带有 practice-tier=operations 标签的节点。最后,向下 API 会将所选 Pod 的 spec.nodeName 复制到容器中的 NODE_NAME 环境变量。
这个循环会让演示代理持续运行。实际的 DaemonSet 可能运行的是日志收集器或监控代理。
cat <<'EOF' > node-reporter.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-reporter
namespace: practice-daemon
spec:
selector:
matchLabels:
app: node-reporter
template:
metadata:
labels:
app: node-reporter
spec:
nodeSelector:
practice-tier: operations
containers:
- name: reporter
image: busybox:1.36
imagePullPolicy: IfNotPresent
command: ["sh", "-c", "while true; do echo node=${NODE_NAME} version=v1; sleep 15; done"]
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
EOF
创建命名空间,应用文件,并等待 DaemonSet 完成发布:
kubectl create namespace practice-daemon
kubectl apply -f node-reporter.yaml
kubectl rollout status daemonset/node-reporter -n practice-daemon --timeout=60s
kubectl get daemonset,pods -n practice-daemon -o wide
向下 API 通过 fieldRef 暴露 Pod 被调度到的节点名称,无需将节点名称硬编码。在这个单节点集群中,期望数量、当前数量和就绪数量都应为 1。
Pod 名称中会包含自动生成的后缀,但 NODE 列应显示 labex-v135。这直接证明节点资格规则与调度结果一致。
在这个单节点测试集群中,重要的列类似如下:
NAME DESIRED CURRENT READY NODE SELECTOR
node-reporter 1 1 1 practice-tier=operations
NAME READY STATUS NODE
node-reporter-... 1/1 Running labex-v135
从左到右阅读第一行:有一个节点匹配选择器,存在一个 Pod,并且该 Pod 已就绪。在更大的集群中,这些数量会随着符合条件的节点数量增加而增加。
观察节点资格与归属关系
在这一步中,你将验证 DaemonSet Pod 的数量由节点是否符合条件决定,而不是由某个副本数字段决定。
先记录一个健康状态下的基线。自定义列会将自动生成的 Pod 关联回 DaemonSet,日志则能证明向下 API 提供了节点名称:
kubectl get pods -n practice-daemon -l app=node-reporter -o custom-columns='POD:.metadata.name,NODE:.spec.nodeName,OWNER:.metadata.ownerReferences[0].kind,READY:.status.containerStatuses[0].ready'
kubectl logs -n practice-daemon -l app=node-reporter --tail=3
归属关系表和日志应类似如下:
POD NODE OWNER READY
node-reporter-... labex-v135 DaemonSet true
node=labex-v135 version=v1
OWNER 字段证明这是由控制器管理的 Pod。日志则证明 NODE_NAME 来自 Pod 实际的 spec.nodeName,而不是硬编码的值。
暂时删除标签,并观察 Pod 消失:
kubectl label node labex-v135 practice-tier-
kubectl get daemonset node-reporter -n practice-daemon
kubectl wait --for=delete pod -n practice-daemon -l app=node-reporter --timeout=60s
标签键末尾的 - 表示删除该标签。恢复节点资格,并等待控制器创建替代 Pod:
删除标签后,DaemonSet 会短暂地呈现以下稳定关系:
NAME DESIRED CURRENT READY
node-reporter 0 0 0
这是节点资格发生变化后的正常响应,并不表示失败。当前没有节点匹配 DaemonSet 的 nodeSelector,因此正确的期望数量就是 0。
kubectl label node labex-v135 practice-tier=operations
kubectl rollout status daemonset/node-reporter -n practice-daemon --timeout=60s
比较标签变化前后的 Pod 名称。替代 Pod 会拥有新的 Pod 身份,而 DaemonSet 名称和期望行为保持不变。这就是协调机制在实际工作。
发布 DaemonSet 更新
在这一步中,你将更新保存的清单,并观察 DaemonSet 替换节点代理。保持文件与线上对象一致,可以让期望状态随时复现。
先使用 cp 创建工作副本。然后使用 Kubernetes 入门实验中介绍过的文本编辑方式:sed -i 's/old/new/' file 会直接在文件中替换匹配的文本,而 grep -n 会打印匹配行及其行号,方便你在应用前检查结果。
cp node-reporter.yaml node-reporter-v2.yaml
sed -i 's/version=v1/version=v2/' node-reporter-v2.yaml
grep -n 'version=' node-reporter-v2.yaml
kubectl apply -f node-reporter-v2.yaml
kubectl rollout status daemonset/node-reporter -n practice-daemon --timeout=60s
kubectl rollout history daemonset/node-reporter -n practice-daemon
kubectl logs -n practice-daemon -l app=node-reporter --tail=3
复制出的文件保留了原始的 v1 示例,便于对比。一次替换只会修改容器命令中的版本文本;应用更新后的文件会改变 Pod 模板,从而创建新的控制器修订版本并替换 Pod。此时日志中应出现 version=v2。
rollout history 会确认 Kubernetes 已记录新的修订版本。因此,DaemonSet 也支持类似 Deployment 的受控模板更新,只不过替换过程是围绕符合条件的节点组织的,而不是围绕副本数量进行的。
测试输出包含两个修订版本以及新的日志消息:
REVISION CHANGE-CAUSE
1 <none>
2 <none>
node=labex-v135 version=v2
CHANGE-CAUSE=<none> 只表示没有记录可读的变更注释。修订版本 2,以及替代 Pod 日志中的 version=v2,才是发布确实完成的有效证据。
总结
你使用节点标签控制了 DaemonSet 的部署资格,观察了每个符合条件的节点运行一个 Pod 的协调过程,追踪了控制器归属关系,并完成了节点代理更新的发布。现在,你可以将 DaemonSet 与面向副本数量的 Deployment,以及面向任务完成的 Job 进行对比。


