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