概述
在前面几章中,我分别介绍了 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 示例:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: kube-system
labels:
k8s-app: node-exporter
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
version: v0.15.2
spec:
selector:
matchLabels:
k8s-app: node-exporter
version: v0.15.2
updateStrategy:
type: OnDelete
template:
metadata:
labels:
k8s-app: node-exporter
version: v0.15.2
spec:
priorityClassName: system-node-critical
containers:
- name: prometheus-node-exporter
image: "prom/node-exporter:v0.15.2"
imagePullPolicy: "IfNotPresent"
args:
- --path.procfs=/host/proc
- --path.sysfs=/host/sys
ports:
- name: metrics
containerPort: 9100
hostPort: 9100
volumeMounts:
- name: proc
mountPath: /host/proc
readOnly: true
- name: sys
mountPath: /host/sys
readOnly: true
resources:
limits:
memory: 50Mi
requests:
cpu: 100m
memory: 50Mi
hostNetwork: true
hostPID: true
volumes:
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
其实用法上和我们之前介绍的 Deployment 等都是差不多的,要定义 Pod 的模板。但是,因为 Node Exporter 是针对 Node 的 Pod,所以这个例子中做了一些特殊的处理,例如将 Host 中的一些系统文件透传给 Pod,并且让 Pod 使用主机网络等等,这些在我们后面的进阶介绍中会有讲到,这里就主要关注 DaemonSet 的用法就好了。
当在 Kubernetes 中应用这份配置之后,应该可以看到和 Node 数量一样多的 Pod 出现:
[root@liqiang.io]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
192.168.30.53 Ready,SchedulingDisabled master 24d v1.15.0
192.168.30.54 Ready node 24d v1.15.0
192.168.30.55 Ready node 24d v1.15.0
[root@liqinag.io]# kubectl get pods -n kube-system | grep exporter
node-exporter-4jhrs 1/1 Running 0 11m
node-exporter-p4dh5 1/1 Running 0 11m
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,例如:
...
spec:
selector:
matchLabels:
k8s-app: hdfs-namenode
updateStrategy:
type: OnDelete
template:
metadata:
labels:
k8s-app: hdfs-namenode
spec:
nodeSelector:
host: high-cpu
...
这样就保证了 Pod 只会在特定的 Host 中以 DaemonSet 的特性启动 Pod。