通过前面章节的学习,我们对 Kubernetes 中一些常见工作负载已经有所了解。比如无状态工作负载 Dployment 可以帮助我们运行指定数目的服务副本,并维护其状态,而对于有状态服务来说,我们同样可以采用 StatefulSet 来做到这一点。

但是,在实际使用的时候,有些场景,比如监控各个节点的状态,使用 Deployment 或者 StatefulSet 都无法满足我们的需求,因为这个时候我们可能会有以下这些需求。

  1. 希望每个节点上都可以运行一个副本,且只运行一个副本。虽然通过调整 spec.replicas 的数值,可以使之等于节点数目,再配合一些调度策略(我们后面讲调度原理的时候会深入解释)可以实现这一点。但是如果节点数目发生了变化呢?
  2. 希望在新节点上也快速拉起副本。比如集群扩容,这个时候会有一些新节点加入进来,如何立即感知到这些节点,并在上面部署新的副本。
  3. 希望节点下线的时候,对应的 Pod 也可以被删除。
  4. ……

Kubernetes 提供的 DaemonSet 就可以完美地解决上述问题,其主要目的就是可以在集群内的每个节点上(或者指定的一堆节点上)都只运行一个副本,即 Pod 和 Node 是一一对应的关系。DaemonSet 会结合节点的情况来帮助你管理这些 Pod,见下面的拓扑结构:

今天我们就来学习一下 DaemonSet,先来看看其主要的使用场景。

DaemonSet 的使用场景

跟 Deployment 和 StatefulSet 一样,DaemonSet 也是一种工作负载,可以管理一些 Pod。 通常来说,主要有以下的用法:

  • 监控数据收集,比如可以将节点信息收集上报给 Prometheus;
  • 日志的收集、轮转和清理;
  • 监控节点状态,比如运行 node-problem-detector 来监测节点的状态,并上报给 APIServer;
  • 负责在每个节点上网络、存储等组件的运行,比如 glusterd、ceph、flannel 等;

现在我们来尝试部署一个 DaemonSet。

部署你的第一个 DaemonSet

这里是一个 DaemonSet 的 YAML 文件:

apiVersion: apps/v1 # 这个地方已经不是 extension/v1beta1 了,在1.16版本已经废弃了,请使用 apps/v1
kind: DaemonSet # 这个是类型名
metadata:name: fluentd-elasticsearch # 对象名namespace: kube-system # 所属的命名空间labels:k8s-app: fluentd-logging
spec:selector:matchLabels:name: fluentd-elasticsearchtemplate:metadata:labels:name: fluentd-elasticsearchspec:containers:- name: fluentd-elasticsearchimage: quay.io/fluentd_elasticsearch/fluentd:v2.5.2volumeMounts:- name: varlogmountPath: /var/log- name: varlibdockercontainersmountPath: /var/lib/docker/containersreadOnly: truerestartPolicy: Alwaysvolumes:- name: varloghostPath:path: /var/log- name: varlibdockercontainershostPath:path: /var/lib/docker/containers

在这个 YAML 文件里,我们有一个地方需要特别注意,就 restartPolicy这个字段,它是缺省字段,默认值是 Always。而且如果你想显式地去设置,你也只能设置为 Always

其他的配置和写法,跟我们之前了解的 Deployment 和 StatefulSet 是类似的。

我们将上面 YAML 文件保存到本地的 fluentd-elasticsearch-ds.yaml 中,然后用 kubectl apply创建出来:

$ kubectl apply -f fluentd-elasticsearch-ds.yaml
daemonset.apps/fluentd-elasticsearch created

创建好后,我们来查看这个 DaemonSet 关联 Pod 的情况:

$ kubectl get pod -n kube-system -l name=fluentd-elasticsearch
NAME                          READY   STATUS    RESTARTS   AGE
fluentd-elasticsearch-m9zjb   1/1     Running   0          85s

可以看到,集群中只有一个 Pod 被创建了出来。我们再来看看集群中有多少个节点:

$ kubectl get node
NAME             STATUS   ROLES    AGE   VERSION
docker-desktop   Ready    master   22d   v1.16.6-beta.0

