跳到主要内容

DaemonSet 详解

DaemonSet 确保在每个节点上都运行一个 Pod 副本。当节点加入集群时,DaemonSet 会自动在新节点上创建 Pod;当节点从集群移除时,DaemonSet 创建的 Pod 会被垃圾回收。

DaemonSet 的典型应用场景

DaemonSet 非常适合运行集群级别的守护进程:

场景示例
日志收集Fluentd、Filebeat、Logstash
监控代理Prometheus Node Exporter、Datadog Agent
网络插件Calico、Cilium、Weave Net
存储插件Ceph RBD、GlusterFS
安全代理Falco、Cilium

这些场景的共同特点是需要在每个节点上运行,并且通常需要访问节点的资源(如日志、监控指标、网络配置等)。

DaemonSet 与其他控制器的对比

特性DaemonSetDeploymentStatefulSet
Pod 数量每个节点一个指定副本数指定副本数
Pod 分布自动分布到所有节点调度器决定调度器决定
节点感知自动响应节点变化无感知无感知
典型用途系统守护进程无状态应用有状态应用

创建 DaemonSet

基础示例:日志收集代理

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
# 允许在控制平面节点上运行
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v5.0.1
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
# 创建 DaemonSet
kubectl apply -f daemonset.yaml

# 查看 DaemonSet
kubectl get daemonset -n kube-system

# 查看 DaemonSet 创建的 Pod
kubectl get pods -n kube-system -l name=fluentd-elasticsearch

# 输出示例(每个节点一个 Pod)
# NAME READY STATUS NODE
# fluentd-elasticsearch-abc12 1/1 Running node-1
# fluentd-elasticsearch-def34 1/1 Running node-2
# fluentd-elasticsearch-ghi56 1/1 Running node-3

必需字段

DaemonSet 的 YAML 配置需要包含以下必需字段:

apiVersion: apps/v1          # API 版本
kind: DaemonSet # 资源类型
metadata:
name: <daemonset-name> # 名称
spec:
selector: # Pod 选择器(必需)
matchLabels:
app: <app-name>
template: # Pod 模板(必需)
metadata:
labels:
app: <app-name> # 必须与 selector 匹配
spec:
containers:
- name: <container-name>
image: <image>

限制 DaemonSet 运行的节点

默认情况下,DaemonSet 会在所有节点上创建 Pod。你可以通过多种方式限制它只在特定节点上运行。

使用 nodeSelector

只在具有特定标签的节点上运行:

spec:
template:
spec:
nodeSelector:
disktype: ssd # 只在有 SSD 的节点上运行
# 给节点添加标签
kubectl label nodes node-1 disktype=ssd

使用节点亲和性

更灵活的节点选择:

spec:
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values:
- amd64
- key: node-role.kubernetes.io/worker
operator: Exists

使用污点和容忍

让 DaemonSet Pod 可以在特定污点的节点上运行:

spec:
template:
spec:
tolerations:
# 容忍 master 节点的污点
- key: node-role.kubernetes.io/master
effect: NoSchedule
# 容忍控制平面节点的污点
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule

DaemonSet 自动添加的容忍

DaemonSet 控制器会自动为 Pod 添加以下容忍,确保 Pod 可以在问题节点上运行:

容忍键效果说明
node.kubernetes.io/not-readyNoExecute节点不健康时仍运行
node.kubernetes.io/unreachableNoExecute节点不可达时仍运行
node.kubernetes.io/disk-pressureNoSchedule磁盘压力大时仍可调度
node.kubernetes.io/memory-pressureNoSchedule内存压力大时仍可调度
node.kubernetes.io/pid-pressureNoSchedulePID 压力大时仍可调度
node.kubernetes.io/unschedulableNoSchedule节点不可调度时仍可调度

这些自动容忍确保 DaemonSet Pod 在节点出现问题之前就能运行,这对于网络插件、监控代理等关键组件非常重要。

与 Daemon Pod 通信

DaemonSet 创建的 Pod 有几种通信模式:

1. Push 模式

Pod 直接将数据推送到外部服务:

containers:
- name: log-collector
image: fluentd
env:
- name: ELASTICSEARCH_URL
value: "http://elasticsearch:9200"

2. NodeIP 和已知端口

使用 hostPort 让 Pod 通过节点 IP 访问:

containers:
- name: node-exporter
image: prom/node-exporter
ports:
- containerPort: 9100
hostPort: 9100 # 绑定到节点端口
# 访问方式
curl http://<node-ip>:9100/metrics

3. Service 和 Endpoints

创建 Service 选择 DaemonSet Pod:

apiVersion: v1
kind: Service
metadata:
name: node-exporter
spec:
type: ClusterIP
clusterIP: None # Headless Service
selector:
app: node-exporter
ports:
- port: 9100
# 通过 DNS 访问(返回所有 Pod IP)
nslookup node-exporter.default.svc.cluster.local

4. DNS 多条 A 记录

使用 Headless Service 自动创建多条 A 记录:

apiVersion: v1
kind: Service
metadata:
name: fluentd
spec:
clusterIP: None
selector:
name: fluentd-elasticsearch
ports:
- port: 24224

更新 DaemonSet

DaemonSet 支持两种更新策略:

RollingUpdate(默认)

滚动更新,逐个替换节点上的 Pod:

spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # 最多 1 个 Pod 不可用
# 更新镜像触发滚动更新
kubectl set image daemonset/fluentd-elasticsearch fluentd-elasticsearch=fluentd:v5.1.0 -n kube-system

# 查看更新状态
kubectl rollout status daemonset/fluentd-elasticsearch -n kube-system

