写在前面
- 学习
k8s
遇到整理笔记 - 感觉不管是
dev
还是ops
相关的框架,都离不开生命周期
,钩子
,守护进程
这些,还是要多看看底层的东西. - K8s中的
DeamonSet
可以理解为特殊的ReplicaSet
,即确保每个节点只运行一个pod副本
生命周期
与集群中工作节点(nodes)的周期相同- 类比 Linux 系统中的守护进程(
systemd
),进程中的守护线程
等 - 博文内容涉及:
DeamonSet
的基本理论,应用场景(创建,新增节点自动调度)
介绍- 有污点的节点调度
DeamonSet
Demo Daemon Pods
的调度原理Daemon Pods
如何实现镜像更新DeamonSet
的替代方案- 部分理论是书里的,读不太懂,以后慢慢消化
人生真难,但不配让我认输 ---沃金
DaemonSet
DaemonSet
是K8s中相对特殊的一个控制器,即确保全部节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod
。 当有节点从集群移除时,这些Pod
也会被回收。删除DaemonSet
将会删除它创建的所有 Pod
。即基于工作节点的单Pod实例,每个节点只跑一个pod
DaemonSet
应用场景
DaemonSet 的一些典型用法:
- 在每个Node上运行一个
GlusterFS存储
或者Ceph存储
的Daemon进程
- 在每个Node上运行一个
日志采集程序
,例如Fluentd
或者Logstach
. - 在每个Node上运行一个
性能监控程序
,采集该Node
的运行性能数据
,例如Prometheus 中的Node Exporter
,collectd
,New Relic agent
或者Ganglia gmond
等。
DaemonSet
的Pod调度策略
与deplay
类似,除了使用系统内置的算法在每台Node上进行调度,也可以在Pod
的定义中使用NodeSelector
或NodeAffinity
来指定满足条件的Node
范围进行调度。
学习环境准备: 新建一个命名空间
代码语言:javascript复制┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$dir=k8s-daemonset-create;mkdir $dir;cd $dir
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl config current-context
kubernetes-admin@kubernetes
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl create ns liruilong-dameonset-create
namespace/liruilong-dameonset-create created
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl config set-context $(kubectl config current-context) --namespace=liruilong-daemonset-create
Context "kubernetes-admin@kubernetes" modified.
kubeadm中的Deamonset
我们使用kubeadm
安装的k8s
环境,有很多组件使用了DaemonSet
,比如calico
是网路相关,所有节点都需要有,kube-proxy
是代理相关,用于负载均衡等操作
查看系统命名空间里的DS
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-ReplicationController]
└─$kubectl get ds -A
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-system calico-node 3 3 3 3 3 kubernetes.io/os=linux 4d23h
kube-system kube-proxy 3 3 3 3 3 kubernetes.io/os=linux 4d23h
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-ReplicationController]
└─$
Demonset的创建
deamonset
和deployment
只有在kind
的位置不同,所以我们可以拷贝deployment
的模板进行修改,通过命令生成deployment
生成文件修改
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl create deployment myds1 --image=nginx --dry-run=client -o yaml > deamonset.yaml
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$vim deamonset.yaml
资源文件 deamonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
creationTimestamp: null
labels:
app: myds1
name: myds1
spec:
#replicas: 1
selector:
matchLabels:
app: myds1
#strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: myds1
spec:
containers:
- image: nginx
name: nginx
resources: {}
#status: {}
我们创建一个deamonset
,当前只有master节点
和一个node节点
正常工作, 因为master节点有污点,所以只允许一个deamon Pod
在工作节点运行
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl apply -f deamonset.yaml
daemonset.apps/myds1 created
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl get nodes
NAME STATUS ROLES AGE VERSION
vms81.liruilongs.github.io Ready control-plane,master 4d22h v1.22.2
vms82.liruilongs.github.io Ready <none> 4d22h v1.22.2
vms83.liruilongs.github.io NotReady <none> 4d22h v1.22.2
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl get pods
NAME READY STATUS RESTARTS AGE
myds1-fbmhp 1/1 Running 0 35s
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$
节点加入集群自动新增节点pod
我们在启动一台机器,会发现,新加入的vms83.liruilongs.github.io
节点自动运行一个deamon Pod
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl get nodes
NAME STATUS ROLES AGE VERSION
vms81.liruilongs.github.io Ready control-plane,master 4d22h v1.22.2
vms82.liruilongs.github.io Ready <none> 4d22h v1.22.2
vms83.liruilongs.github.io Ready <none> 4d22h v1.22.2
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl get pods
NAME READY STATUS RESTARTS AGE
myds1-prldj 1/1 Running 0 6m13s
myds1-pvwm4 1/1 Running 0 10m
Deamonset污点节点加入pod
那如何在有污点的节点加入Ds的Pod呢,下面我们从新修改deamonset
资源文件,容忍有污点的节点
tolerations:
- operator: Exists
容忍污点的资源文件deamonsettaint.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
creationTimestamp: null
labels:
app: myds1
name: myds1
spec:
#replicas: 1
selector:
matchLabels:
app: myds1
#strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: myds1
spec:
terminationGracePeriodSeconds: 0
tolerations:
- operator: Exists
containers:
- image: nginx
name: nginx
resources: {}
#status: {}
代码语言:javascript复制┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl apply -f deamonsettaint.yaml
daemonset.apps/myds1 created
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl get pods
NAME READY STATUS RESTARTS AGE
myds1-8tsnz 0/1 ContainerCreating 0 3s
myds1-9l6d9 0/1 ContainerCreating 0 3s
myds1-wz44b 0/1 ContainerCreating 0 3s
会发现每个节点都运行一个deamontset相关的pod
当然,如果我们不想所以有污点的节点都运行deamonset相关pod,那么我们可以使用另一种指定kye的方式,指定部分节点容忍污点
查看节点的污点标识
代码语言:javascript复制┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl describe nodes vms81.liruilongs.github.io | grep Taint
Taints: node-role.kubernetes.io/master:NoSchedule
资源文件定义
代码语言:javascript复制apiVersion: apps/v1
kind: DaemonSet
metadata:
creationTimestamp: null
labels:
app: myds1
name: myds1
spec:
selector:
matchLabels:
app: myds1
template:
metadata:
creationTimestamp: null
labels:
app: myds1
spec:
terminationGracePeriodSeconds: 0
tolerations:
- operator: Exists
key: node-role.kubernetes.io/master
effect: "NoSchedule"
containers:
- image: nginx
name: nginx
resources: {}
会发现deamonset可以运行在master和node节点
代码语言:javascript复制┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl apply -f deamonsetaint.yaml
daemonset.apps/myds1 created
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-daemonset-create]
└─$kubectl get pods
NAME READY STATUS RESTARTS AGE
myds1-f7hbb 0/1 ContainerCreating 0 4s
myds1-hksp9 0/1 ContainerCreating 0 4s
myds1-nnmzp 0/1 ContainerCreating 0 4s
Daemon Pods 是如何被调度的
DaemonSet
确保所有符合条件的节点都运行该 Pod 的一个副本。 通常,运行 Pod
的节点由 Kubernetes
调度器选择。 不过,DaemonSet Pods
由 DaemonSet
控制器(DaemonSetsController)创建和调度。这就带来了以下问题:
Pod 行为
的不一致性 :正常 Pod 在被创建后等待调度时处于Pending
状态,DaemonSet Pods
创建后不会处于Pending
状态下。- Pod 抢占 由默认调度器(
kube-scheduler
)处理,DaemonSet 控制器
将在不考虑 Pod 优先级和抢占 的情况下制定调度决策。 - DaemonSetsController 没有办法在节点资源变动时收到通知
- DaemonSetsController 没有办法遵循 Pod 的亲和性和反亲和性设置
- DaemonSetsController 可能须要二次实现 Pod 调度的重要逻辑,形成了重复的代码逻辑
代码语言:javascript复制
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
与 Daemon Pods 通信
DaemonSet 中的 Pod 进行通信的几种可能模式如下(不太懂先记下来): |
---|
推送(Push):配置 DaemonSet 中的 Pod,将更新发送到另一个服务,例如统计数据库。 这些服务没有客户端。 |
NodeIP 和已知端口:DaemonSet 中的 Pod 可以使用 hostPort,从而可以通过节点 IP 访问到 Pod。客户端能通过某种方法获取节点 IP 列表,并且基于此也可以获取到相应的端口。 |
DNS:创建具有相同 Pod 选择算符的 无头服务, 通过使用 endpoints 资源或从 DNS 中检索到多个 A 记录来发现 DaemonSet。 |
Service:创建具有相同 Pod 选择算符的服务,并使用该服务随机访问到某个节点上的 守护进程(没有办法访问到特定节点)。 |
更新 DaemonSet
如果工作节点的标签被修改,DaemonSet
将立刻向新匹配上的节点添加 Pod, 同时删除不匹配的节点上的 Pod。可以修改DaemonSet
创建的 Pod
。不过并非Pod
的所有字段都可更新。 下次当某节点(即使具有相同的名称)被创建时,DaemonSet
控制器还会使用最初的模板。
可以修改 DaemonSet
创建的 Pod。不过并非 Pod
的所有字段都可更新。 下次当某节点(即使具有相同的名称)被创建时,DaemonSet 控制器还会使用最初的模板。
删除一个 DaemonSet
。如果使用 kubectl 并指定--cascade=orphan
选项, 则Pod
将被保留在节点上。接下来如果创建使用相同选择器的新 DaemonSet
, 新的 DaemonSet
会收养已有的 Pod
。 如果有 Pod 需要被替换,DaemonSet
会根据其updateStrategy
来替换。
- OnDelete : 使用
OnDelete
更新策略时,在更新DaemonSet
模板后,只有当你手动删除老的DaemonSet pods
之后,新的DaemonSet Pod
才会被自动创建。跟Kubernetes 1.6
以前的版本类似。 - RollingUpdate : 这是默认的更新策略。使用
RollingUpdate
更新策略时,在更新DaemonSet
模板后, 老的DaemonSet pods
将被终止,并且将以受控方式自动创建新的DaemonSet pods
。 更新期间,最多只能有DaemonSet
的一个 Pod 运行于每个节点上。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
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.5.2
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
对 DaemonSet 执行滚动更新,K8s 官方文档中有很详细的描述,感兴趣小伙伴可以看看,https://kubernetes.io/zh/docs/tasks/manage-daemon/update-daemon-set/
DaemonSet 的替代方案
init 脚本
直接在节点上启动守护进程(例如使用 init、upstartd
或 systemd
)的做法当然是可行的。 不过,基于 DaemonSet
来运行这些进程有如下一些好处:
- 像所运行的其他应用一样,
DaemonSet
具备为守护进程提供监控和日志管理的能力。 - 为守护进程和应用所使用的配置语言和工具(如 Pod 模板、kubectl)是相同的。
- 在资源受限的容器中运行守护进程能够增加守护进程和应用容器的隔离性。 然而,这一点也可以通过在容器中运行守护进程但却不在 Pod 中运行之来实现。 例如,直接基于 Docker 启动。
裸 Pod
直接创建 Pod
并指定其运行在特定的节点上也是可以的。 然而,DaemonSet 能够替换由于任何原因(例如节点失败、例行节点维护、内核升级) 而被删除或终止的 Pod
。 由于这个原因,你应该使用 DaemonSet 而不是单独创建 Pod。
静态 Pod
通过在一个指定的、受 kubelet
监视的目录下编写文件来创建 Pod
也是可行的。 这类 Pod
被称为静态 Pod。 不像 DaemonSet
,静态 Pod 不受 kubectl
和其它 Kubernetes API
客户端管理。 静态 Pod 不依赖于 API 服务器,这使得它们在启动引导新集群的情况下非常有用。 此外,静态 Pod 在将来可能会被废弃。
Deployments
DaemonSet
与 Deployments
非常类似, 它们都能创建 Pod,并且 Pod
中的进程都不希望被终止(例如,Web 服务器、存储服务器)。建议为无状态的服务使用 Deployments
,比如前端服务。 对这些服务而言,对副本的数量进行扩缩容、平滑升级,比精确控制 Pod 运行在某个主机上要重要得多。 当需要 Pod
副本总是运行在全部或特定主机上,并且当该 DaemonSet
提供了节点级别的功能(允许其他 Pod 在该特定节点上正确运行)时, 应该使用 DaemonSet。
例如,网络插件通常包含一个以 DaemonSet
运行的组件。 这个 DaemonSet
组件确保它所在的节点的集群网络正常工作
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-ReplicationController]
└─$kubectl get ds -A
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-system calico-node 3 3 3 3 3 kubernetes.io/os=linux 4d23h
kube-system kube-proxy 3 3 3 3 3 kubernetes.io/os=linux 4d23h
┌──[root@vms81.liruilongs.github.io]-[~/ansible/k8s-ReplicationController]
└─$