learn-tech/专栏/Kubernetes入门实战课/19Daemonset:忠实可靠的看门狗.md
2024-10-16 06:37:41 +08:00

14 KiB
Raw Blame History

                        因收到Google相关通知网站将会择期关闭。相关通知内容
                        
                        
                        19 Daemonset忠实可靠的看门狗
                        你好我是Chrono。

上一次课里我们学习了Kubernetes里的一个新API对象Deployment它代表了在线业务能够管理多个Pod副本让应用永远在线还能够任意扩容缩容。

虽然Deployment非常有用但是它并没有完全解决运维部署应用程序的所有难题。因为和简单的离线业务比起来在线业务的应用场景太多太复杂Deployment的功能特性只覆盖了其中的一部分无法满足其他场景的需求。

今天我们就来看看另一类代表在线业务API对象DaemonSet它会在Kubernetes集群的每个节点上都运行一个Pod就好像是Linux系统里的“守护进程”Daemon

为什么要有DaemonSet

想知道为什么Kubernetes会引入DaemonSet对象那就得知道Deployment有哪些不足。

我们先简单复习一下Deployment它能够创建任意多个的Pod实例并且维护这些Pod的正常运行保证应用始终处于可用状态。

但是Deployment并不关心这些Pod会在集群的哪些节点上运行在它看来Pod的运行环境与功能是无关的只要Pod的数量足够应用程序应该会正常工作。

这个假设对于大多数业务来说是没问题的比如Nginx、WordPress、MySQL它们不需要知道集群、节点的细节信息只要配置好环境变量和存储卷在哪里“跑”都是一样的。

但是有一些业务比较特殊,它们不是完全独立于系统运行的,而是与主机存在“绑定”关系,必须要依附于节点才能产生价值,比如说:

网络应用如kube-proxy必须每个节点都运行一个Pod否则节点就无法加入Kubernetes网络。 监控应用如Prometheus必须每个节点都有一个Pod用来监控节点的状态实时上报信息。 日志应用如Fluentd必须在每个节点上运行一个Pod才能够搜集容器运行时产生的日志数据。 安全应用同样的每个节点都要有一个Pod来执行安全审计、入侵检查、漏洞扫描等工作。

这些业务如果用Deployment来部署就不太合适了因为Deployment所管理的Pod数量是固定的而且可能会在集群里“漂移”实际的需求却是要在集群里的每个节点上都运行Pod也就是说Pod的数量与节点数量保持同步。

所以Kubernetes就定义了新的API对象DaemonSet它在形式上和Deployment类似都是管理控制Pod但管理调度策略却不同。DaemonSet的目标是在集群的每个节点上运行且仅运行一个Pod就好像是为节点配上一只“看门狗”忠实地“守护”着节点这就是DaemonSet名字的由来。

如何使用YAML描述DaemonSet

DaemonSet和Deployment都属于在线业务所以它们也都是“apps”组使用命令 kubectl api-resources 可以知道它的简称是 ds YAML文件头信息应该是

apiVersion: apps/v1 kind: DaemonSet metadata: name: xxx-ds

不过非常奇怪kubectl 不提供自动创建DaemonSet YAML样板的功能也就是说我们不能用命令 kubectl create 直接创建出一个DaemonSet对象。

这个缺点对于我们使用DaemonSet的确造成了不小的麻烦毕竟如果用 kubectl explain 一个个地去查字段再去写YAML实在是太辛苦了。

不过Kubernetes不给我们生成样板文件的机会我们也可以自己去“抄”。你可以在Kubernetes的官网https://kubernetes.io/zh/docs/concepts/workloads/controllers/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使用了流行的NoSQL数据库Redis你也许对它很熟悉

把这份YAML和上节课里的Deployment对象简单对比一下你会发现

前面的 kind、metadata 是对象独有的信息,自然是不同的,但下面的 spec 部分DaemonSet也有 selector 字段,匹配 template 里Pod的 labels 标签和Deployment对象几乎一模一样。

再仔细观察我们就会看到DaemonSet在 spec 里没有 replicas 字段这是它与Deployment的一个关键不同点意味着它不会在集群里创建多个Pod副本而是要在每个节点上只创建出一个Pod实例。

