【Kubernetes】K8s筆記(九):DaemonSet 守護進程集
0. Deployment 有哪些不足
Deployment 能夠創建任意多個的 Pod 實例,并且維護這些 Pod 的正常運行,保證應用始終處于可用狀態。Deployment 并不關心 Pod 在哪些節點上運行,只要 Pod 的數量足夠,應用程序應該會正常工作。
但是有些業務是和運行環境相關的,它們并非完全獨立于系統運行,而是與主機存在綁定關系,必須依附于節點才能產生價值:
-
網絡應用如 kube-proxy,節點不運行這個 Pod 就無法加入 Kubernetes 網絡
-
監控應用如 Prometheus,節點需要運行這個 Pod 報告節點運行狀態
-
日志應用如 Fluentd,節點需要運行這個 Pod 來收集日志
-
安全應用,每個節點都要有一個 Pod 來執行安全審計、入侵檢查、漏洞掃描等工作
這些業務如果用 Deployment 來部署就不太合適了,因為 Deployment 所管理的 Pod 數量是固定的,而且可能會在集群里“漂移”,但,實際的需求卻是要在集群里的每個節點上都運行 Pod,也就是說 Pod 的數量與節點數量保持同步。
所以,Kubernetes 就定義了新的 API 對象 DaemonSet,它在形式上和 Deployment 類似,都是管理控制 Pod,但管理調度策略卻不同。
DaemonSet 確保全部(或者某些)節點上運行一個 Pod 的副本。 當有節點加入集群時, 也會為他們新增一個 Pod 。 當有節點從集群移除時,這些 Pod 也會被回收。刪除 DaemonSet 將會刪除它創建的所有 Pod。
1. 使用 YAML 描述 DaemonSet 對象
使用命令 kubectl api-resources
可以知道它的簡稱是 ds
。Kubernetes 不提供自動創建 DaemonSet YAML 樣板的功能,我們只能去 DaemonSet 的文檔頁面獲取一個 DaemonSet 的 YAML 模板。我們把它拷貝下來,再去掉多余的部分,就可以做成自己的一份樣板文件:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: redis-ds
labels:
app: redis-ds
spec:
selector:
matchLabels:
name: redis-ds
template:
metadata:
labels:
name: redis-ds
spec:
containers:
- image: redis:5-alpine
name: redis
ports:
- containerPort: 6379
這個 DaemonSet 對象的名字是 redis-ds
,鏡像是 redis:5-alpine
。
在 spec
部分,DaemonSet 也有 selector
字段,匹配 template
里 Pod 的 labels
標簽,這和 Deployment 對象幾乎一模一樣。
DaemonSet 在 spec
里沒有 replicas
字段,意味著它不會在集群里創建多個 Pod 副本,而是要在每個節點上只創建出一個 Pod 實例。也就是說,DaemonSet 僅僅是在 Pod 的部署調度策略上和 Deployment 不同,其他的都是相同的,某種程度上我們也可以把 DaemonSet 看做是 Deployment 的一個特例。
所以我們只需要用 kubectl create
先創建出一個 Deployment 對象,然后把 kind
改成 DaemonSet
,再刪除 spec.replicas
就行了。
2. 在 Kubernetes 里使用 DaemonSet
現在,讓我們執行命令 kubectl apply
,把 YAML 發送給 Kubernetes,讓它創建 DaemonSet 對象,再用 kubectl get
查看對象的狀態:
$ kubectl apply -f redis-ds.yaml
daemonset.apps/redis-ds created
l$ kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
redis-ds 1 1 0 1 0 <none> 12s
$ kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
redis-ds 1 1 1 1 1 <none> 15s
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-ds-ktmrm 1/1 Running 0 22s 10.10.1.26 worker1 <none> <none>
雖然我們沒有指定 DaemonSet 里 Pod 要運行的數量,但它自己就會去查找集群里的節點,在節點里創建 Pod。因為我們的實驗環境里有一個 Master 一個 Worker,而 Master 默認是不跑應用的,所以 DaemonSet 就只生成了一個 Pod,運行在了“worker”節點上。
按照 DaemonSet 的本意,應該在每個節點上都運行一個 Pod 實例才對,但 Master 節點卻被排除在外了,這就不符合我們當初的設想了。
為了應對 Pod 在某些節點的“調度”和“驅逐”問題,它定義了兩個新的概念:污點(taint) 和 容忍度(toleration)。
3. Taint 和 Toleration
“污點”是 Kubernetes 節點的一個屬性,它的作用也是給節點“貼標簽”,但為了不和已有的 labels
字段混淆,就改成了 taint
。
和“污點”相對的,就是 Pod 的“容忍度”,顧名思義,就是 Pod 能否“容忍”污點。
Kubernetes 在創建集群的時候會自動給節點 Node 加上一些“污點”,方便 Pod 的調度和部署。你可以用 kubectl describe node
來查看 Master 和 Worker 的狀態:
$ kubectl describe node k8s-master
Name: k8s-master
Roles: control-plane
···
Taints: node-role.kubernetes.io/control-plane:NoSchedule
···
$ kubectl describe node worker1
Name: worker1
Roles: <none>
···
Taints: <none>
···
可以看到,Master 節點默認有一個 taint
,名字是 node-role.kubernetes.io/master
,它的效果是 NoSchedule
,也就是說這個污點會拒絕 Pod 調度到本節點上運行,而 Worker 節點的 taint
字段則是空的。這正是 Master 和 Worker 在 Pod 調度策略上的區別所在,通常來說 Pod 都不能容忍任何“污點”,所以加上了 taint
屬性的 Master 節點也就會無緣 Pod 了。
兩種方法讓 DaemonSet 在 Master 節點(或者任意其他節點)上運行:
-
去掉 Master 節點上的
taint
,DaemonSet 自然就不需要再區分 Master/Worker$ kubectl taint node master node-role.kubernetes.io/master:NoSchedule-
這種方法修改的是 Node 的狀態,影響面會比較大,可能會導致很多 Pod 都跑到這個節點上運行,所以我們可以保留 Node 的“污點”,為需要的 Pod 添加“容忍度”,只讓某些 Pod 運行在個別節點上,實現“精細化”調度
-
為 Pod 添加字段 tolerations,讓它能夠“容忍”某些“污點”,就可以在任意的節點上運行了
tolerations
是一個數組,里面可以列出多個被“容忍”的“污點”,需要寫清楚“污點”的名字、效果。比較特別是要用operator
字段指定如何匹配“污點”,一般我們都使用Exists
,也就是說存在這個名字和效果的“污點”。
如果我們想讓 DaemonSet 里的 Pod 能夠在 Master 節點上運行,就要寫出這樣的一個tolerations
,容忍節點的node-role.kubernetes.io/master:NoSchedule
這個污點:tolerations: - key: node-role.kubernetes.io/control-plane effect: NoSchedule operator: Exists
重新部署加上了“容忍度”的 DaemonSet:
$ kubectl apply -f ds.yml $ kubectl get ds -o wide NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR redis-ds 2 2 2 2 2 <none> 53m redis redis:5-alpine name=redis-ds
需要特別說明的是:“容忍度”并不是 DaemonSet 獨有的概念,而是從屬于 Pod,所以理解了“污點”和“容忍度”之后,你可以在 Job/CronJob、Deployment 里為它們管理的 Pod 也加上 tolerations,從而能夠更靈活地調度應用。
污點和容忍度的文檔在這里:https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/taint-and-toleration/ ,有哪些污點、污點有哪些效果可以在這里找到。
4. 靜態 Pod
DaemonSet 是在 Kubernetes 里運行節點專屬 Pod 最常用的方式,但它不是唯一的方式,Kubernetes 還支持另外一種叫“靜態 Pod”的應用部署手段。
“靜態 Pod”非常特殊,它不受 Kubernetes 系統的管控,不與 apiserver、scheduler 發生關系,所以是“靜態”的。但既然它是 Pod,也必然會“跑”在容器運行時上,也會有 YAML 文件來描述它,而唯一能夠管理它的 Kubernetes 組件也就只有在每個節點上運行的 kubelet 了。
“靜態 Pod”的 YAML 文件默認都存放在節點的 /etc/kubernetes/manifests
目錄下,它是 Kubernetes 的專用目錄。
Kubernetes 的 4 個核心組件 apiserver、etcd、scheduler、controller-manager 原來都以靜態 Pod 的形式存在的,這也是它們能夠先于 Kubernetes 集群啟動的原因。
附:DaemonSet YAML
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: redis-ds
labels:
app: redis-ds
spec:
selector:
matchLabels:
name: redis-ds
template:
metadata:
labels:
name: redis-ds
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
operator: Exists
containers:
- image: redis:5-alpine
name: redis
ports:
- containerPort: 6379