介紹
daemonset 是衆多控制器中的一種(其他還有deployment等等),它的作用是保證在每個節點隻部署一個Pod,而且是隻在pod 模闆中定義的标簽的節點上保證運作一個pod。
如果節點下線, DaemonSet不會在其他地方重新建立pod。但是,當将 一個新節點添加到叢集中時, DaemonSet會立刻部署一個新的pod執行個體 。
與daemonset 相比副本控制器(rc或rs)是保證叢集有固定數量的pod ,而不一定是均勻的每個節點一個這樣分布。
删除了 pod那麼它也會重新啟個新的pod 。與ReplicaSet一樣,DaemonSet 從配 pod 模闆建立pod。
主要特征
- 這個 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
daemonSet
kube-flannel-ds
和
kube-proxy
分别負責在每個節點上運作 flannel 和 kube-proxy 元件。
因為 flannel 和 kube-proxy 屬于系統元件,需要在指令行中通過
--namespace=kube-system
指定 namespace
kube-system
。如果不指定則隻傳回預設 namespace
default
中的資源。
建立DaemonSet
建立一個yaml 檔案如下:
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
中:
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
同樣為了便于了解,這裡隻保留了最重要的資訊。
-
指定這是一個 DaemonSet 類型的資源。kind: DaemonSet
-
定義了 containers
的容器。kube-proxy
-
是目前 DaemonSet 的運作時狀态,這個部分是 status
特有的。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: /
- 直接使用 Host 的網絡。
- 設定容器啟動指令。
- 通過 Volume 将 Host 路徑
、/proc
和 /sys
映射到容器中。/
執行
kubectl apply -f node_exporter.yml
:
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)