K8s: 控制器之DaemonSet对象和调度
6 )DaemonSet 仅在某些节点上运行 Pod。5 )DaemonSet Pod选择器。4 )DaemonSet Pod模板。3 )DaemonSet 必须字段。
DaemonSet
1 )概述
- 为了适配于不同的应用的部署场景K8s就提出了这个 DaemonSet 顾名思义就是后台进程的意思
- docker在运行的时候加一个参数
-d
意思就是后台运行 - 因为我们集群里面有很多任务是必须要在后台运行的
- 比如说,存储的进程,ceph 或 glusterd
- ceph 是一种开源的分布式多样存储的一个开源软件
- 它能够把集群的这些用于存储的Service以这种服务的方式跑在K8s集群上
- 底层它是一个分布式存储这么一个架构
- 然后另外的应务场景景就是日志, 如:logstash 或 flunentd
- 每一种应用都会输出各种各样的日志,包括集群自己本身的日志
- 它就适合于以这种DaemonSet的方式运行
- 还有就是监控的场景,包括监控的节点,如:Prometheus Node Exporter 或 collectd
- 所以,K8s所有的功能的设计围绕的都是一个主题
- 就是让云原生的应用,微服务在 K8s 上运行的更好
- 它所有的功能都是为了这个目的去设计的
- 每个功能,我们都应该去了解,它为什么会出现,以及解决了什么问题
2 )应用
-
$
vi ds-demo1.yaml
apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-elasticsearch namespace: kube-system # 注意在命名空间里 labels: k8s-app: fluentd-logging spec: selector: matchLabels: # DaemonSet 会通过这个 label 来寻找所有包含 fluentd-elasticsearch 名称的pod name: fluentd-elasticsearch template: metadata: labels: name: fluentd-elasticsearch spec: tolerations: # 容忍度,提供了一种规则匹配,比如是否可以在主节点上运行 # this toleration is to have the daemonset runnable on master nodes # remove it if your masters can't run pods - key: node-role.kubernetes.io/master effect: NoSchedule # 不允许在主节点运行 containers: - name: fluentd-elasticsearch image: quay.io/fluentd_elasticsearch/fluentd:v2.9.0 resources: limits: memory: 200Mi cpu: 100m memory: 200Mi volumeMounts: # 挂载主机的一些目录 - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true terminationGracePeriodSeconds: 30 volumes: # pv 挂载卷 - name: varlog hostPath: # 主机的路径 path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers
-
$
kubectl apply -f ds-demo1.yaml
daemonset.apps/fluentd-elasticsearch created
-
$
kubectl -n kube-system describe daemonset fluentd-elasticsearch
Name: fluentd-elasticsearch Selector: name=fluentd-elasticsearch Node-Selector: <none> Labels: k8s-app=fluentd-logging Annotations: deprecated.daemonset.template.generation: 1 Desired Number of Nodes Scheduled: 3 Current Number of Nodes Scheduled: 3 Number of Nodes Scheduled with Up-to-date Pods: 3 Number of Nodes Scheduled with Available Pods: 3 Number of Nodes Misscheduled: 0 Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed Pod Template: Labels: name=fluentd-elasticsearch Containers: fluentd-elasticsearch: Image: quay.io/fluentd_elasticsearch/fluentd:v2.9.0 Port: <none> Host Port: <none> Limits: cpu: 100m memory: 200Mi Environment: <none> Mounts: /var/lib/docker/containers from varlibdockercontainers (ro) /var/log from varlog (rw) Volumes: varlog: Type: HostPath (bare host directory volume) Path: /var/log HostPathType: varlibdockercontainers: Type: HostPath (bare host directory volume) Path: /var/lib/docker/containers HostPathType: Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulCreate 4m14s daemonset-controller Created pod: fluentd-elasticsearch-pf8d5 Normal SuccessfulCreate 4m14s daemonset-controller Created pod: fluentd-elasticsearch-nm682 Normal SuccessfulCreate 4m14s daemonset-controller Created pod: fluentd-elasticsearch-stmlc
-
$
kubectl get all -n kube-system
NAME READY STATUS RESTARTS AGE pod/coredns-7f6cbbb7b8-4vjd6 1/1 Running 6 (16h ago) 6d4h pod/coredns-7f6cbbb7b8-h895p 1/1 Running 6 (16h ago) 6d4h pod/etcd-master.k8s 1/1 Running 14 (16h ago) 6d4h pod/fluentd-elasticsearch-nm682 1/1 Running 0 5m44s pod/fluentd-elasticsearch-pf8d5 1/1 Running 0 5m44s pod/fluentd-elasticsearch-stmlc 1/1 Running 0 5m44s pod/kube-apiserver-master.k8s 1/1 Running 13 (16h ago) 6d4h pod/kube-controller-manager-master.k8s 1/1 Running 26 (16h ago) 6d4h pod/kube-proxy-78gzp 1/1 Running 7 (16h ago) 6d4h pod/kube-proxy-7rwvp 1/1 Running 7 (16h ago) 6d4h pod/kube-proxy-946qv 1/1 Running 6 (16h ago) 6d4h pod/kube-scheduler-master.k8s 1/1 Running 25 (16h ago) 6d4h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kube-dns ClusterIP 10.1.0.10 <none> 53/UDP,53/TCP,9153/TCP 6d4h NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE daemonset.apps/fluentd-elasticsearch 3 3 3 3 3 <none> 5m44s daemonset.apps/kube-proxy 3 3 3 3 3 kubernetes.io/os=linux 6d4h NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/coredns 2/2 2 2 6d4h NAME DESIRED CURRENT READY AGE replicaset.apps/coredns-7f6cbbb7b8 2 2 2 6d4h
-
$
kubectl -n kube-system describe pod/fluentd-elasticsearch-stmlc
Name: fluentd-elasticsearch-stmlc Namespace: kube-system Priority: 0 Node: master.k8s/10.211.55.13 Start Time: Tue, 23 Apr 2024 15:54:09 +0800 Labels: controller-revision-hash=6b74446c5d name=fluentd-elasticsearch pod-template-generation=1 Annotations: <none> Status: Running IP: 10.244.0.2 IPs: IP: 10.244.0.2 Controlled By: DaemonSet/fluentd-elasticsearch Containers: fluentd-elasticsearch: Container ID: docker://f581721a1865d49242ef4359b377cb71606d2263283502cc04f4f2b7cb643d95 Image: quay.io/fluentd_elasticsearch/fluentd:v2.9.0 Image ID: docker-pullable://quay.io/fluentd_elasticsearch/fluentd@sha256:54716d825ec9791ffb403ac17a1e82159c98ac6161e02b2a054595ad01aa6726 Port: <none> Host Port: <none> State: Running Started: Tue, 23 Apr 2024 15:54:55 +0800 Ready: True Restart Count: 0 Limits: cpu: 100m memory: 200Mi Requests: cpu: 100m memory: 200Mi Environment: <none> Mounts: /var/lib/docker/containers from varlibdockercontainers (ro) /var/log from varlog (rw) /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-z5jjq (ro) Conditions: Type Status Initialized True Ready True ContainersReady True PodScheduled True Volumes: varlog: Type: HostPath (bare host directory volume) Path: /var/log HostPathType: varlibdockercontainers: Type: HostPath (bare host directory volume) Path: /var/lib/docker/containers HostPathType: kube-api-access-z5jjq: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 3607 ConfigMapName: kube-root-ca.crt ConfigMapOptional: <nil> DownwardAPI: true QoS Class: Guaranteed Node-Selectors: <none> Tolerations: node-role.kubernetes.io/master:NoSchedule node.kubernetes.io/disk-pressure:NoSchedule op=Exists node.kubernetes.io/memory-pressure:NoSchedule op=Exists node.kubernetes.io/not-ready:NoExecute op=Exists node.kubernetes.io/pid-pressure:NoSchedule op=Exists node.kubernetes.io/unreachable:NoExecute op=Exists node.kubernetes.io/unschedulable:NoSchedule op=Exists Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 8m14s default-scheduler Successfully assigned kube-system/fluentd-elasticsearch-stmlc to master.k8s Normal Pulling 8m12s kubelet Pulling image "quay.io/fluentd_elasticsearch/fluentd:v2.9.0" Normal Pulled 7m28s kubelet Successfully pulled image "quay.io/fluentd_elasticsearch/fluentd:v2.9.0" in 44.107259915s Normal Created 7m28s kubelet Created container fluentd-elasticsearch Normal Started 7m28s kubelet Started container fluentd-elasticsearch
- 可以看到这个pod被分配到 master.k8s 节点上了,说明 DaemonSet 可以部署到 master 节点
- 同时,也可以部署到其他节点,它 和 Deployment 的重要区别是
- DaemonSet 在 node 节点的进程只有一个
- Deployment 在 node 节点的进程副本可以有很多
- 所以,每个节点部署一个 DaemonSet 用来收集node上的所有事情
3 )DaemonSet 必须字段
- 和所有其他 K8s 配置一样,DaemonSet 需要 apiVersion 、 kind 和 metadata 字段
- 有关配置文件的基本信息,DaemonSet 对象的名称必须是一个合法的,DaemonSet 也需要一个.spec 配置段
4 )DaemonSet Pod模板
- .spec 中唯一必需的字段是 .spec.template
- .spec.template 是一个Pod 模板,除了它是嵌套的,因而不具有 apiVersion或kind字段之外,它与Pod具有相同的 schema
- 除了Pod必需字段外,在 DaemonSet中的Pod模板必须指定合理的标签
- 在 DaemonSet 中的 Pod 模板必须具有一个值为 Always 的 RestartPolicy, 当该值未指定时,默认是 Always
5 )DaemonSet Pod选择器
- .spec.selector 字段表示 Pod 选择器,它与 Job 的 .spec.selector 的作用是相同的
- 从 K8s 1.8 开始,必须指定与 .spec.template 的标签匹配的 Pod 选择器
- 用户不指定 Pod 选择器时,该字段不再有默认值
- 选择器的默认值生成结果与 kubectl apply 不兼容
- 此外,一旦创建了 DaemonSet,它的 .spec.selector 就不能修改
- 修改 Pod 选择器可能导致 Pod 意外悬浮,并且这对用户来说是费解的
- spec.selector 是一个对象,如下两个字段组成:
matchLabels
- 与 ReplicationController的.spec.selector
的作用相同matchExpressions
- 允许构建更加复杂的选择器,可以通过指定 key、value 列表以及将 key 和 value 列表关联起来的 operator
- 当上述两个字段都指定时,结果会按逻辑与(AND)操作处理
- 如果指定了
.spec.selector
,必须与.spec.template.metadata.labels
相匹配- 如果与后者不匹配,则 DeamonSet 会被 API 拒绝
- 另外,通常不应直接通过另一个 DaemonSet 或另一个工作负载资源(例如 ReplicaSet) 来创建其标签与该选择器匹配的任何 Pod
- 否则,DaemonSet 控制器会认为这些 Pod 是由它创建的, K8s 不会阻止你这样做
- 你可能要执行此操作的一种情况是,手动在节点上创建具有不同值的 Pod 进行测试
6 )DaemonSet 仅在某些节点上运行 Pod
- 如果指定了
.spec.template.spec.nodeSelector
- DaemonSet 控制器将在能够与 Node 选择器 匹配的节点上创建 Pod
- 类似这种情况,可以指定
.spec.template.spec.affinity
- 之后 DaemonSet 控制器将在能够与节点亲和性 匹配的节点上创建 Pod
- 如果根本就没有指定,则 DaemonSet Controller 将在所有节点上创建 Pod
DaemonSet的调度
1 )概述
- 我们知道 DaemonSet,它是确保每个节点都运行该pod的一个副本
- 就是说节点上的后台任务,就只能一个副本, 如果我的后台任务在每个节点上跑两个副本
- 比如说,日志收集跑两个副本,这个数据就重复,然后呢,数据库里面会多很多冗余的记录
- 所以,这并不是用户期望的,所以DaemonSet的一个重要的任务就是保证节点上运行一个pod
- 现在我们看下 Daemon Pods 是如何被调度的
2 )通过默认调度器调度
-
DaemonSet 确保所有符合条件的节点都运行该 Pod 的一个副本
-
通常,运行 Pod 的节点由 K8s 调度器选择
-
不过,DaemonSet Pods 由 DaemonSet 控制器创建和调度
-
这就带来了以下问题:
- Pod 行为的不一致性:
- 正常 Pod 在被创建后等待调度时处于 Pending 状态
- DaemonSet Pods 创建后不会处于 Pending 状态下
- 这使用户感到困惑。
- [Pod 抢占] 由默认调度器处理
- 启用抢占后,DaemonSet 控制器将在不考虑 Pod 优先级
- 和 抢占 的情况下制定调度决策
- Pod 行为的不一致性:
-
ScheduleDaemonSetPods 允许您使用默认调度器而不是 DaemonSet 控制器来调度 DaemonSets
-
方法是将 NodeAffinity 条件而不是
.spec.nodeName
条件添加到 DaemonSet Pods -
默认调度器接下来将 Pod 绑定到目标主机, 如果 DaemonSet Pod 的节点亲和性配置已存在,则被替换
-
DaemonSet 控制器仅在创建或修改 DaemonSet Pod 时执行这些操作, 并且不会更改 DaemonSet 的 spec.template
nodeAffinity: # 节点亲和,可以供节点选择 requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchFields: - key: metadata.name operator: In values: - target-host-name
-
此外,系统会自动添加 node.kubernetes.io/unschedulable:NoSchedule 容忍度到 DaemonSet Pods
-
在调度 DaemonSet Pod 时,默认调度器会忽略 unschedulable 节点
3 )与 Daemon Pods 通信3种方式
- 与 DaemonSet 中的 Pod 进行通信的几种可能模式如下:
- NodeIP 和已知端口
- DaemonSet 中的 Pod 可以使用
hostPort
,从而可以通过节点 IP 访问到Pod - 客户端能通过某种方法获取节点 IP 列表,并且基于此也可以获取到相应的端口
- DaemonSet 中的 Pod 可以使用
- DNS
- 创建具有相同 Pod 选择器的无头服务通过使用
endpoints
资源 - 或从 DNS 中检索到多个 A 记录来发现 DaemonSet
- 比如: $
kubectl -n kube-system get pod | grep dns
coredns-7f6cbbb7b8-4vjd6 1/1 Running 6 (17h ago) 6d5h coredns-7f6cbbb7b8-h895p 1/1 Running 6 (17h ago) 6d5h
- 上面 coredns 服务就会记录集群中所有的A记录
- 创建具有相同 Pod 选择器的无头服务通过使用
- Service
- 创建具有相同 Pod 选择器的服务
- 并使用该服务随机访问到某个节点上的守护进程(没有办法访问到特定节点)
- NodeIP 和已知端口
4 )更新DaemonSet
- 如果节点的标签被修改,DaemonSet 将立刻向新匹配上的节点添加Pod, 同时删除不匹配的节点上的Pod
- 可以修改 DaemonSet 创建的 Pod,不过并非Pod的所有字段都可更新
- 下次当某节点(即使具有相同的名称)被创建时,DaemonSet 控制器还会使用最初的模板
- 可以删除一个 DaemonSet。如果使用 kubectl 并指定
--cascade=false
选项,则Pod将被保留在节点上 - 接下来如果创建使用相同选择器的新 DaemonSet,新的 DaemonSet 会收养已有的 Pod
- 如果有Pod 需要被替换,DaemonSet 会根据其 updateStrategy 来替换
5 )DaemonSet 的替代方案
- init 脚本
- 直接在节点上启动守护进程(例如使用
init
、upstartd
或systemd
)的做法当然是可行的 - 不过,基于 DaemonSet 来运行这些进程有如下一些好处:
- 像所运行的其他应用一样,DaemonSet 具备为守护进程提供监控和日志管理的能力
- 为守护进程和应用所使用的配置语言和工具(如 Pod 模板、 kubectl )是相同的
- 在资源受限的容器中运行守护进程能够增加守护进程和应用容器的隔离性
- 然而,这一点也可以通过在容器中运行守护进程但却不在 Pod 中运行之来实现
- 例如,直接基于 Docker 启动
- 直接在节点上启动守护进程(例如使用
6 )和Deployments的区别
- DaemonSet 与 Deployments非常类似, 它们都能创建 Pod
- 并且 Pod 中的进程都不希望被终止(例如,Web 服务器、存储服务器)
- 建议为无状态的服务使用 Deployments,比如前端服务
- 对这些服务而言,对副本的数量进行扩缩容、平滑升级,比精确控制 Pod 运行在某个主机上要重要得多
- 当需要Pod 副本总是运行在全部或特定主机上,并需要它们先于其他 Pod 启动时,应该使用 DaemonSet
更多推荐
所有评论(0)