介绍
daemonset 是众多控制器中的一种(其他还有deployment等等),它的作用是保证在每个节点只部署一个Pod,而且是只在pod 模板中定义的标签的节点上保证运行一个pod。
如果节点下线, DaemonSet不会在其他地方重新创建pod。但是,当将 一个新节点添加到集群中时, DaemonSet会立刻部署一个新的pod实例 。
与daemonset 相比副本控制器(rc或rs)是保证集群有固定数量的pod ,而不一定是均匀的每个节点一个这样分布。
删除了 pod那么它也会重新启个新的pod 。与ReplicaSet一样,DaemonSet 从配 pod 模板创建pod。
![k8s控制器DamenSet_docker](https://s2.51cto.com/images/blog/202211/08180341_636a297d472a183371.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
主要特征
- 这个 Pod 运行在 Kubernetes 集群里的每一个节点(Node)上;
- 每个节点上只有一个这样的 Pod 实例;
- 当有新的节点加入 Kubernetes 集群后,该 Pod 会自动地在新节点上被创建出来;而当旧节点被删除后,它上面的 Pod也相应地会被回收掉。
使用场景
包括pod执行系统级别的与基础结构相关的操作。例如,希望在每个节点上运行日志收集器和资源监控器。另 一个典型的例子是Kubemetes 自己的 kube-proxy进程,它需要运行在所有节点上才能使服务工作。
- 在集群的每个节点上运行存储 Daemon,比如 分布式存储 glusteFS 或 ceph。
- 在每个节点上运行日志收集 Daemon,比如 flunentd 或 logstash。
- 在每个节点上运行监控 Daemon,比如 Prometheus Node Exporter 或 collectd。
DaemonSet只在特定的结点上运行pod
DaemonSet pod 默认部署到集群中所有节点上,除非指定这些pod在部分节点上运行,这是通过pod模板中的nodeSelector 属性指定的,这是 DaemonSet定义的一部分似于RSet和RC中的 pod 模板。
注意节点可以被设置为不可调度防止 pod 被部署到节点上,但是DaemonSet会把pod部署到这些节点上,因为无法调度的属性只会被调度器(例如deployment等)使用,而 Daemon Set管理 pod 完全绕过调度器这是预期的,因为DaemonSet 的目的是运行系统服务,即使是在不可调度的节点上,系统服务通常也需要运行。
其实 Kubernetes 自己就在用 DaemonSet 运行系统组件。执行如下命令:
kubectl get daemonset --namespace=kube-system
![k8s控制器DamenSet_公众号_02](https://s2.51cto.com/images/blog/202211/08180341_636a297d75c5250181.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
daemonSet kube-flannel-ds
和 kube-proxy
分别负责在每个节点上运行 flannel 和 kube-proxy 组件。
![k8s控制器DamenSet_微信_03](https://s2.51cto.com/images/blog/202211/08180341_636a297d8b60378926.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
因为 flannel 和 kube-proxy 属于系统组件,需要在命令行中通过 --namespace=kube-system
指定 namespace kube-system
。如果不指定则只返回默认 namespace default
中的资源。
创建DaemonSet
创建一个yaml 文件如下:
![k8s控制器DamenSet_公众号_04](https://s2.51cto.com/images/blog/202211/08180341_636a297db462973304.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
kubectl create -f xx.yaml
当把某个节点的标签(daemonset 中pod 模板定义的节点标签)删除,那么此节点上面的pod 也会消失。 当删除 DaemonSet 也会联通pod 一起删除。
下面详细分析两个 k8s 自己的 DaemonSet:kube-flannel-ds
和 kube-proxy
。
kube-flannel-ds
flannel 的 DaemonSet 就定义在 kube-flannel.yml
中:
![k8s控制器DamenSet_微信_05](https://s2.51cto.com/images/blog/202211/08180341_636a297ddee2d58779.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
DaemonSet 配置文件的语法和结构与 Deployment 几乎完全一样,只是将 kind
设为 DaemonSet
。
hostNetwork 指定 Pod 直接使用的是 Node 的网络,相当于 docker run --network=host
。考虑到 flannel 需要为集群提供网络连接,这个要求是合理的。
containers
定义了运行 flannel 服务的两个容器。
kube-proxy
由于无法拿到 kube-proxy
的 YAML 文件,只能运行如下命令查看其配置:
kubectl edit daemonset kube-proxy --namespace=kube-system
![k8s控制器DamenSet_docker_06](https://s2.51cto.com/images/blog/202211/08180342_636a297e0bb3c6239.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
![k8s控制器DamenSet_docker_07](https://s2.51cto.com/images/blog/202211/08180342_636a297e3030849565.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
同样为了便于理解,这里只保留了最重要的信息。
-
kind: DaemonSet
指定这是一个 DaemonSet 类型的资源。 -
containers
定义了 kube-proxy
的容器。 -
status
是当前 DaemonSet 的运行时状态,这个部分是 kubectl edit
特有的。
其实 Kubernetes 集群中每个当前运行的资源都可以通过 kubectl edit
查看其配置和运行状态,比如
kubectl edit deployment nginx-deployment
DaemonSet使用
下面以 Prometheus Node Exporter 为例演示如何运行自己的 DaemonSet。
Prometheus 是流行的系统监控方案,Node Exporter 是 Prometheus 的 agent,以 Daemon 的形式运行在每个被监控节点上。
如果是直接在 Docker 中运行 Node Exporter 容器,命令为:
docker run -d \
-v "/proc:/host/proc" \
-v "/sys:/host/sys" \
-v "/:/rootfs" \
--net=host \ prom/node-exporter \
--path.procfs /host/proc \
--path.sysfs /host/sys \
--collector.filesystem.ignored-mount-points "^/(sys|proc|dev|host|etc)($|/)"
将其转换为 DaemonSet 的 YAML 配置文件 node_exporter.yml:
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: node-exporter-daemonset
spec:
template:
metadata:
labels:
app: prometheus
spec:
hostNetwork: true
containers:
- name: node-exporter
image: prom/node-exporter
imagePullPolicy: IfNotPresent
command:
- /bin/node_exporter
- --path.procfs
- /host/proc
- --path.sysfs
- /host/sys
- --collector.filesystem.ignored-mount-points
- ^/(sys|proc|dev|host|etc)($|/)
volumeMounts:
- name: proc
mountPath: /host/proc
- name: sys
mountPath: /host/sys
- name: root
mountPath: /rootfs
volumes:
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
- name: root
hostPath:
path: /
![k8s控制器DamenSet_docker_08](https://s2.51cto.com/images/blog/202211/08180342_636a297e5c2b764477.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
- 直接使用 Host 的网络。
- 设置容器启动命令。
- 通过 Volume 将 Host 路径
/proc
、/sys
和 /
映射到容器中。
执行 kubectl apply -f node_exporter.yml
:
![k8s控制器DamenSet_微信_09](https://s2.51cto.com/images/blog/202211/08180342_636a297e840ea85105.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
DaemonSet node-exporter-daemonset
部署成功,k8s-node1 和 k8s-node2 上分别运行了一个 node exporter Pod。
然后我们来稍微看一下源码,k8s是通过daemon_controller里面的manage方法来管理Pod删减操作的:
manage方法里面首先会获取daemon pod 与 node 的映射关系,然后判断每一个 node 是否需要运行 daemon pod,然后遍历完node之后将需要创建的Pod列表和需要删除Pod的列表交给syncNodes执行。
func (dsc *DaemonSetsController) manage(ds *apps.DaemonSet, nodeList []*v1.Node, hash string) error {
// 获取已存在 daemon pod 与 node 的映射关系
nodeToDaemonPods, err := dsc.getNodesToDaemonPods(ds)
if err != nil {
return fmt.Errorf("couldn't get node to daemon pod mapping for daemon set %q: %v", ds.Name, err)
}
// 判断每一个 node 是否需要运行 daemon pod
var nodesNeedingDaemonPods, podsToDelete []string
for _, node := range nodeList {
nodesNeedingDaemonPodsOnNode, podsToDeleteOnNode, err := dsc.podsShouldBeOnNode(
node, nodeToDaemonPods, ds)
if err != nil {
continue
}
//将需要删除的Pod和需要在某个节点创建Pod存入列表中
nodesNeedingDaemonPods = append(nodesNeedingDaemonPods, nodesNeedingDaemonPodsOnNode...)
podsToDelete = append(podsToDelete, podsToDeleteOnNode...)
}
podsToDelete = append(podsToDelete, getUnscheduledPodsWithoutNode(nodeList, nodeToDaemonPods)...)
//为对应的 node 创建 daemon pod 以及删除多余的 pods
if err = dsc.syncNodes(ds, podsToDelete, nodesNeedingDaemonPods, hash); err != nil {
return err
}
return nil
}
下面我们看一下podsShouldBeOnNode方法是如何判断哪些Pod需要创建和删除的:
在podsShouldBeOnNode会调用nodeShouldRunDaemonPod方法来判断该node是否需要运行 daemon pod 以及能不能调度成功,然后获取该node上有没有创建该daemon pod。
通过判断shouldRun, shouldContinueRunning将需要创建 daemon pod 的 node 列表以及需要删除的 pod 列表获取到,shouldSchedule 主要检查 node 上的资源是否充足,shouldContinueRunning 默认为 true。
func (dsc *DaemonSetsController) podsShouldBeOnNode(
node *v1.Node,
nodeToDaemonPods map[string][]*v1.Pod,
ds *apps.DaemonSet,
) (nodesNeedingDaemonPods, podsToDelete []string, err error) {
//判断该 node 是否需要运行 daemon pod 以及能不能调度成功
shouldRun, shouldContinueRunning, err := dsc.nodeShouldRunDaemonPod(node, ds)
if err != nil {
return
}
//获取该节点上的指定ds的pod列表
daemonPods, exists := nodeToDaemonPods[node.Name]
switch {
//如果daemon pod是可以运行在这个node上,但是还没有创建,那么创建一个
case shouldRun && !exists:
nodesNeedingDaemonPods = append(nodesNeedingDaemonPods, node.Name)
// 需要 pod 一直运行
case shouldContinueRunning:
var daemonPodsRunning []*v1.Pod
for _, pod := range daemonPods {
if pod.DeletionTimestamp != nil {
continue
}
//如果 pod 运行状态为 failed,则删除该 pod
if pod.Status.Phase == v1.PodFailed {
...
podsToDelete = append(podsToDelete, pod.Name)
} else {
daemonPodsRunning = append(daemonPodsRunning, pod)
}
}
//如果节点上已经运行 daemon pod 数 > 1,保留运行时间最长的 pod,其余的删除
if len(daemonPodsRunning) > 1 {
sort.Sort(podByCreationTimestampAndPhase(daemonPodsRunning))
for i := 1; i < len(daemonPodsRunning); i++ {
podsToDelete = append(podsToDelete, daemonPodsRunning[i].Name)
}
}
// 如果 pod 不需要继续运行但 pod 已存在则需要删除 pod
case !shouldContinueRunning && exists:
for _, pod := range daemonPods {
if pod.DeletionTimestamp != nil {
continue
}
podsToDelete = append(podsToDelete, pod.Name)
}
}
return nodesNeedingDaemonPods, podsToDelete, nil
}
DaemonSet 对象的滚动更新和StatefulSet是一样的,可以通过 .spec.updateStrategy.type 设置更新策略。目前支持两种策略:
- OnDelete:默认策略,更新模板后,只有手动删除了旧的 Pod 后才会创建新的 Pod;
- RollingUpdate:更新 DaemonSet 模版后,自动删除旧的 Pod 并创建新的 Pod。
DaemonSet重点方法
|-> dsc.getNodesToDaemonPods() //获取当前状态
|
|
|-> manage -->|-> dsc.podsShouldBeOnNode() --> dsc.nodeShouldRunDaemonPod //汇总需要创建的pod和待删除的Pod
| |
| |
syncDaemonSet --> | |-> dsc.syncNodes //Pod数量一致性
|
|-> rollingUpdate //Pod版本一致性
|
|
|-> updateDaemonSetStatus //更新daemonset对象的status字段
Prometheus监控(1)
三丰,公众号:soft张三丰Prometheus监控(1)
Prometheus监控(2)
三丰,公众号:soft张三丰Prometheus监控(2)
Prometheus监控(3)
三丰,公众号:soft张三丰Prometheus监控(3)
Prometheus监控(4)
三丰,公众号:soft张三丰Prometheus监控(4)
Prometheus监控(5)
三丰,公众号:soft张三丰Prometheus监控(5)
关注公众号 soft张三丰
![k8s控制器DamenSet_docker_10](https://s2.51cto.com/images/blog/202211/08180342_636a297e9e04373290.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)