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 与其他控制器的对比
| 特性 | DaemonSet | Deployment | StatefulSet |
|---|---|---|---|
| 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-ready | NoExecute | 节点不健康时仍运行 |
node.kubernetes.io/unreachable | NoExecute | 节点不可达时仍运行 |
node.kubernetes.io/disk-pressure | NoSchedule | 磁盘压力大时仍可调度 |
node.kubernetes.io/memory-pressure | NoSchedule | 内存压力大时仍可调度 |
node.kubernetes.io/pid-pressure | NoSchedule | PID 压力大时仍可调度 |
node.kubernetes.io/unschedulable | NoSchedule | 节点不可调度时仍可调度 |
这些自动容忍确保 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
小结
本章我们学习了:
- DaemonSet 概念:确保每个节点运行一个 Pod
- 应用场景:日志收集、监控代理、网络插件
- 节点选择:nodeSelector、节点亲和性、污点容忍
- 更新策略:RollingUpdate 和 OnDelete
- 通信模式:Push、NodeIP、Service、DNS
- 最佳实践:优先级、资源限制、健康检查
练习
- 创建一个 DaemonSet,在每个节点上运行 Node Exporter
- 使用 nodeSelector 限制 DaemonSet 只在特定节点运行
- 更新 DaemonSet 的镜像版本并观察滚动更新过程
- 创建一个 Service 暴露 DaemonSet Pod