由于目前集群中就只有一个节点,所以 Kubernetes 只为这一个节点生成了 Pod。我们来查看下该 DaemonSet 的整体状态:

$ kubectl get ds -n kube-system fluentd-elasticsearch
NAME                    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
fluentd-elasticsearch   1         1         0       1            0           <none>          18

在 kubectl 使用的时候,我们常常将DaemonSet缩写成 ds。这里输出列表的含义如下:
DESIRED 代表该 DaemonSet 期望要创建的 Pod 个数,即我们需要几个,它跟节点数目息息相关;

  • CURRENT代表当前已经存在的 Pod 个数;
  • READY 代表目前已就绪的 Pod 个数;
  • UP-TO-DATE 代表最新创建的个数;
  • AVAILABLE代表目前可用的 Pod个数;
  • NODE SELECTOR表示节点选择标签,这个在 DaemonSet 中非常有用。有时候我们只希望在部分节点上运行一些 Pod,比如我们只节点上带有 app=logging-node 的节点上运行一些服务,就可以通过这个标签选择器来实现。

限定 DaemonSet 运行的节点

现在我们来看看如何限定一个 DaemonSet,让其只在某些节点上运行,比如只在带有 app=logging-node的节点上运行,可以看这张图:

此时,我们就可以通过 DaemonSet 的 selector 来实现:

apiVersion: apps/v1
kind: DaemonSet
metadata:name: my-dsnamespace: demoLabels:key: value
spec:selector: # 通过这个 selector,我们就可以让 daemonset pod 只在指定的节点上运行matchLabels:app: logging-nodetemplate:metadata:labels:name: my-daemonset-container...

这样一个 DaemonSet 就会只匹配集群中所有带有标签 app=logging-node 的节点,并分别在这些节点上运行一个 Pod。

如果集群中节点的标签发生了变化,这个时候DaemonSetsController会立刻为新匹配上的节点创建 Pod,同时删除不匹配的节点上的 Pod。

知道了这些我们再来看如何调度。

DaemonSet 的 Pod 是如何被调度的

早期 Kubernetes 是通过DaemonSetsController(在 kube- controller-manager 组件内以 goroutine 方式运行)调度 DaemonSet 管理的 Pod,这些 Pod 在创建的时候,就在 Pod 的 spec 中提前指定了节点名称,即 spec.nodeName。这些 Pod 由于指定了节点,所以不会经过默认调度器进行调度,这就导致了一些问题。

  • 不一致的 Pod 行为:其他的 Pod 都是通过默认调度器进行调度的,初始状态都是 Pending(等待调度),而 DaemonSet 的这些 Pod 的起始状态却不是 Pending。
  • DaemonSetsController 并不会感知到节点的资源变化;
  • 默认调度器的一些高级特性需要在 、DaemonSetsController 中二次实现。
  • 多组件负责调度会导致 Pod 抢占等功能实现起来非常困难;
  • ...

如果你有兴趣,你可以看看设计文档 Schedule DaemonSet Pods by default scheduler, not DaemonSet controller ,它详细介绍了DaemonSetsController调度时遇到的各种问题,并给出了详细的解决方案。

简单来说,DaemonSet Pod 依然由 DaemonSetsController 进行创建,但是不预先指定spec.nodeName了,而通过节点的亲和性,交由默认调度器进行调度。

我们回过头来看看上面fluentd-elasticsearch这个 DaemonSet 创建的 Pod:

$ kubectl get pod -n kube-system fluentd-elasticsearch-m9zjb -o yaml
apiVersion: v1
kind: Pod
metadata:creationTimestamp: "2020-09-25T12:01:31Z"generateName: fluentd-elasticsearch-labels:controller-revision-hash: 5b5b9c8855name: fluentd-elasticsearchpod-template-generation: "1"name: fluentd-elasticsearch-m9zjbnamespace: kube-systemownerReferences:- apiVersion: apps/v1blockOwnerDeletion: truecontroller: truekind: DaemonSetname: fluentd-elasticsearchuid: 33dc29aa-60b0-4486-8645-731daa85f25d...
spec:affinity:nodeAffinity: # daemonset 就是利用了 nodeAffinity 的能力requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchFields:- key: metadata.nameoperator: Invalues:- docker-desktopcontainers:- image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2imagePullPolicy: IfNotPresentname: fluentd-elasticsearch......

