【探索 Kubernetes|作业管理篇 系列 15】DaemonSet 的”过人之处“

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 【探索 Kubernetes|作业管理篇 系列 15】DaemonSet 的”过人之处“

前言

大家好,我是秋意零。

在上一篇中,我们讲解了 StatefulSet 的存储状态;我们发现,它的存储状态,就是利用了 PV 与 PVC 的设计。StatefulSet 自动为我们创建 PVC 并且以 <pvc-name>-<pod-name>-<编号>命名,从而始终与 Pod 编号名一致的绑定。

需要注意的是:StatefulSet 的“滚动更新”是从最后一个 Pod 开始的,为了不破坏拓扑状态。如:主从数据库,主的 Pod 编号是 0,后面是从,如果先更新主数据库 0 编号,那么后面的从数据库就会出现问题。

StatefulSet 的“滚动更新”还允许我们进行更精细的控制,比如:金丝雀发布(Canary Deploy)或者灰度发布,这意味着应用的多个实例中被指定的一部分不会被更新到最新的版本。

StatefulSet 的 partition 字段设置为 2,那么编号小于 2 的 Pod 是不会进行镜像更新的

$ kubectl patch statefulset mysql -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset.apps/mysql patched

今天的内容是 DaemonSet 控制器。

👿 简介

  • 🏠 个人主页秋意零
  • 🧑 个人介绍:在校期间参与众多云计算相关比赛,如:🌟 “省赛”、“国赛”,并斩获多项奖项荣誉证书
  • 🎉 目前状况:24 届毕业生,拿到一家私有云(IAAS)公司 offer,暑假开始实习
  • 🔥 账号:各个平台, 秋意零 账号创作者、 云社区 创建者
  • 💕欢迎大家:欢迎大家一起学习云计算,走向年薪 30 万

正文开始

  • 快速上船,马上开始掌舵了(Kubernetes),距离开船还有 3s,2s,1s...

一、DaemonSet 介绍

DaemonSet 的主要作用,是在集群中运行一个类似守护进程的 Pod 服务(Daemon Pod)。

DaemonSet 管理 Pod 的三个特性:

  • 这个 Pod 运行在 Kubernetes 集群中,每一个节点(Node)上
  • DaemonSet 只会在每个节点创建一个 Pod(Daemon Pod);
  • 集群中有新节点加入时,DaemonSet 也会马上为这个新节点创建一个 Pod;当然当节点从集群中移除时,这个 Pod 也会回收。

实际上就是为每个节点只创建一个 Pod,就是像所谓的守护程序。

Daemon Pod 的意义

DaemonSet 机制听起来很简单,但是 Daemon Pod 的意义确实是非常重要的。如下:

  • 1.各种网络插件的 Agent 组件,用来处理这个节点上容器网络,必须运行在每一个节点上;
  • 2.各种存储插件的 Agent 组件,用来在这个节点上挂载远程存储目录,操作容器的 Volume 目录,也必须运行在每一个节点上;
  • 3.各种监控组件和日志组件,负责这个节点上的监控信息和日志搜集,也必须运行在每一个节点上。

需要注意的是,DaemonSet 运行的时机,很多时候比整个 Kubernetes 集群出现的时机都要早。

这个乍一听起来可能有点儿奇怪。但其实你来想一下:如果这个 DaemonSet 正是一个网络插件的 Agent 组件呢?

  • 首先要知道,在部署 Kubernetes 集群时,没有网络插件,节点的状态就会是 NotReady 状态(NetworkReady=false)。
  • 这个情况下,普通 Pod 是不能运行在集群中的。而为了让集群能正常运行 Pod 那么就需要使用 DaemonSet。

这里或许还有一个疑问,就是普通 Pod 在这种 NotReady 情况下不能运行,那 DaemonSet 就能运行吗?

二、DaemonSet 的“过人之处”

要理解 DaemonSet 的“过人之处”。首先,要了解 DaemonSet 的工作过程。

