背景
靜兒作為美團容器化團隊HULK的一員,經常需要和Kubernetes(k8s)打交道。第一次登陸node(宿主機)的時候,發現連續登陸幾臺都看到了Prometheus-Node-Exporter字樣的docker程式。他們和普通的Pod(容器)一樣,佔用IP等資源,佔用宿主機允許的pod數上限。後來通過看書瞭解到這是DaemonSet控制管理的Pod.
DaemonSet官方文件譯文
一個DaemonSet確保了所有的node上僅有一個的Pod的一個例項。當node被新增到叢集中,Pod也被新增上去。當node被從叢集移除,這些Pod會被垃圾回收。刪除一個DaemonSet將會清理它建立的Pod。
舉一些DaemonSet典型用法的例子:
-
在每個node上執行一個叢集儲存守護程式,例如glusterd、ceph
-
在每個node上執行一個日誌集合,例如fuentd或者logstash
-
在每個node上執行一個node監控後臺執行緒,例如Prometheus Node Exporter,collectd,Dynatrace OneAgent,AppDynamics Agent,Datadog agent,New Relic agent,Ganglia gmod 或者Instana agent.
在一種簡單的場合下,一個DeamonSet會被使用在任意種後臺執行緒、覆蓋所有的node。在更復雜的安裝方式中,多個DaemonSet會被用於一種後臺執行緒。但是在不同的硬體型別會對應不同的標識或者不同的記憶體和CPU請求。
寫一個DaemonSet Spec
建立一個DaemonSet
在YAML檔案中生命一個DaemonSet。daemonset.yaml檔案描述了一個執行著fluentd-elasticsearch的docker映象的DaemonSet。
controllers/daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
spec:
path: /var/lib/docker/containers
-
建立一個基於YAML檔案的DaemonSet
kubectl create -f https://k8s.io/examples/controllers/daemonset.yaml
所需的欄位
和其他的Kubernetes配置檔案一樣,一個DaemonSet需要apiVersion,kind和metadata欄位。配置檔案的通用資訊,可以看deploying application,configuring containers和object management using kubectl文件。
一個DaemonSet也需要一個spec區
Pod模板
.spec.template是.spec的必需欄位。
.spec.template是一個pod模板。除了是巢狀的並且沒有apiVersion或者kind之外,它的schema和pod是一樣的。
除了pod必需的欄位,在DaemonSet中的pod模板必需指定合適的label(詳見pod selector)。
在DaemonSet中的pod模板必需要有一個Always的RestartPolicy。如果沒有明確指定,預設也是Aways。
Pod選擇器
.spec.selector欄位是pod的選擇器。它的功能和job的.spec.selector一樣。
在Kubernetes1.8中,必需指定一個帶有.spec.template的pod選擇器。當pod選擇器為空時將不會再是預設的選擇器。選擇器預設和kubectl apply是不相容的。一旦DaemonSet被建立,.spec.selector就不能變了。一旦改變了pod選擇器,可能會導致意外將這個pod變成「孤島」。使用者會很迷惑。
.spec.selector是有兩個欄位組成的物件:
-
matchLabels - 和ReplicationController的.spec.selector是一樣的
-
matchExpressions - 通過制定key、values列表、operatorl來定製更加精細的選擇器。
指定了兩個,它們的作用關係是and。
一旦.spec.selector被指定,就必須和.spec.template.metadata.labels匹配。不匹配的配置會被API拒掉。
同時,使用者平時也不應該建立匹配這些選擇器的標籤。包括直接建立、通過其他的DaemonSet建立,或者通過其他的像ReplicaSet這樣的控制器來建立。否則,DaemonSet控制器會認為這些pod是自己建立的。但是如果說想手動建立一個值不同的pod放在node上做測試就另當別論了。
在指定node上執行pod
指定.spec.template.spec.nodeSelector,DaemonSet控制器會在node上建立一個匹配node選擇器的pod。同時,如果指定.spec.template.spec.affinity,這時候DaemonSet控制器會建立匹配node的affinity的pod。如果什麼兩者都不指定,DaemonSet控制器將會在所有node上建立pod。
Daemon的pod是怎麼被排程的
通過DaemonSet控制器來排程(1.12版本被禁用)
pod實際執行的裝置通常是Kubernetes排程器來選擇的。但是DaemonSet控住器建立的pod是已經指定好了裝置的(Pod在建立時.spec.nodeName已經被指定了,所以會被排程器忽略)。基於這個原因:
-
node節點上的欄位unschedulable會被DaemonSet控制器忽略。
-
DaemonSet控制器在排程還沒開始時就會建立Pod來幫助啟動叢集。
被預設排程器排程(1.12版本開始預設啟動)
DaemonSet確保所有有資格的node執行一個pod的一個例項。一般來說,Kubernetes控制器決定了一個Pod選擇哪個node。但是DaemonSet控制器卻負責建立和排程DaemonSet的pod。這引入了下面的問題:
-
不一致的Pod行為:普通Pod會以Pending狀態建立出來等待排程。但是DaemonSet的Pod的初始狀態卻不是Pending。這讓使用者很疑惑。
-
預設排程器處理Pod優先權(Pod preemption)。當preemption被啟用,DaemonSet控制器在做排程決策時就不考慮pod優先權。
ScheduleDaemonSetPods允許你使用預設排程器而不是DaemonSet控制器來排程。這是通過新增NodeAffinity項而不是.spec.nodeName到DaemonSet的Pod來實現的。預設排程被應用於繫結pod到目標宿主機。DaemonSet Pod的node affinity已經存在時會被替換。DaemonSet控制器只在建立或者修改DaemonSet Pod時才會這樣。不會修改DaemonSet的spec.template。
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
operator: In
values:
汙點和容忍
Daemon Pod支援汙點和容忍。下面的容忍會根據相應的特性被自動新增到DaemonSet。
總結
初學一個技術如果感覺無法下手,學了也記不住的趕腳。不如先從一個問題出發:為什麼會有這個Pod存在?這樣先進行感知再系統學習。
相關閱讀