我們之前說k8s中使用deployment、statefulset工作負載資源來分別維護無狀態和有狀態應用。這篇小作文我們會學習如何使用DaemonSet來維護一個守護進程(應用)。
一、DaemonSet是什么?
DaemonSet 是一個確保全部或者某些節點上必須運行一個 Pod的工作負載資源(守護進程),當有節點加入集群時, 也會為他們新增一個 Pod。
下面是常用的使用案例:
- 集群守護進程,如
Kured、node-problem-detector - 日志收集守護進程,如
fluentd、logstash - 監控守護進程,如promethues
node-exporter
通過創建DaemonSet 可以確保 守護進程pod 被調度到每個可用節點上運行。
二、DaemonSet 如何工作?
DaemonSet 是由控制器(controller manager)管理的 Kubernetes 工作資源對象。我們通過聲明一個想要的daemonset狀態,表明每個節點上都需要有一個特定的 Pod。協調控制回路會比較期望狀態和當前觀察到的狀態。如果觀察到的節點沒有匹配的 Pod,DaemonSet controller將自動創建一個。可以參考之前《k8s工作流程詳解》
在這個過程包括現有節點和所有新創建的節點。不過DaemonSet 控制器創建的 Pod 會被Kubernetes 調度器忽略,即DaemonSet Pods 由 DaemonSet 控制器創建和調度。這樣帶來的兩個微妙的問題:
- Pod 行為的不一致性:正常 Pod 在被創建后等待調度時處於
Pending狀態, DaemonSet Pods 創建后不會處於Pending狀態下。 - Pod 搶占行為由默認調度器處理。啟用搶占后,DaemonSet 控制器將在不考慮 Pod 優先級和搶占 的情況下制定調度決策。
所以在k8s v1.12以后DaemonSet Controller 將會向 DaemonSet 的 Pod 添加 .spec.nodeAffinity 字段,而不是 .spec.nodeName 字段,並進一步由 kubernetes 調度器將 Pod 綁定到目標節點。如果 DaemonSet 的 Pod 已經存在了 nodeAffinity 字段,該字段的值將被替換。
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: metadata.name
operator: In
values:
- target-host-name
daemonset pod的默認容忍規則如下:

DaemonSet 默認在每個節點上創建一個 Pod。當然也可以使用節點選擇器來限制可接受節點的數量。DaemonSet 控制器將僅在與 YAML 文件中預定義的nodeSelector字段匹配的節點上創建Pod。我們在下面會使用到。
三、DaemonSet實例
創建DaemonSet
我們只需要將前面deployment中的kind調整為DaemonSet 就可以創建出一個DaemonSet守護進程
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: my-daemonset
spec:
selector:
matchLabels:
app: my-daemon
template:
metadata:
labels:
app: my-daemon
spec:
containers:
- name: daemonset-container
image: httpd
ports:
- containerPort : 80
通過apply應用后查看資源狀態
$ kubectl get daemonset
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
my-daemonset 1 1 1 1 1 <none> 10m
由於我們minikube只有一個node 所以只建立了一個副本,在節點通過get查看到已創建出這個daemonset pod
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
my-daemonset-97z2g 1/1 Running 0 10m
在daemonset資源狀態中可以看到NODE SELECTOR的值為none,顯然我們可以通過在pod模板中添加nodeSelector使DaemonSet 控制器僅在與Node 選擇算符匹配的節點上創建出pod,接下來我們添加一個nodeSelector
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: my-daemonset
spec:
selector:
matchLabels:
app: my-daemon
template:
metadata:
labels:
app: my-daemon
spec:
containers:
- name: daemonset-container
image: httpd
ports:
- containerPort : 80
nodeSelector:
kubernetes.io/hostname: minikube
這樣我們的pod只會在hostname為minikube的Node上創建DaemonSet守護進程的pod
$ kubectl get daemonset
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
my-daemonset 1 1 1 1 1 kubernetes.io/hostname=minikube 30m
除了通過nodeSelector來控制節點調度外,還可以通過上面提到的容忍策略即tolerations使daemonset pod 調度到“非正常“Node。
我們可以來看一個fluentd的官方elasticsearch daemonset
源文件地址:fluentd-daemonset-elasticsearch.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
labels:
k8s-app: fluentd-logging
version: v1
spec:
selector:
matchLabels:
k8s-app: fluentd-logging
version: v1
template:
metadata:
labels:
k8s-app: fluentd-logging
version: v1
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1-debian-elasticsearch
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch-logging"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
- name: FLUENT_ELASTICSEARCH_SCHEME
value: "http"
# Option to configure elasticsearch plugin with self signed certs
# ================================================================
- name: FLUENT_ELASTICSEARCH_SSL_VERIFY
value: "true"
# Option to configure elasticsearch plugin with tls
# ================================================================
- name: FLUENT_ELASTICSEARCH_SSL_VERSION
value: "TLSv1_2"
# X-Pack Authentication
# =====================
- name: FLUENT_ELASTICSEARCH_USER
value: "elastic"
- name: FLUENT_ELASTICSEARCH_PASSWORD
value: "changeme"
# Logz.io Authentication
# ======================
- name: LOGZIO_TOKEN
value: "ThisIsASuperLongToken"
- name: LOGZIO_LOGTYPE
value: "kubernetes"
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
# When actual pod logs in /var/lib/docker/containers, the following lines should be used.
# - name: dockercontainerlogdirectory
# mountPath: /var/lib/docker/containers
# readOnly: true
# When actual pod logs in /var/log/pods, the following lines should be used.
- name: dockercontainerlogdirectory
mountPath: /var/log/pods
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
# When actual pod logs in /var/lib/docker/containers, the following lines should be used.
# - name: dockercontainerlogdirectory
# hostPath:
# path: /var/lib/docker/containers
# When actual pod logs in /var/log/pods, the following lines should be used.
- name: dockercontainerlogdirectory
hostPath:
path: /var/log/pods
特別之處在於,為了收集master節點上的pod日志,將會容忍fluentd調度到master節點。其中tolerations如下

Daemon Pods 通信
與 DaemonSet 中的 Pod 進行通信的幾種模式如下:
- 推送(Push):配置 DaemonSet 中的 Pod,將更新發送到另一個服務,例如統計數據庫。
- NodeIP 和已知端口:DaemonSet 中的 Pod 可以使用
hostPort,從而可以通過節點 IP 訪問到 Pod。客戶端能通過某種方法獲取節點 IP 列表,並且基於此也可以獲取到相應的端口。比如prometheus的node-exporter。 - DNS:創建具有相同 Pod 選擇算符的 無頭服務 通過使用
endpoints資源或從 DNS 中檢索到多個 A 記錄來發現 DaemonSet。
DaemonSet 更新
如果節點的標簽被修改,DaemonSet 將立刻向新匹配上的節點添加 Pod, 同時刪除不匹配的節點上的 Pod。
可以刪除一個 DaemonSet。如果使用 kubectl 指定 --cascade=orphan 選項, 則 Pod 將被保留在節點上。接下來如果創建使用相同選擇算符的新 DaemonSet, 新的 DaemonSet 會收養已有的 Pod。 如果有 Pod 需要被替換,DaemonSet 會根據其 updateStrategy 來替換。
比如prometheus中的node-exporter

以上是關於k8s中的DaemonSet相關內容。
參考:
希望小作文對你有些許幫助,如果內容有誤請指正。
您可以隨意轉載、修改、發布本文,無需經過本人同意。blog:iqsing.github.io