来看一个 DaemonSet 的 YAML 文件:

  • 可以看到,DaemonSet 的 YAML 与 Deployment 的 YAML 文件可以说是几乎一模一样只不过是没有 replicas 字段因为 DaemonSet 每个节点只需要一个副本,replicas 字段就没有必要存在了。
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: example-daemonset
  labels:
    app: example
spec:
  selector:
    matchLabels:
      app: example
  template:
    metadata:
      labels:
        app: example
    spec:
      tolerations:
      - key: node.kubernetes.io/unreachable
        operator: Exists
        effect: NoExecute
      containers:
      - name: example-container
        image: nginx:latest

那么,DaemonSet 又是如何保证每个 Node 节点上有且只有一个被管理的 Pod 的呢?这是一个典型的“控制器模型”能够处理的问题。

以上面 YAML 为例

1.DaemonSet Controller,首先从 Etcd 中获取所有 Node 列表,然后遍历所有的 Node,查找检查是否有 app: example标签的 Pod 在运行。

这个查找检查过程有三种情况:

  • 没有这种 Pod,那就需要在该 Node 节点上创建一个 Pod;
  • 有这种 Pod,需要判断是否为 1,大于 1 就删除;
  • 正好有这种 Pod,说明这个节点正常。

2.当查找到有这种 Pod 时,删除节点上多余的 Pod,很容易;当查找到没有这种 Pod 时,如何让 Pod 在指定 Node 节点上创建呢?通过两种方式:

2.1. 配置 DaemonSet 的节点亲和性(nodeAffinity)规则,指定它应该运行在哪些节点上。

节点亲和性(nodeAffinity):可以通俗的理解为,你更喜欢谁,就和谁靠的更近从而在一起(这属于调度内容,详细内容之后章节展开)。

Kubernetes 集群中的网络插件(calico)或者 kube-porxy 组件,它们都是 DaemonSet 控制器方式部署的。我们可以查看它们所管理的 Pod ,如图:

  • 可以看到 spec.affinity.nodeAffinity这个字段是 DaemonSet 默认为 Pod 添加的字段,是它创建 Pod 的机制。
  • requiredDuringSchedulingIgnoredDuringExecution:它的意思是说,这个 nodeAffinity 必须在每次调度的时候予以考虑。

上图中的 matchFields 字段(匹配字段)是我们的匹配 Node 节点的机制,上图中 key: metadata.namevalues: [master01]配置的哪里?看下图,它匹配的是 Node 节点的 YAML 文件中的 metadata.name字段。

2.2 此外,DaemonSet 还会给这个 Pod 自动加上另外一个与调度相关的字段,叫作 tolerations。这个字段意味着这个 Pod,会“容忍”(Toleration)某些 Node 的“污点”(Taint)。

Toleration 和 Taint(容忍和污点):可以通俗的理解为,你能忍受理解某人的缺点,也就不在意它的缺点了。

下面是 DaemonSet 自动生成的一组 tolerations 字段的完整格式:

tolerations:
- key: node.kubernetes.io/not-ready
  operator: Exists
  effect: NoExecute
- key: node.kubernetes.io/unreachable
  operator: Exists
  effect: NoExecute
- key: node.kubernetes.io/disk-pressure
  operator: Exists
  effect: NoSchedule
- key: node.kubernetes.io/memory-pressure
  operator: Exists
  effect: NoSchedule
- key: node.kubernetes.io/unschedulable
  operator: Exists
  effect: NoSchedule

上述 tolerations 字段包含了一系列的 toleration 条目,每个条目描述了容忍一个特定污点的规则。例如:

operator: Exists 表示只要存在该污点,就容忍该节点。

effect: NoSchedule 表示不在该节点上进行新的调度。可是,DaemonSet 自动地给被管理的 Pod 加上了这个特殊的 Toleration,就使得这些 Pod 可以忽略这个限制,继而保证每个节点上都会被调度一个 Pod。

  • node.kubernetes.io/not-ready :表示容忍 NotReady 状态的节点。
  • node.kubernetes.io/unreachable:容忍节点处于 Unreachable 状态。
  • node.kubernetes.io/disk-pressure:容忍节点存在磁盘压力。
  • node.kubernetes.io/memory-pressure:容忍节点存在内存压力。
  • node.kubernetes.io/unschedulable:容忍节点标记为不可调度。

