云原生之K8S系列:Kubernetes Daemonset 全面指南
DaemonSet是什么?
Kubernetes是一個分布式系統,Kubernetes平臺管理員應該有一些功能可以在所有節點上運行特定于平臺的應用程序。例如,在所有Kubernetes節點上運行日志代理。
這就是Daemonset發揮作用的地方。
Daemonset是一個原生的Kubernetes對象。顧名思義,它旨在運行系統守護進程。
DaemonSet對象旨在確保每個工作節點上都運行一個pod。這意味著您不能在節點中擴展daemonset pods。由于某種原因,如果從節點刪除daemonset pod,則daemonset控制器將再次創建它。
讓我們看一個例子。如果有500個工作節點,并且您部署了一個daemonset,則默認情況下daemonset控制器將為每個工作節點運行一個pod。總共是500個Pod。但是,使用nodeSelector、nodeAffinity、Taints和Tolerations,可以限制daemonset在特定節點上運行。
例如,在有100個工作節點的集群中,一個可能有20個標記為GPU的工作節點來運行批處理工作負載。你應該在這20個工作節點上運行pod。在這種情況下,可以使用節點選擇器將pod部署為守護進程。我們將在本指南的后面討論它。
另一個例子是,您有特定數量的工作節點專用于平臺工具(入口、監控、日志等),并且希望僅在標記為平臺工具的節點上運行與平臺工具相關的Daemonset。在這種情況下,您可以使用nodeSelector僅在平臺工具專用的工作節點上運行daemonset pods。
Kubernetes后臺進程的用例
DaemonSet的基本用例是在集群本身中。如果你看一下Kubernetes架構,kube-proxy組件會運行一個daemonset。
下面是Daemonset的實際用例。
- 集群日志收集:在每個節點上運行日志采集器,以集中Kubernetes日志數據。例如:fluentd, logstash, fluentbit
- 集群監控:在集群中的每個節點上部署監控代理,例如Prometheus節點導出器,以收集和公開節點級度量。通過這種方式,prometheus可以獲取所有工作節點的監控指標。
- 安全性和合規性:使用kube-bench等工具在每個節點上運行CIS基準測試。還要在需要額外安全措施的特定節點上部署安全代理,如入侵檢測系統或漏洞掃描器。例如,處理PCI和pii兼容數據的節點。
- 存儲配置:在每個節點上運行存儲插件,為整個集群提供共享存儲系統。
- 網絡管理:在每個節點上運行網絡插件或防火墻,以確保網絡策略的一致執行。例如,Calico CNI插件在所有節點上以Daemonset的形式運行。
根據需求,我們可以為一種守護進程部署多個DaemonSet,對各種硬件類型使用各種標志或內存和CPU請求。
DaemonSet例子
像其他Kubernetes對象一樣,DaemonSet也通過使用YAML文件來配置。我們需要創建一個清單文件,其中將包含守護進程所需的所有配置信息。
假設我們想在集群的所有工作節點上部署一個fluentd日志代理作為Deamonset。
下面是daemonset的示例。部署在logging命名空間中的Yaml文件。
您還可以從Github Repo的Kubernetes課程中獲得daemonset YAML示例。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: logging
labels:
app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd
template:
metadata:
labels:
name: fluentd
spec:
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
讓我們來了解一下清單文件。
- apiVersion:apps/v1用于DaemonSet
- kind:后臺進程,如Pod、部署和服務
- metadata:放置DaemonSet的名稱、提及命名空間、注釋和標簽。在我們的例子中,DaemonSet的名稱是fluentd。
- spec.selector: pods的選擇器由DaemonSet管理。這個值必須是在pod模板中指定的標簽。這個值是不可變的。
- spec.template:這是一個必填字段,指定守護進程要使用的pod模板。以及容器的所有必填字段。除了apiVersion和kind之外,它具有pod schema的所有內容。
template.metadata包含pod和模板的詳細信息。Spec將具有pod的模式。
在pod模板中,我們使用quay.io/fluentd_elasticsearch/fluentd:v2.5.2鏡像,它將在Kubernetes集群的每個節點上運行。每個pod將收集日志并將數據發送到ElasticSearch。增加了對pod的資源限制和請求,以及相應的volume和volumeMount。
我們不提供任何副本計數,這是因為DaemonSet的副本計數本質上是動態的,因為它依賴于集群的節點計數。
讓我們使用以下命令來部署此清單文件。首先,我們必須創建一個命名空間,并在該命名空間中部署daemonset。
kubectl create ns logging
kubectl apply -f daemonset.yaml
檢查DaemonSet狀態和pods狀態。
kubectl get daemonset -n logging
kubectl get pods -n logging -o wide
你可以看到fluentd pods運行在兩個可用的工作節點上。
下面是一些其他有用的命令來描述、編輯和獲取DaemonSet。
kubectl describe daemonset -n logging
kubectl edit daemonset -n logging
kubectl get ds
應用污點和對Daemonset的耐受
Taints和tolerance是Kubernetes的功能,它允許你確保pods不會被放置在不合適的節點上。我們污染節點并在pod模式中添加公差。
kubectl taint 節點 node1 key1=value1:<效果>
有3種效果:
- NoSchedule:Kubernetes調度器只允許調度對受污染節點具有容錯能力的pods。
- PreferNoSchedule:Kubernetes調度器將嘗試避免調度對受污染節點不具有容錯能力的pods。
- NoExecute:如果pods對受污染的節點不具有容錯能力,Kubernetes將從節點中移除正在運行的pods。
下面,我用關鍵應用程序和價值監控污染了其中一個節點,效果是NoExecute。我們不希望DaemonSet在這個特定節點上運行pod。
kubectl taint node k8s-worker-2 app=fluentd-logging:NoExecute
現在在daemonset.yaml中添加類似這樣的容錯功能.
spec:
tolerations:
- key: app
value: fluentd-logging
operator: Equal
effect: NoExecute
containers:
-----
-----
當您更新DaemonSet時,您將看到一個在節點k8s-worker-2上運行的pod被刪除。DaemonSet現在不會在這個節點上調度任何pod。
為Daemonset Pods使用Nodeselector
我們可以使用nodeSelector在一些特定節點上運行pods。DaemonSet控制器將在與節點選擇器的鍵和值匹配的節點上創建Pods
首先,您需要為節點添加一個標簽。
kubectl label node <node-name> key=value
例如,假設您想將一個節點標記為type=platform-tools,可以使用以下命令。
kubectl label node k8s-worker-1 type=platform-tools
現在,要將nodeSelector應用到Daemonset,請在spec部分下使用鍵和值添加nodeSelector,如下所示。
spec:
nodeSelector:
<key>: <value>
下圖顯示了Daemonset YAML,其中nodeSelector spec高亮顯示為黃色。
Daemonset節點親和性
我們還可以使用節點親和性實現對節點如何選擇的更細粒度的控制。DaemonSet控制器將在與節點親和性相匹配的節點上創建Pods。
Node affinity在概念上類似于nodeSelector,允許你根據節點標簽約束pod可以調度哪些節點。節點關聯有兩種類型:
- requiredDuringSchedulingIgnoredDuringExecution:除非滿足規則,否則調度器無法調度Pod。它的功能類似于nodeSelector,但語法更有表現力。
- preferredDuringSchedulingIgnoredDuringExecution:調度器試圖找到滿足規則的節點。如果沒有匹配的節點可用,調度器仍然調度Pod。
我們可以像這樣給清單文件添加一個關聯:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: key-name
operator: In
values:
- value-name
pod只允許運行在具有matchFields節中提到的鍵和值的節點上。
下面的Daemonset YAML使用了以粗體突出顯示的兩個關聯規則。節點標簽所需的規則和選擇實例標簽實例類型t2.large的節點的首選規則。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: logging
labels:
app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd
template:
metadata:
labels:
name: fluentd
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: type
operator: In
values:
- platform-tools
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: instance-type
operator: In
values:
- t2.large
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
Daemonset特權訪問
在某些情況下,您需要從Deamonset pod獲得訪問主機的特權。例如,calico CNI daemoset需要對其網絡需求進行主機級訪問,因為它需要修改IPtables。
另一個例子是Kube-proxy的daemonset。它還需要特權訪問。
你可以使用Pod規范中的securityContext來允許或拒絕特權訪問。安全上下文定義Pod或容器的權限和訪問控制設置。要為pod指定安全設置,需要在pod清單文件中包含securityContext字段。
spec:
securityContext:
runAsNonRoot: true
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
securityContext:
allowPrivilegeEscalation: false
-------
第一個是由對象定義的pod級安全上下文,第二個是由單個容器定義的SecurityContext。
- allowPrivilegeEscalation:控制進程是否可以獲得比其父進程更多的權限。
- privileged:以特權模式運行容器。特權容器中的進程本質上等同于主機上的root。
- runAsNonRoot:表示容器必須以非root用戶運行。
- runAsUser:運行容器進程入口點的UID。
- runAsGroup:運行容器進程入口點的GID。
滾動更新、回滾和刪除Daemonset
讓我們看一下更新、刪除和回滾守護進程部署的概念。
(1) 滾動更新
DaemonSet有兩種更新策略類型:
- OnDelete:使用OnDelete策略,只有當我們手動刪除任何pod時,才會創建DaemonSet pod。
- RollingUpdate:這是默認的更新策略。使用RollingUpdate策略,每當更新DaemonSet模板時,舊的pod將被終止,新的pod將被自動創建。最多只有一個DaemonSet的pod在運行。
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
(2) 回滾
我們可以使用以下命令回滾DaemonSet:
kubectl rollout undo daemonset <daemonset-name>
檢查DaemonSet的所有修訂版本:
kubectl rollout history daemonset <daemonset-name>
如果想回滾到特定的版本,可以使用:
kubectl rollout undo daemonset <daemonset-name> --to-revision=<revision>
刪除:
kubectl delete daemonset <daemonset-name>
如果你想讓pod在節點上運行,請使用--cascade=false。
DaemonSet Pod Priority
Kubernetes Pod優先級決定了一個Pod相對于另一個Pod的重要性。
我們可以為DaemonSet設置更高的pod PriorityClass,以防將關鍵系統組件作為一個DaemonSet運行。這確保了守護進程的pod不會被低優先級或不那么關鍵的pod搶占。
PriorityClass用于定義pod的優先級。PriorityClass對象可以是任何小于或等于10億的32位整數值。值越高,優先級越高。創建一個優先級類,并將其用于DaemonSet pod spec
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 100000
globalDefault: false
description: "daemonset priority class"
運行此命令檢查:
kubectl get priorityClass
我們需要在daemonset.yaml中添加priorityClass:
spec:
priorityClassName: high-priority
containers:
------
------
terminationGracePeriodSeconds: 30
volumes:
------
如果您查看Kube-Proxy & Cluser CNI (Calico)守護進程集,它的priority類設置為system-node-critical,它具有最高的優先級。它是Kubernetes中內置的priority類,應用于pod,在任何情況下都不應該被刪除。