Deployment可以用于部署无状态的应用,例如系统的接口层或者逻辑层,而多个Pod可以用于负载均衡和容灾。如果有这样一个需求,需要在集群的每个节点上都部署一个Pod,如果使用Deployment该怎么配置呢?如果每个节点都有一个Pod,副本数就跟集群的节点数相同,而集群的节点数量是可能变化的,那Deployment中怎么设置replicas字段呢?
为了解决这样的场景,k8s提供了DaemonSet:在每个节点上都部署一个Pod。
DaemonSet的使用场景通常有:
既然DaemonSet保证每个Node上都有一个Pod,当Node的数量变化时,Pod的数量也会随之变化,从而保证Pod的数量跟Node的数量一致。
与Deployment相比,DaemonSet的spec部分主要有两个区别:
其他的minReadySeconds、revisionHistoryLimit、selector、template等字段一模一样。
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:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
上面是k8s官方文档的一个示例:将主机上的/var/log目录挂载到容器的/var/log目录,在每个节点部署一个fluentd用于采集/var/log中的日志,这里还设置了Pod的资源限制和容忍,保证该Pod可以运行在包含master在内的所有节点,其中,spec.strategy字段没有设置,用的默认的滚动更新。
跟Deployment类似,DaemonSet的更新通常也是更新镜像,如果一个集群有5000台机器,当使用kubectl set image
更新某个DaemonSet资源时,默认的滚动更新策略会一步一步将所有节点的Pod进行更新,当需要回滚时,又需要对所有Pod进行回滚,机器比较多的情况下,升级和回滚的风险和耗时都是比较高的。
这里提供一种通过使用节点亲和性控制Pod升级频率的方式。
节点亲和性:只在包含某些标签的节点上部署Pod,例如,只在ssd类型的节点上部署Pod,可以给ssd类型的节点加上disk-type=ssd的标签,然后就可以通过节点亲和性让Pod只运行在这些节点上。
对于DaemonSet,也可以使用节点亲和性:
# pod.spec
affinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExperssions:
- key: kubernetes.io/hostname
operator: NotIn
values: []
默认情况下,Pod会运行在所有节点,如果需要控制让Pod只运行在某些节点,可以把其他节点加入此处的values数组中。
例如,如果集群有3个节点,分别是master、node1、node2,初始时可以将3个节点的名称放到数组中:
# pod.spec
affinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExperssions:
- key: kubernetes.io/hostname
operator: NotIn
values: ["master", "node1", "node2"]
此时,Pod不会运行在任何一个节点,当要让node1上的Pod运行时,可以将node1从values数组中删除:
# pod.spec
affinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExperssions:
- key: kubernetes.io/hostname
operator: NotIn
values: ["master", "node2"]
此时,Pod就会运行在node1上。
通过这种方式就可以控制DaemonSet的Pod安装和更新的频率,当然,修改values数组的操作肯定还是以来其他的程序完成的。
Deployment和DaemonSet是日常部署过程中常用的两种部署方式,Deployment用于无状态应用的部署,DaemonSet用于全部节点的部署,所以,如果你的应用只是提供服务,不需要有稳定的存储,数据丢了也不要紧,那就选择Deployment,如果你的程序需要运行在每个节点上,常见的如agent应用(数据采集、日志上报),那就选择DaemonSet。