也就是说DaemonSet仅仅是在Pod的部署调度策略上和Deployment不同其他的都是相同的某种程度上我们也可以把DaemonSet看做是Deployment的一个特例。

我还是把YAML描述文件画了一张图好让你看清楚与Deployment的差异

了解到这些区别现在我们就可以用变通的方法来创建DaemonSet的YAML样板了你只需要用 kubectl create 先创建出一个Deployment对象然后把 kind 改成 DaemonSet再删除 spec.replicas 就行了,比如:

export out="--dry-run=client -o yaml"

change "kind" to DaemonSet

kubectl create deploy redis-ds --image=redis:5-alpine $out

如何在Kubernetes里使用DaemonSet

现在,让我们执行命令 kubectl apply把YAML发送给Kubernetes让它创建DaemonSet对象再用 kubectl get 查看对象的状态:

看这张截图虽然我们没有指定DaemonSet里Pod要运行的数量但它自己就会去查找集群里的节点在节点里创建Pod。因为我们的实验环境里有一个Master一个Worker而Master默认是不跑应用的所以DaemonSet就只生成了一个Pod运行在了“worker”节点上。

暂停一下,你发现这里有什么不对劲了吗?

按照DaemonSet的本意应该在每个节点上都运行一个Pod实例才对但Master节点却被排除在外了这就不符合我们当初的设想了。

显然DaemonSet没有尽到“看门”的职责它的设计与Kubernetes集群的工作机制发生了冲突有没有办法解决呢

当然Kubernetes早就想到了这点为了应对Pod在某些节点的“调度”和“驱逐”问题它定义了两个新的概念污点taint和容忍度toleration

什么是污点taint和容忍度toleration

“污点”是Kubernetes节点的一个属性它的作用也是给节点“贴标签”但为了不和已有的 labels 字段混淆,就改成了 taint。

和“污点”相对的就是Pod的“容忍度”顾名思义就是Pod能否“容忍”污点。

我们把它俩放在一起就比较好理解了。集群里的节点各式各样有的节点“纯洁无瑕”没有“污点”而有的节点因为某种原因粘上了“泥巴”也就有了“污点”。Pod也脾气各异有的“洁癖”很严重不能容忍“污点”只能挑选“干净”的节点而有的Pod则比较“大大咧咧”要求不那么高可以适当地容忍一些小“污点”。

这么看来“污点”和“容忍度”倒是有点像是一个“相亲”的过程。Pod就是一个挑剔的“甲方”而“乙方”就是集群里的各个节点Pod会根据自己对“污点”的“容忍程度”来选择合适的目标比如要求“不抽烟不喝酒”但可以“无车无房”最终决定在哪个节点上“落户”。

Kubernetes在创建集群的时候会自动给节点Node加上一些“污点”方便Pod的调度和部署。你可以用 kubectl describe node 来查看Master和Worker的状态

kubectl describe node master

Name: master Roles: control-plane,master ... Taints: node-role.kubernetes.io/master:NoSchedule ...

kubectl describe node worker

Name: worker Roles: ... Taints: ...

可以看到Master节点默认有一个 taint名字是 node-role.kubernetes.io/master它的效果是 NoSchedule也就是说这个污点会拒绝Pod调度到本节点上运行而Worker节点的 taint 字段则是空的。

这正是Master和Worker在Pod调度策略上的区别所在通常来说Pod都不能容忍任何“污点”所以加上了 taint 属性的Master节点也就会无缘Pod了。

明白了“污点”和“容忍度”的概念你就知道该怎么让DaemonSet在Master节点或者任意其他节点上运行了方法有两种。

第一种方法是去掉Master节点上的 taint让Master变得和Worker一样“纯洁无瑕”DaemonSet自然就不需要再区分Master/Worker。

操作Node上的“污点”属性需要使用命令 kubectl taint然后指定节点名、污点名和污点的效果去掉污点要额外加上一个 -。

比如要去掉Master节点的“NoSchedule”效果就要用这条命令

kubectl taint node master node-role.kubernetes.io/master:NoSchedule-

