概述

在前面几章中,我分别介绍了 Replicas,Deployment 以及 Job,似乎把 Pod 的一些常用操作都说完了,但其实不然,在还没上 Kubernetes 之前,我们的应用都是部署在裸机之上。然后,会出于高可用的考虑,有几台机器就在几台机器上部署上相同的应用(当然,DB 和 ZK 我们是例外的),看上去这个需求在 Kubernetes 上不是很合理,但是,有些时候却是需要的。

玩 CNCF 的同学应该知道,CNCF 第一个毕业的项目是 Kubernetes,那么你是否知道第二个毕业的项目是什么?哈哈,我直接告诉你,是 Prometheus,一个用于采集监控数据,并且执行报警规则的 Metric 数据处理应用,为什么我要提它呢,因为和它相关的 exporter 中有一个很重要的流行 Exporter 叫做 NodeExporter。Node Exporter 用于采集一个 Host 上的各种性能指标,并且暴露给 Prometheus 采集,即使你上了 Kubernetes,很多时候你都是需要关注 Kubernetes Node 所处的裸机的状态的,所以 Node Exporter 尤其不能少。

而 Node Exporter 需要被用于采集每一个 Host 的数据状态,所以这就产生了一种需求,在每个 Kubernetes 的机器上都要运行一个 Pod,而实现这个需求的 Kubernetes 资源类型叫做 DaemonSet,也正是本文要介绍的组件。

DaemonSet 功能

前面说了,DaemonSet 的功能就是保证每个 Node 都运行 Pod,但是,如果某个 Kubernetes 的 Node 下线之后,Kubernetes 不会在其他节点上再运行一个 Pod,还是保持每个 Node 一个 Pod,Node 掉了几个就少几个。但是,如果你往 Kubernetes 集群中加入一个新的 Node,Kubernetes 却是会在这个新的 Node 中加入一个 Pod,保证存活着的 Node 中都有 DaemonSet 的 Pod。

这里就给出一个我刚才描述的 Node Exporter 的 DaemonSet 示例:

  1. apiVersion: apps/v1
  2. kind: DaemonSet
  3. metadata:
  4. name: node-exporter
  5. namespace: kube-system
  6. labels:
  7. k8s-app: node-exporter
  8. kubernetes.io/cluster-service: "true"
  9. addonmanager.kubernetes.io/mode: Reconcile
  10. version: v0.15.2
  11. spec:
  12. selector:
  13. matchLabels:
  14. k8s-app: node-exporter
  15. version: v0.15.2
  16. updateStrategy:
  17. type: OnDelete
  18. template:
  19. metadata:
  20. labels:
  21. k8s-app: node-exporter
  22. version: v0.15.2
  23. spec:
  24. priorityClassName: system-node-critical
  25. containers:
  26. - name: prometheus-node-exporter
  27. image: "prom/node-exporter:v0.15.2"
  28. imagePullPolicy: "IfNotPresent"
  29. args:
  30. - --path.procfs=/host/proc
  31. - --path.sysfs=/host/sys
  32. ports:
  33. - name: metrics
  34. containerPort: 9100
  35. hostPort: 9100
  36. volumeMounts:
  37. - name: proc
  38. mountPath: /host/proc
  39. readOnly: true
  40. - name: sys
  41. mountPath: /host/sys
  42. readOnly: true
  43. resources:
  44. limits:
  45. memory: 50Mi
  46. requests:
  47. cpu: 100m
  48. memory: 50Mi
  49. hostNetwork: true
  50. hostPID: true
  51. volumes:
  52. - name: proc
  53. hostPath:
  54. path: /proc
  55. - name: sys
  56. hostPath:
  57. path: /sys

其实用法上和我们之前介绍的 Deployment 等都是差不多的,要定义 Pod 的模板。但是,因为 Node Exporter 是针对 Node 的 Pod,所以这个例子中做了一些特殊的处理,例如将 Host 中的一些系统文件透传给 Pod,并且让 Pod 使用主机网络等等,这些在我们后面的进阶介绍中会有讲到,这里就主要关注 DaemonSet 的用法就好了。

当在 Kubernetes 中应用这份配置之后,应该可以看到和 Node 数量一样多的 Pod 出现:

  1. [root@liqiang.io]# kubectl get nodes
  2. NAME STATUS ROLES AGE VERSION
  3. 192.168.30.53 Ready,SchedulingDisabled master 24d v1.15.0
  4. 192.168.30.54 Ready node 24d v1.15.0
  5. 192.168.30.55 Ready node 24d v1.15.0
  6. [root@liqinag.io]# kubectl get pods -n kube-system | grep exporter
  7. node-exporter-4jhrs 1/1 Running 0 11m
  8. node-exporter-p4dh5 1/1 Running 0 11m
  9. node-exporter-tbk9p 1/1 Running 0 11m

这样正如我们所预期的一样,也就是说 DaemonSet 和我们所学习到的特性是一致的。

额外的需求

虽然每个节点都运行一个 Pod 是一个确实存在的需求,但是,有的时候却又不是要求每个 Node 都要又一个 Pod,而是有一些特别的需求,例如我们有 10 台机器用于运行 HDFS,正如我们所知道的,HDFS 有 NameNode 和 DataNode 之分,那么 DataNode 可能是每个 Host 都要有的,但是 NameNode 却不是那么必须,可能只要 3-5 台就好了,这样有什么好处呢?一个很明显的好处就是节约成本,对于 NameNode 所在的节点,我们配置可以高一些,而只有 DataNode 的节点,配置可能可以相对调低,然后存储可以相对加强一些。

那么对于这样的需求,DaemonSet 能不能实现?毫无疑问,我会问基本上都是可以的。和其他的 Controller 一样,DaemonSet 也是支持 Selector 的,而且是 NodeSelector,通过我们给 Node 加上一些 Label,然后给 NameNode 加上一些 DaemonSet 的 NodeSelector,例如:

  1. ...
  2. spec:
  3. selector:
  4. matchLabels:
  5. k8s-app: hdfs-namenode
  6. updateStrategy:
  7. type: OnDelete
  8. template:
  9. metadata:
  10. labels:
  11. k8s-app: hdfs-namenode
  12. spec:
  13. nodeSelector:
  14. host: high-cpu
  15. ...

这样就保证了 Pod 只会在特定的 Host 中以 DaemonSet 的特性启动 Pod。