一、简介

DaemonSet保证在每个Node上都运行一个Pod,如果 新增一个Node,这个Pod也会运行在新增的Node上,如果删除这个DadmonSet,就会清除它所创建的Pod。常用来部署一些集群日志收集,监控等全局应用。

常见的场景如下:
1、运行存储集群daemon,比如ceph,glusterd等;
2、运行一个日志收集daemon,比如logstash,fluentd等;
3、运行监控daemon,比如Prometheus Node Exporter,collectd,New Relic agent,Ganglia gmond等;

二、调度策略

通常情况下,通过DaemonSet创建的的Pod应该调度到那个节点是通过Kubernetes调度策略决定的,然而,当这个Pod被创建的时候,运行在那个节点上其实已经被提前决定了,所以它会忽略调度器。因此:

  • DaemonSet控制器并不在乎Node的unschedulable字段;
  • 即使调度器没有启动,DaemonSet控制器都可以创建Pod;

但是可以通过以下方法来让Pod运行到指定的Node上:

  • nodeSelector:只调度到匹配指定label的Node上;
  • nodeAffinity:功能更丰富的Node选择器,比如支持集合操作;
  • podAffinity:调度到满足条件的Pod所在的Node上;

2.1、nodeSelector

就是给需要运行在的Node打标签,比如只运行在有ssd硬盘的node上,那么我们就可以给这些node打一个标签:

  1. kubectl label nodes node-01 disktype=ssd

然后在DaemonSet的字段中定义nodeSelector为disktype=ssd:

  1. spec:
  2. nodeSelector:
  3. disktype: ssd

2.2、nodeAffinity

nodeAffinity目前支持两种:requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution,分别代表必须满足条件和优选条件。比如下面的例子代表调度到包含标签kubernetes.io/e2e-az-name并且值为e2e-az1或e2e-az2的Node上,并且优选还带有标签another-node-label-key=another-node-label-value的Node。

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: with-node-affinity
  5. spec:
  6. affinity:
  7. nodeAffinity:
  8. requiredDuringSchedulingIgnoredDuringExecution:
  9. nodeSelectorTerms:
  10. - matchExpressions:
  11. - key: kubernetes.io/e2e-az-name
  12. operator: In
  13. values:
  14. - e2e-az1
  15. - e2e-az2
  16. preferredDuringSchedulingIgnoredDuringExecution:
  17. - weight: 1
  18. preference:
  19. matchExpressions:
  20. - key: another-node-label-key
  21. operator: In
  22. values:
  23. - another-node-label-value
  24. containers:
  25. - name: with-node-affinity
  26. image: gcr.io/google_containers/pause:2.0

2.3、podAffinity

podAffinity基于Pod的标签来选择Node,仅调度到满足条件Pod所在的Node上,支持podAffinity和podAntiAffinity。这个功能比较绕,以下面的例子为例:

  • 如果一个“Node所在Zone中包含至少一个带有security=S1标签且运行中的Pod”,那么可以调度到该Node
  • 不调度到“包含至少一个带有security=S2标签且运行中Pod”的Node上
    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: with-pod-affinity
    5. spec:
    6. affinity:
    7. podAffinity:
    8. requiredDuringSchedulingIgnoredDuringExecution:
    9. - labelSelector:
    10. matchExpressions:
    11. - key: security
    12. operator: In
    13. values:
    14. - S1
    15. topologyKey: failure-domain.beta.kubernetes.io/zone
    16. podAntiAffinity:
    17. preferredDuringSchedulingIgnoredDuringExecution:
    18. - weight: 100
    19. podAffinityTerm:
    20. labelSelector:
    21. matchExpressions:
    22. - key: security
    23. operator: In
    24. values:
    25. - S2
    26. topologyKey: kubernetes.io/hostname
    27. containers:
    28. - name: with-pod-affinity
    29. image: gcr.io/google_containers/pause:2.0

三、更新

DaemonSet支持滚动更新,其定义字段为updateStrategy,可以通过kubectl explain ds.spec.updateStrategy查看。

  1. [root@master ~]# kubectl explain ds.spec.updateStrategy
  2. KIND: DaemonSet
  3. VERSION: extensions/v1beta1
  4. RESOURCE: updateStrategy <Object>
  5. DESCRIPTION:
  6. An update strategy to replace existing DaemonSet pods with new pods.
  7. FIELDS:
  8. rollingUpdate <Object>
  9. Rolling update config params. Present only if type = "RollingUpdate".
  10. type <string>
  11. Type of daemon set update. Can be "RollingUpdate" or "OnDelete". Default is
  12. OnDelete.

其rollingUpdate字段只有一个maxUnavailable,没有maxSurge,因为DaemonSet只允许在node上运行一个。
DaemonSet的更新策略有两个:

  • RollingUpdate:滚动更新
  • OnDelete:当删除Pod的时候更新,默认的更新策略;

四、例子

定义一个收集日志的DaemonSet,使用filebeat收集日志,通过filebeat收集日志传给redis:
# vim filebeat-ds.yaml

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: redis
  5. namespace: default
  6. spec:
  7. replicas: 1
  8. selector:
  9. matchLabels:
  10. app: redis
  11. role: cachedb
  12. template:
  13. metadata:
  14. labels:
  15. app: redis
  16. role: cachedb
  17. spec:
  18. containers:
  19. - name: redis
  20. image: redis:5.0.5-alpine
  21. ports:
  22. - name: redis
  23. containerPort: 6379
  24. ---
  25. apiVersion: v1
  26. kind: Service
  27. metadata:
  28. name: redis
  29. namespace: default
  30. spec:
  31. type: ClusterIP
  32. selector:
  33. app: redis
  34. role: cachedb
  35. ports:
  36. - port: 6379
  37. ---
  38. apiVersion: apps/v1
  39. kind: DaemonSet
  40. metadata:
  41. name: filebeat-ds
  42. namespace: default
  43. spec:
  44. selector:
  45. matchLabels:
  46. app: filebeat
  47. role: logstorage
  48. template:
  49. metadata:
  50. labels:
  51. app: filebeat
  52. role: logstorage
  53. spec:
  54. containers:
  55. - name: filebeat
  56. image: ikubernetes/filebeat:5.6.5-alpine
  57. env:
  58. - name: REDIS_HOST
  59. value: redis.default.svc.cluster.local

我们创建这个YAML文件

  1. # kubectl apply -f filebeat-ds.yaml

然后查看svc,pod的状态

  1. [root@master daemonset]# kubectl get svc
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 4d6h
  4. redis ClusterIP 10.68.213.73 <none> 6379/TCP 5s
  5. [root@master daemonset]# kubectl get pods
  6. NAME READY STATUS RESTARTS AGE
  7. filebeat-ds-pgmzt 1/1 Running 0 2m50s
  8. filebeat-ds-wx44z 1/1 Running 0 2m50s
  9. filebeat-ds-zjv68 1/1 Running 0 2m50s
  10. redis-85c7ccb675-ks4rc 1/1 Running 0 4m2s

然后我们进filebeat容器搞一点测试数据:

  1. # kubectl exec -it filebeat-ds-pgmzt -- /bin/bash
  2. # cd /var/log/containers/
  3. # echo "123" > a.log

然后进redis容器查看数据:

  1. # kubectl exec -it redis-85c7ccb675-ks4rc -- /bin/sh
  2. /data # redis-cli -h redis.default.svc.cluster.local -p 6379
  3. redis.default.svc.cluster.local:6379> KEYS *
  4. 1) "filebeat"
  5. redis.default.svc.cluster.local:6379>