这些 tolerations 使得 DaemonSet 的 Pod 能够在带有这些污点的节点上正常运行。

查看 calico 网络插件 的 Pod 上的 Toleration:

这时,你会想到,我在前面提到的 DaemonSet 的“过人之处”,其实就是依靠 Toleration 实现的。

三、DaemonSet 的镜像版本如何维护

  1. 创建 DaemonSet 的 YAML 文件:
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: example-daemonset
  labels:
    app: example
spec:
  selector:
    matchLabels:
      app: example
  template:
    metadata:
      labels:
        app: example
    spec:
      containers:
      - name: example-container
        image: nginx:latest
        imagePullPolicy: IfNotPresent

可以看到,现在每个 Node 节点上都有一个 example-daemonset(DaemonSet) 所管理的 Pod:

[root@master01 ~]# kubectl apply -f daemonSet.yaml
daemonset.apps/example-daemonset created
[root@master01 ~]# kubectl get pod -owide
NAME                      READY   STATUS    RESTARTS   AGE    IP              NODE       NOMINATED NODE   READINESS GATES
example-daemonset-bmfs5   1/1     Running   0          2m5s   10.244.5.14     worker01   <none>           <none>
example-daemonset-dgtp4   1/1     Running   0          2m5s   10.244.241.82   master01   <none>           <none>
  1. 镜像更新

DaemonSet 与 Deployment 一样都能进行版本管理。使用 kubectl rollout history查看:

[root@master01 ~]# kubectl rollout history daemonset example-daemonset
daemonset.apps/example-daemonset
REVISION  CHANGE-CAUSE
1         <none>

使用 kubectl set image命令修改镜像版本:

[root@master01 ~]# kubectl set image daemonset/example-daemonset --record example-container=nginx:alpine
Flag --record has been deprecated, --record will be removed in the future
daemonset.apps/example-daemonset image updated

使用kubectl rollout history命令再次查看更新版本记录:

[root@master01 ~]# kubectl rollout history daemonset example-daemonset
daemonset.apps/example-daemonset
REVISION  CHANGE-CAUSE
1         <none>
2         kubectl set image daemonset/example-daemonset example-container=nginx:alpine --record=true

一切皆对象

Deployment 管理这些版本,靠的是“一个版本对应一个 ReplicaSet 对象”。可是,DaemonSet 控制器操作的直接就是 Pod。那么,它的这些版本又是如何维护的呢?

所谓,一切皆对象!

在 Kubernetes 项目中,任何你觉得需要记录下来的状态,都可以被用 API 对象的方式实现。当然,“版本”也不例外。

Kubernetes v1.7 之后添加了一个 API 对象,叫 ControllerRevision,专门用来记录某种 Controller 对象的版本。

[root@master01 ~]# kubectl get controllerrevision
NAME                          CONTROLLER                         REVISION   AGE
example-daemonset-67d867976   daemonset.apps/example-daemonset   2          10m
example-daemonset-d8494f4bd   daemonset.apps/example-daemonset   1          20m
[root@master01 ~]# kubectl describe controllerrevision example-daemonset-67d867976

我们查看版本 2 是否是我们对应的版本:

  • 可以看到,Annotations 记录了,我们 kubectl 修改镜像版本的命令;以及 data 字段保存了该版本对应的完整的 DaemonSet 的 API 对象

将 DaemonSet 回滚到 Revision=1 时的状态:

  • kubectl describe controllerrevision可以看到它的 Revision 字段,而 kubectl rollout undo实际上读取到了 的 ControllerRevision 的对象保存的 Data 字段。而这个 Data 字段里保存的信息,就是 Revision=1 时这个 DaemonSet 的完整 API 对象。
[root@master01 ~]# kubectl rollout undo daemonset/example-daemonset --to-revision=1
daemonset.apps/example-daemonset rolled back
[root@master01 ~]#
[root@master01 ~]# kubectl describe controllerrevision example-daemonset-d8494f4bd