因为DaemonSet一直在监控集群节点的状态命令执行后Master节点已经没有了“污点”所以它立刻就会发现变化然后就会在Master节点上创建一个“守护”Pod。你可以用 kubectl get 来查看这个变动情况:

但是这种方法修改的是Node的状态影响面会比较大可能会导致很多Pod都跑到这个节点上运行所以我们可以保留Node的“污点”为需要的Pod添加“容忍度”只让某些Pod运行在个别节点上实现“精细化”调度。

这就是第二种方法为Pod添加字段 tolerations让它能够“容忍”某些“污点”就可以在任意的节点上运行了。

tolerations 是一个数组,里面可以列出多个被“容忍”的“污点”,需要写清楚“污点”的名字、效果。比较特别是要用 operator 字段指定如何匹配“污点”,一般我们都使用 Exists也就是说存在这个名字和效果的“污点”。

如果我们想让DaemonSet里的Pod能够在Master节点上运行就要写出这样的一个 tolerations容忍节点的 node-role.kubernetes.io/master:NoSchedule 这个污点:

tolerations:

  • key: node-role.kubernetes.io/master effect: NoSchedule operator: Exists

现在我们先用 kubectl taint 命令把Master的“污点”加上

kubectl taint node master node-role.kubernetes.io/master:NoSchedule

然后我们再重新部署加上了“容忍度”的DaemonSet

kubectl apply -f ds.yml

你就会看到DaemonSet仍然有两个Pod分别运行在Master和Worker节点上与第一种方法的效果相同。

需要特别说明一下“容忍度”并不是DaemonSet独有的概念而是从属于Pod所以理解了“污点”和“容忍度”之后你可以在Job/CronJob、Deployment里为它们管理的Pod也加上 tolerations从而能够更灵活地调度应用。

至于都有哪些污点、污点有哪些效果我就不细说了Kubernetes官网文档https://kubernetes.io/zh/docs/concepts/scheduling-eviction/taint-and-toleration/)上都列的非常清楚,在理解了工作原理之后,相信你自己学起来也不会太难

什么是静态Pod

DaemonSet是在Kubernetes里运行节点专属Pod最常用的方式但它不是唯一的方式Kubernetes还支持另外一种叫“静态Pod”的应用部署手段。

“静态Pod”非常特殊它不受Kubernetes系统的管控不与apiserver、scheduler发生关系所以是“静态”的。

但既然它是Pod也必然会“跑”在容器运行时上也会有YAML文件来描述它而唯一能够管理它的Kubernetes组件也就只有在每个节点上运行的kubelet了。

“静态Pod”的YAML文件默认都存放在节点的 /etc/kubernetes/manifests 目录下它是Kubernetes的专用目录。

下面的这张截图就是Master节点里目录的情况

你可以看到Kubernetes的4个核心组件apiserver、etcd、scheduler、controller-manager原来都以静态Pod的形式存在的这也是为什么它们能够先于Kubernetes集群启动的原因。

如果你有一些DaemonSet无法满足的特殊的需求可以考虑使用静态Pod编写一个YAML文件放到这个目录里节点的kubelet会定期检查目录里的文件发现变化就会调用容器运行时创建或者删除静态Pod。

小结

好了今天我们学习了Kubernetes里部署应用程序的另一种方式DaemonSet它与Deployment很类似差别只在于Pod的调度策略适用于在系统里运行节点的“守护进程”。

简单小结一下今天的内容:

DaemonSet的目标是为集群里的每个节点部署唯一的Pod常用于监控、日志等业务。 DaemonSet的YAML描述与Deployment非常接近只是没有 replicas 字段。 “污点”和“容忍度”是与DaemonSet相关的两个重要概念分别从属于Node和Pod共同决定了Pod的调度策略。 静态Pod也可以实现和DaemonSet同样的效果但它不受Kubernetes控制必须在节点上纯手动部署应当慎用。

课下作业

最后是课下作业时间,给你留两个思考题:

你觉得DaemonSet和Deployment在用法上还有哪些不同它们分别适用于哪些场景 你觉得在Kubernetes里应该如何用好“污点”和“容忍度”这两个概念

欢迎留言分享你的想法,和其他同学一起参与讨论。我们下节课再见。