# 查看更新历史
kubectl rollout history daemonset/fluentd-elasticsearch -n kube-system

OnDelete

手动更新,只有 Pod 被删除时才更新:

spec:
updateStrategy:
type: OnDelete
# 更新 DaemonSet 后,需要手动删除 Pod 才会更新
kubectl delete pod fluentd-elasticsearch-xxx -n kube-system

完整示例:Node Exporter

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
labels:
app: node-exporter
spec:
selector:
matchLabels:
app: node-exporter
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
template:
metadata:
labels:
app: node-exporter
spec:
serviceAccountName: node-exporter
containers:
- name: node-exporter
image: prom/node-exporter:v1.7.0
args:
- --path.procfs=/host/proc
- --path.sysfs=/host/sys
- --path.rootfs=/host/root
- --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)
ports:
- name: metrics
containerPort: 9100
hostPort: 9100
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
volumeMounts:
- name: proc
mountPath: /host/proc
readOnly: true
- name: sys
mountPath: /host/sys
readOnly: true
- name: root
mountPath: /host/root
mountPropagation: HostToContainer
readOnly: true
tolerations:
- effect: NoSchedule
operator: Exists
- effect: NoExecute
operator: Exists
volumes:
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
- name: root
hostPath:
path: /
---
apiVersion: v1
kind: Service
metadata:
name: node-exporter
namespace: monitoring
spec:
clusterIP: None
selector:
app: node-exporter
ports:
- name: metrics
port: 9100
targetPort: 9100

DaemonSet 的替代方案

1. Init 脚本

直接在节点上运行守护进程(如 systemd)。缺点是无法通过 Kubernetes API 统一管理。

2. 静态 Pod

通过写入节点上的特定目录创建 Pod,不依赖 API Server。适合集群引导阶段,但不支持 kubectl 管理。

# 静态 Pod 配置路径(kubelet 配置)
# staticPodPath: /etc/kubernetes/manifests/

3. Deployment

Deployment 也可以在所有节点上运行 Pod(通过 nodeSelector),但无法自动响应节点变化。

常用命令

# 查看 DaemonSet
kubectl get daemonset -A

# 查看特定 DaemonSet
kubectl describe daemonset <name> -n <namespace>

# 查看 DaemonSet 创建的 Pod
kubectl get pods -l app=<daemonset-name>

# 更新 DaemonSet 镜像
kubectl set image daemonset/<name> <container>=<new-image> -n <namespace>

# 查看更新状态
kubectl rollout status daemonset/<name> -n <namespace>

# 回滚 DaemonSet
kubectl rollout undo daemonset/<name> -n <namespace>

# 查看更新历史
kubectl rollout history daemonset/<name> -n <namespace>

# 删除 DaemonSet
kubectl delete daemonset <name> -n <namespace>

调试技巧

检查 Pod 为什么没有运行

# 查看 DaemonSet 状态
kubectl describe daemonset <name> -n <namespace>

# 检查节点选择器
kubectl get daemonset <name> -o jsonpath='{.spec.template.spec.nodeSelector}'

# 检查节点标签
kubectl get nodes --show-labels

# 检查污点和容忍
kubectl describe node <node-name> | grep -A5 Taints

查看 Pod 日志

# 查看特定节点上的 Pod 日志
kubectl logs -n kube-system daemonset/<daemonset-name>

# 查看特定 Pod 日志
kubectl logs <pod-name> -n kube-system

进入 Pod 调试

# 进入 Pod 执行命令
kubectl exec -it <pod-name> -n kube-system -- /bin/sh

# 在节点上查看 hostPath 挂载
kubectl exec -it <pod-name> -n kube-system -- ls /host/log

最佳实践

1. 设置优先级

确保关键 DaemonSet 在资源紧张时仍能运行:

spec:
template:
spec:
priorityClassName: system-node-critical # 高优先级

2. 限制资源使用

避免 DaemonSet 占用过多资源:

resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "128Mi"

3. 使用健康检查

确保 DaemonSet Pod 正常运行:

livenessProbe:
httpGet:
path: /health
port: 9100
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 9100
initialDelaySeconds: 5
periodSeconds: 5

4. 使用安全上下文

限制 Pod 权限:

securityContext:
runAsUser: 1000
runAsGroup: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true

5. 正确处理信号

优雅终止 DaemonSet Pod:

spec:
template:
spec:
terminationGracePeriodSeconds: 30
containers:
- name: app
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]

DaemonSet 选择决策树

                    需要在每个节点运行?

┌──────┴──────┐
│ │
是 否
│ │
▼ ▼
DaemonSet 需要固定副本数?

┌──────┴──────┐
│ │
是 否
│ │
▼ ▼
有状态应用? 无状态应用?
│ │
┌─────┴─────┐ ┌──┴──┐
│ │ │ │
是 否 是 否
│ │ │ │
▼ ▼ ▼ ▼
StatefulSet Deployment Job CronJob

小结

本章我们学习了:

  1. DaemonSet 概念:确保每个节点运行一个 Pod
  2. 应用场景:日志收集、监控代理、网络插件
  3. 节点选择:nodeSelector、节点亲和性、污点容忍
  4. 更新策略:RollingUpdate 和 OnDelete
  5. 通信模式:Push、NodeIP、Service、DNS
  6. 最佳实践:优先级、资源限制、健康检查

练习

  1. 创建一个 DaemonSet,在每个节点上运行 Node Exporter
  2. 使用 nodeSelector 限制 DaemonSet 只在特定节点运行
  3. 更新 DaemonSet 的镜像版本并观察滚动更新过程
  4. 创建一个 Service 暴露 DaemonSet Pod