DaemonSetsController 创建的这个 Pod,自动添加了 spec.affinity.nodeAffinity指定节点的名称,替换以前spec.nodeName 的方式。

除了这个nodeAffinity之外,DaemonSetsController还会自动加一些 Toleration 到 Pod 中。有兴趣可以查看这个清单。我们在后续调度器章节,将统一介绍这些 Affinity 和 Toleration 的用法。

接下来,我们看看它的升级方法。

如何升级一个 Daemonset

升级一个 DaemonSet 其实非常简单,你可以通过kubectl edit 直接编辑对应的 DaemonSet 对象:

kubectl edit ds -n kube-system fluentd-elasticsearch

也可以直接在fluentd-elasticsearch-ds.yaml 中修改,然后使用kubectl apply

$ kubectl apply -f fluentd-elasticsearch-ds.yaml

那么修改后,DaemonSet 的这些 Pod 又是如何更新的呢?

DaemonSet 中定义了两种更新策略。

第一种是 OnDelete,顾名思义,当指定这种策略时,我们只有先手动删除原有的 Pod 才会触发新的 DaemonSet Pod 的创建。否则,不论你怎么修改 DaemonSet ,都不会触发新的 Pod 生成。第二种是 RollingUpdate,这是默认的更新策略,使用这种策略可以实现滚动更新,同时你还可以更精细地控制更新的范围,比如通过 maxUnavailable 为 1 来控制更新的速度(你也可以理解为更新时设置的步长),这表示每次只会先删除 1 个 Pod,待其重新创建并Ready 后,再更新同样的方法更新其他的 Pod。在更新期间,每个节点上最多只能有 DaemonSet 的一个 Pod。

我给你举个例子,下面这个 YAML 是一个使用了RollingUpdate更新策略的 DaemonSet :

apiVersion: apps/v1
kind: DaemonSet
metadata:name: fluentd-elasticsearchnamespace: kube-systemlabels:k8s-app: fluentd-logging
spec:selector:matchLabels:name: fluentd-elasticsearchupdateStrategy: # 这里指定了更新策略type: RollingUpdate # 进行滚动更新rollingUpdate:maxUnavailable: 1 # 这是默认的值template:metadata:labels:name: fluentd-elasticsearchspec:containers:- name: fluentd-elasticsearchimage: quay.io/fluentd_elasticsearch/fluentd:v2.5.2volumeMounts:- name: varlogmountPath: /var/log- name: varlibdockercontainersmountPath: /var/lib/docker/containersreadOnly: trueterminationGracePeriodSeconds: 30volumes:- name: varloghostPath:path: /var/log- name: varlibdockercontainershostPath:path: /var/lib/docker/containers

最后

从业务状态的角度来看,Kubernetes 使用 Deployment 和 StatefulSet 来分别支持无状态服务和有状态服务。而 DaemonSet 则从另外的一个角度来为集群中的所有节点提供基础服务,比如网络、存储等。

通过 DaemonSet,我们可以确保在所有满足条件的 Node 上只运行一个 Pod 实例,通常使用于日志收集、监控、网络等场景。Kubernetes 的组件 kube-prox 有时也可以借助 DaemonSet 拉起,这样每个节点上就会只运行一个 kube-proxy 的实例。Kubeadm 拉起的集群就是这样部署 kube-proxy 的。

同时 DaemonSet 也帮助我们解决了集群中节点动态变化时业务实例的部署和运维能力,比如扩容、缩容、节点宕机等场景。

欢迎大家扫码关注,获取更多信息