在执行完这次回滚完成后,你会发现,DaemonSet 的 Revision 并不会从 Revision=2 退回到 1,而是会增加成 Revision=3。这是因为,一个新的 ControllerRevision 被创建了出来。

[root@master01 ~]# kubectl rollout history daemonset/example-daemonset
daemonset.apps/example-daemonset
REVISION  CHANGE-CAUSE
2         kubectl set image daemonset/example-daemonset example-container=nginx:alpine --record=true
3         <none>
[root@master01 ~]# kubectl get controllerrevision
NAME                          CONTROLLER                         REVISION   AGE
example-daemonset-67d867976   daemonset.apps/example-daemonset   2          31m
example-daemonset-d8494f4bd   daemonset.apps/example-daemonset   3          41m

总结

通过上面的介绍,DaemonSet 的工作过程。

首先,是它的控制循环,查找检查所有 Node 节点上的 Pod 的情况,来决定是否创建或者删除一个 Pod;

其次,在创建 Pod 时,DaemonSet 会自动给这个 Pod 加上一个 nodeAffinity 和 Toleration,来保证 Pod 在指定节点运行,并且忽略 unschedulable “污点”;

最后,DaemonSet 使用 ControllerRevision,来保存和管理对应的“版本”。

StatefulSet 也是直接控制 Pod 对象的,那么它是不是也在使用 ControllerRevision 进行版本管理呢?

没错。在 Kubernetes 项目里,ControllerRevision 其实是一个通用的版本管理对象。这样,Kubernetes 项目就巧妙地避免了每种控制器都要维护一套冗余的代码和逻辑的问题。



相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
11月前
|
存储 Kubernetes 调度
k8s教程(pod篇)-DaemonSet(每个node上只调度一个pod)
k8s教程(pod篇)-DaemonSet(每个node上只调度一个pod)
209 0
|
8天前
|
Kubernetes 监控 调度
【赵渝强老师】K8s的DaemonSet控制器
DaemonSet控制器确保每个节点上运行一个Pod副本,适用于监控、日志收集等场景。通过示例创建DaemonSet并查看Pod信息,展示了其自动扩展和回收的能力。视频讲解和代码示例详细说明了DaemonSet的使用方法和调度机制。
|
3月前
|
Kubernetes 监控 调度
在K8S中,DaemonSet类型资源特性?
在K8S中,DaemonSet类型资源特性?
|
3月前
|
Prometheus Kubernetes 监控
在K8S中,DaemonSet类型的资源特性有哪些?
在K8S中,DaemonSet类型的资源特性有哪些?
|
3月前
|
Prometheus Kubernetes 监控
在k8S中,DaemonSet类型的资源特性有哪些?
在k8S中,DaemonSet类型的资源特性有哪些?
|
3月前
|
存储 Kubernetes 关系型数据库
Kubernetes(K8S) Controller - StatefulSet、DaemonSet 介绍
Kubernetes(K8S) Controller - StatefulSet、DaemonSet 介绍
33 0
|
5月前
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用问题之运行run-application --target kubernetes-application执行,通过进程的返回码来决定作业是否成功,任务返回码都是0,该怎么办
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
5月前
|
Kubernetes Oracle 关系型数据库
实时计算 Flink版操作报错合集之用dinky在k8s上提交作业,会报错:Caused by: org.apache.flink.table.api.ValidationException:,是什么原因
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
261 0
|
6月前
|
Kubernetes Java 流计算
在Kubernetes上运行Flink应用程序时,你可以使用Flink Kubernetes Client提供的命令来提交作业
在Kubernetes上运行Flink应用程序时,你可以使用Flink Kubernetes Client提供的命令来提交作业
98 6
|
11月前
|
Kubernetes Cloud Native NoSQL
TuGraph Analytics云原生部署:基于K8S Operator的轻量级作业启动方案
TuGraph Analytics作业可以通过Console提交部署到K8S集群,但Console是一个独立的Web系统,部署形态上相对较重。在平台工具系统接入或大数据生态集成场景中,需要更轻量级的快速接入TuGraph Analytics的方案。