使用 Kubernetes DaemonSet 部署节点服务

KubernetesBeginner
立即练习

简介

有些软件需要在每个符合条件的节点上运行一个实例,而不是按照固定的副本数量运行。日志收集器、监控代理和网络组件通常会使用 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 进行对比。