文章目录
Deployment 有哪些不足
Deployment 能够创建任意多个的 Pod 实例,并且维护这些 Pod 的正常运行,保证应用始终处于可用状态。Deployment 并不关心 Pod 在哪些节点上运行,只要 Pod 的数量足够,应用程序应该会正常工作。
但是有些业务是和运行环境相关的,它们并非完全独立于系统运行,而是与主机存在绑定关系,必须依附于节点才能产生价值:
- 网络应用如 kube-proxy,节点不运行这个 Pod 就无法加入 Kubernetes 网络
- 监控应用如 Prometheus,节点需要运行这个 Pod 报告节点运行状态
- 日志应用如 Fluentd,节点需要运行这个 Pod 来收集日志
- 安全应用,每个节点都要有一个 Pod 来执行安全审计、入侵检查、漏洞扫描等工作
这些业务如果用 Deployment 来部署就不太合适了,因为 Deployment 所管理的 Pod 数量是固定的,而且可能会在集群里漂移
,但,实际的需求却是要在集群里的每个节点上都运行 Pod,也就是说 Pod 的数量与节点数量保持同步
所以,Kubernetes 就定义了新的 API 对象 DaemonSet
,它在形式上和 Deployment 类似,都是管理控制 Pod,但管理调度策略却不同。
DaemonSet
确保全部(或者某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。
一、使用 YAML 描述 DaemonSet 对象
使用命令 kubectl api-resources
可以知道它的简称是 ds
。Kubernetes 不提供自动创建 DaemonSet YAML 样板的功能
,我们只能去 DaemonSet 的文档页面获取一个 DaemonSet 的 YAML 模板。我们把它拷贝下来,再去掉多余的部分,就可以做成自己的一份样板文件:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: redis-ds
labels:
app: redis-ds
spec:
selector:
matchLabels:
name: redis-ds
template:
metadata:
labels:
name: redis-ds
spec:
containers:
- image: redis:5-alpine
name: redis
ports:
- containerPort: 6379
这个 DaemonSet
对象的名字是 redis-ds
,镜像是 redis:5-alpine
。
在 spec
部分,DaemonSet
也有 selector
字段,匹配 template
里 Pod
的 labels
标签,这和 Deployment
对象几乎一模一样。
DaemonSet
在 spec
里没有 replicas
字段,意味着它不会在集群里创建多个 Pod 副本,而是要在每个节点上只创建出一个 Pod 实例。也就是说,DaemonSet
仅仅是在 Pod 的部署调度策略
上和 Deployment 不同,其他的都是相同的,某种程度上我们也可以把 DaemonSet 看做是 Deployment 的一个特例
。
所以我们只需要用 kubectl create
先创建出一个 Deployment
对象,然后把 kind
改成DaemonSet
,再删除 spec.replicas
就行了。
二、在 Kubernetes 里使用 DaemonSet
现在,让我们执行命令 kubectl apply
,把 YAML 发送给 Kubernetes,让它创建 DaemonSet
对象,再用 kubectl get
查看对象的状态:
$ kubectl apply -f redis-ds.yaml
daemonset.apps/redis-ds created
l$ kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
redis-ds 1 1 0 1 0 <none> 12s
$ kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
redis-ds 1 1 1 1 1 <none> 15s
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-ds-ktmrm 1/1 Running 0 22s 10.10.1.26 k8s-work01 <none> <none>
虽然我们没有指定 DaemonSet 里 Pod 要运行的数量,但它自己就会去查找集群里的节点,在节点里创建 Pod。因为我们的实验环境里有一个 Master 一个 Worker,而 Master 默认是不跑应用的,所以 DaemonSet 就只生成了一个 Pod,运行在了worker
节点上。
按照 DaemonSet 的本意,应该在每个节点上都运行一个 Pod 实例才对,但 Master 节点却被排除在外了,这就不符合我们当初的设想了。
为了应对 Pod 在某些节点的调度
和驱逐
问题,它定义了两个新的概念:污点(taint)
和 容忍度(toleration)
。
三、Taint 和 Toleration
污点
是 Kubernetes 节点的一个属性,它的作用也是给节点贴标签
,但为了不和已有的 labels 字段混淆,就改成了taint
。
和污点
相对的,就是 Pod 的容忍度
,顾名思义,就是 Pod 能否容忍
污点。
Kubernetes 在创建集群的时候会自动给节点 Node 加上一些污点
,方便 Pod 的调度和部署。你可以用 kubectl describe node
来查看 Master 和 Worker 的状态:
$ kubectl describe node k8s-master01
Name: k8s-master01
Roles: control-plane
···
Taints: node-role.kubernetes.io/control-plane:NoSchedule
···
$ kubectl describe node worker1
Name: k8s-worker01
Roles: <none>
···
Taints: <none>
···
可以看到,Master 节点默认有一个 taint,名字是 node-role.kubernetes.io/master
,它的效果是 NoSchedule
,也就是说这个污点会拒绝 Pod 调度到本节点上运行,而 Worker 节点的 taint
字段则是空的。这正是 Master 和 Worker 在 Pod 调度策略上的区别所在,通常来说 Pod 都不能容忍任何污点
,所以加上了 taint
属性的 Master 节点也就会无缘 Pod 了。
两种方法让 DaemonSet 在 Master 节点
(或者任意其他节点)上运行:
- 去掉
Master 节点上的 taint
,DaemonSet 自然就不需要再区分 Master/Worker
$ kubectl taint node master node-role.kubernetes.io/master:NoSchedule-
这种方法修改的是 Node 的状态,影响面会比较大,可能会导致很多 Pod 都跑到这个节点上运行,所以我们可以保留 Node 的污点
,为需要的 Pod 添加容忍度
,只让某些 Pod 运行在个别节点上,实现精细化
调度
- 为 Pod 添加字段 tolerations,让它能够
容忍
某些污点
,就可以在任意的节点上运行了
tolerations
是一个数组,里面可以列出多个被容忍
的污点
,需要写清楚污点
的名字、效果。比较特别是要用 operator
字段指定如何匹配污点
,一般我们都使用 Exists
,也就是说存在这个名字和效果的污点
。
如果我们想让 DaemonSet 里的 Pod 能够在 Master 节点上运行,就要写出这样的一个 tolerations
,容忍节点的
tolerations:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
operator: Exists
重新部署加上了容忍度
的 DaemonSet:
$ kubectl apply -f ds.yml
$ kubectl get ds -o wide
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR
redis-ds 2 2 2 2 2 <none> 53m redis redis:5-alpine name=redis-ds
需要特别说明的是:容忍度
并不是 DaemonSet 独有的概念,而是从属于 Pod
,所以理解了污点
和容忍度
之后,你可以在 Job/CronJob、Deployment 里为它们管理的 Pod 也加上 tolerations
,从而能够更灵活地调度应用。
点和容忍度的文档在这里:点我,有哪些污点、污点有哪些效果可以在这里找到。
四、静态 Pod
DaemonSet 是在 Kubernetes 里运行节点专属 Pod 最常用的方式,但它不是唯一的方式,Kubernetes 还支持另外一种叫静态 Pod
的应用部署手段
静态 Pod
非常特殊,它不受 Kubernetes 系统的管控,不与 apiserver、scheduler 发生关系,所以是静态
的。但既然它是 Pod,也必然会跑
在容器运行时上,也会有 YAML 文件来描述它,而唯一能够管理它的 Kubernetes 组件也就只有在每个节点上运行的 kubelet 了。
静态 Pod
的 YAML 文件默认都存放在节点的 /etc/kubernetes/manifests
目录下,它是 Kubernetes 的专用目录。
Kubernetes 的 4 个核心组件 apiserver、etcd、scheduler、controller-manager
原来都以静态 Pod 的形式存在的,这也是它们能够先于 Kubernetes 集群启动的原因。
DaemonSet YAML
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: redis-ds
labels:
app: redis-ds
spec:
selector:
matchLabels:
name: redis-ds
template:
metadata:
labels:
name: redis-ds
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
operator: Exists
containers:
- image: redis:5-alpine
name: redis
ports:
- containerPort: 6379
总结
- DaemonSet 的目标是为集群里的每个节点部署唯一的 Pod,常用于监控、日志等业务。
- DaemonSet 的 YAML 描述与 Deployment 非常接近,只是没有 replicas 字段。
污点
和容忍度
是与 DaemonSet 相关的两个重要概念,分别从属于 Node 和 Pod,共同决定了 Pod 的调度策略。- 静态 Pod 也可以实现和 DaemonSet 同样的效果,但它不受 Kubernetes 控制,必须在节点上纯手动部署,应当慎用。