#yyds干货盘点# 如何在 Kubernete 中运行 DaemonSet 守护进程?(13)相关推荐

  1. #yyds干货盘点# 如何在 Kubernete 中做日志收集与管理(14)

    说到日志,你应该不陌生.日志中不仅记录了代码运行的实时轨迹,往往还包含着一些关键的数据.错误信息,等等.日志方便我们进行分析统计及监控告警,尤其是在后期问题排查的时候,我们通过日志可以很方便地定位问题 ...

  2. idea test包_6.Flinkx如何在idea中运行?

    Flinkx如何在idea中运行? 1.下载zip包 Flinkx链接 2.解压jar包 普通的解压软件解压即可 3.解压的bin目录 window环境下双击解压后的flinkx-1.8_releas ...

  3. 如何用xapmm测试php_如何在Xampp中运行PHP程序?

    成为经过认证的专业PHP是最流行的web后端编程语言.PHP代码将作为web服务器模块或命令行界面运行.要运行PHP for the web,您需要安装像Apache这样的web服务器,还需要像MyS ...

  4. 函数调用关系图如何画_程序是如何在 CPU 中运行的(二)

    笔者能力有限,如果文中出现错误的地方,还请各位朋友能够给我指出来,我将不胜感激,谢谢~ 前言 在上一篇文章中<程序是如何在 CPU 中运行的(一)>笔者讲述了程序中一条一条指令以及一条一条 ...

  5. 如何在docker中运行MySQL实例(转载)

    如何在docker中运行MySQL实例 转自:https://blog.csdn.net/siying8419/article/details/79670246 通常初学者学习docker时,不太清楚 ...

  6. 数据绑定如何在AngularJS中运行?

    本文翻译自:How does data binding work in AngularJS? How does data binding work in the AngularJS framework ...

  7. 如何在DW中运行PHP文件

    如何在DW中运行PHP文件 需要两步, 第一步安装appserver软件,第二步在DW中配置站点. 目录 如何在DW中运行PHP文件 一.安装appserver 二 .DW配置站点 三.后记 一.安装 ...

  8. 如何在Jupyter中运行R语言

    如有帮助,请随手点赞. 如何在Jupyter中运行R语言(两种解决方案) 简单方案 (1)Windows用户在Anaconda prompt中运行下面代码: conda install -c r r- ...

  9. php运行js代码,如何在PHP中运行JavaScript代码?(代码示例)

    JavaScript是客户端脚本语言,PHP是用于与数据库交互的服务器端脚本语言.那么如何如何在PHP中运行JavaScript?本篇文章就来给大家介绍几种在PHP中运行JavaScript的方法,希 ...

最新文章

  1. 关于学习Python的一点学习总结(33->继承中内置方法及多继承)
  2. 居然出错.谁能帮我解决一下.
  3. NHibernateLinq简单的CRUD操作
  4. Fiddler 4设置代理后无法上网的问题解决办法(亲测有效)
  5. 三十六、深入Vue.js组件Component(上篇)
  6. Redis 安装启动
  7. [html] 如何更改浏览器左上角标题旁的图标?
  8. 二.java下使用RabbitMQ实现hello world
  9. MySQL入门 (四) : JOIN 与UNION 查询
  10. 如何在Kaggle 首战中进入前 10%
  11. 南京晓庄学院大一第二学期计算机数据结构期末考试试卷及答案,南京晓庄学院数据结构题库参考答案...
  12. CTF学习笔记(杂项)
  13. 深度学习入门(九)——深度学习框架概览
  14. 【转载】教你怎么将centos7打造成桌面系统
  15. 为开发者而生 | 2021 SuperMap开发者大会议程全公布
  16. Android开启指纹验证
  17. 自己的服务器进不去显示403,HTTP 403错误:含义和解决方法
  18. 如何实现复制文本到剪贴板?
  19. android微信qq分享,android 一键分享 QQ 微信
  20. 关于光通信的最强进阶科普

热门文章

  1. DirectX学习资料汇总
  2. u盘锁计算机,最新U盘制作电脑锁方法
  3. MapReduce处理小文件合并
  4. (OK) (solved) How restore /cust partition - 华为全网通 honor 5x - KIW-AL10 - B228
  5. 原来无线接入点那么普及了
  6. 掉光的头发竟然又长出来了!这瓶泰国变态生姜水,连秃头都能救一下!
  7. ViewPager底部圆点指示器
  8. Video 全屏播放、禁止拖动进度条、禁止下载
  9. IP地址与长整数之间的转换详解
  10. 可以识别U盘,但是双击的时候出现Please inert disk into removable drivers?