本文已超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
Kubernetes 中的進階排程
編輯註:這篇文章是關於 Kubernetes 1.6 新功能的深入文章系列的一部分
Kubernetes 排程器的預設行為在大多數情況下運作良好——例如,它確保 Pod 僅放置在具有足夠可用資源的節點上,它試圖將來自同一組(ReplicaSet、StatefulSet 等)的 Pod 分散到各個節點,它試圖平衡節點的資源利用率等等。
但有時您會想要控制 Pod 的排程方式。例如,也許您想要確保某些 Pod 僅排程在具有專用硬體的節點上,或者您想要共置頻繁通訊的服務,或者您想要將一組節點專用於一組特定的使用者。最終,您比 Kubernetes 更了解您的應用程式應該如何排程和部署。因此,Kubernetes 1.6 提供了四種進階排程功能:節點親和性/反親和性、污點與容忍度、Pod 親和性/反親和性以及自訂排程器。這些功能目前在 Kubernetes 1.6 中均為Beta版。
節點親和性/反親和性
節點親和性/反親和性是一種設定規則的方法,用於決定排程器選擇哪些節點。此功能是 nodeSelector 功能的概括化,nodeSelector 功能自 Kubernetes 1.0 版本以來就已存在。規則是使用節點上的自訂標籤和 Pod 中指定的選擇器這些熟悉的概念來定義的,並且它們可以是必要的或偏好的,具體取決於您希望排程器多嚴格地執行它們。
必須滿足必要規則,Pod 才能排程到特定節點上。如果沒有節點符合條件(加上所有其他正常條件,例如具有足夠的可用資源來滿足 Pod 的資源請求),則 Pod 將不會被排程。必要規則在 nodeAffinity 的 requiredDuringSchedulingIgnoredDuringExecution 欄位中指定。
例如,如果我們想要要求在多區域 Kubernetes 叢集的 us-central1-a GCE 區域中的節點上進行排程,我們可以將以下親和性規則指定為 Pod 規格的一部分
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "failure-domain.beta.kubernetes.io/zone"
operator: In
values: ["us-central1-a"]
“IgnoredDuringExecution”表示如果節點上的標籤發生變更且不再符合親和性規則,Pod 仍將繼續執行。未來計劃提供 requiredDuringSchedulingRequiredDuringExecution,一旦 Pod 不滿足節點親和性規則,就會將 Pod 從節點驅逐。
偏好規則表示,如果節點符合規則,則將首先選擇這些節點,並且僅當沒有可用的偏好節點時,才會選擇非偏好節點。您可以偏好而不是要求將 Pod 部署到 us-central1-a,方法是稍微更改 Pod 規格以使用 preferredDuringSchedulingIgnoredDuringExecution
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "failure-domain.beta.kubernetes.io/zone"
operator: In
values: ["us-central1-a"]
節點反親和性可以透過使用負面運算子來實現。因此,例如,如果我們希望我們的 Pod 避開 us-central1-a,我們可以這樣做
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "failure-domain.beta.kubernetes.io/zone"
operator: NotIn
values: ["us-central1-a"]
您可以使用的有效運算子包括 In、NotIn、Exists、DoesNotExist、Gt 和 Lt。
此功能的其他用例包括根據節點的硬體架構、作業系統版本或專用硬體來限制排程。節點親和性/反親和性在 Kubernetes 1.6 中是 Beta 版。
污點與容忍度
一個相關的功能是“污點與容忍度”,它允許您標記(“污點”)節點,以便任何 Pod 都無法排程到該節點上,除非 Pod 明確“容忍”該污點。標記節點而不是 Pod(如節點親和性/反親和性中那樣)對於叢集中大多數 Pod 應避免排程到節點上的情況特別有用。例如,您可能想要將您的 Master 節點標記為僅可由 Kubernetes 系統組件排程,或者將一組節點專用於一組特定的使用者,或者讓常規 Pod 遠離具有特殊硬體的節點,以便為需要特殊硬體的 Pod 留出空間。
kubectl 指令允許您在節點上設定污點,例如
kubectl taint nodes node1 key=value:NoSchedule
建立一個污點,將節點標記為任何沒有容忍度(鍵為 key,值為 value,效果為 NoSchedule 的污點)的 Pod 都不可排程。(其他污點效果包括 PreferNoSchedule,它是 NoSchedule 的偏好版本,以及 NoExecute,這表示當應用污點時,任何在節點上執行的 Pod 都將被驅逐,除非它們容忍該污點。) 您添加到 PodSpec 的容忍度,以便讓相應的 Pod 容忍此污點,看起來會像這樣
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
除了在 Kubernetes 1.6 中將污點與容忍度移至 Beta 版之外,我們還引入了一個 Alpha 功能,該功能使用污點與容忍度讓您自訂 Pod 繫結到節點的時間長度,當節點遇到網路分割等問題時,而不是使用預設的五分鐘。有關更多詳細資訊,請參閱文件中的 此章節。
Pod 親和性/反親和性
節點親和性/反親和性允許您根據節點的標籤來限制 Pod 可以運行的節點。但是,如果您想要指定關於 Pod 應如何彼此相對放置的規則,例如在服務內或相對於其他服務中的 Pod 分散或打包 Pod 呢?為此,您可以使用 Pod 親和性/反親和性,它在 Kubernetes 1.6 中也是 Beta 版。
讓我們看一個範例。假設您在服務 S1 中有前端,它們與服務 S2 中的後端頻繁通訊(“南北向”通訊模式)。因此,您希望這兩個服務共置在同一個雲端供應商區域中,但您不想手動選擇區域——如果區域發生故障,您希望 Pod 重新排程到另一個(單一)區域。您可以使用如下所示的 Pod 親和性規則來指定此項(假設您為此服務的 Pod 提供標籤“service=S2”,並為另一個服務的 Pod 提供標籤“service=S1”)
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: service
operator: In
values: [“S1”]
topologyKey: failure-domain.beta.kubernetes.io/zone
與節點親和性/反親和性一樣,也有 preferredDuringSchedulingIgnoredDuringExecution 變體。
Pod 親和性/反親和性非常靈活。想像一下,您已經分析了服務的效能,並且發現來自服務 S1 的容器在與來自服務 S2 的容器共用同一個節點時會互相干擾,可能是由於快取干擾效應或飽和網路連結。或者,也許由於安全考量,您永遠不希望 S1 和 S2 的容器共用一個節點。若要實作這些規則,只需對上面的程式碼片段進行兩處變更——將 podAffinity 變更為 podAntiAffinity,並將 topologyKey 變更為 kubernetes.io/hostname。
自訂排程器
如果 Kubernetes 排程器的各種功能無法讓您充分控制工作負載的排程,您可以將排程任意 Pod 子集的責任委派給您自己的自訂排程器,這些排程器可以與預設 Kubernetes 排程器並行或取代預設 Kubernetes 排程器運行。多個排程器在 Kubernetes 1.6 中是 Beta 版。
每個新的 Pod 通常都由預設排程器排程。但是,如果您提供您自己的自訂排程器的名稱,則預設排程器將忽略該 Pod,並允許您的排程器將 Pod 排程到節點。讓我們看一個範例。
在這裡,我們有一個 Pod,我們在其中指定了 schedulerName 欄位
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
schedulerName: my-scheduler
containers:
- name: nginx
image: nginx:1.10
如果我們在未部署自訂排程器的情況下建立此 Pod,則預設排程器將忽略它,並且它將保持在 Pending 狀態。因此,我們需要一個自訂排程器來尋找並排程 schedulerName 欄位為 my-scheduler 的 Pod。
自訂排程器可以使用任何語言編寫,並且可以根據您的需要盡可能簡單或複雜。以下是一個非常簡單的自訂排程器範例,以 Bash 編寫,可隨機分配節點。請注意,您需要將其與 kubectl proxy 一起運行才能使其正常運作。
#!/bin/bash
SERVER='localhost:8001'
while true;
do
for PODNAME in $(kubectl --server $SERVER get pods -o json | jq '.items[] | select(.spec.schedulerName == "my-scheduler") | select(.spec.nodeName == null) | .metadata.name' | tr -d '"')
;
do
NODES=($(kubectl --server $SERVER get nodes -o json | jq '.items[].metadata.name' | tr -d '"'))
NUMNODES=${#NODES[@]}
CHOSEN=${NODES[$[$RANDOM % $NUMNODES]]}
curl --header "Content-Type:application/json" --request POST --data '{"apiVersion":"v1", "kind": "Binding", "metadata": {"name": "'$PODNAME'"}, "target": {"apiVersion": "v1", "kind"
: "Node", "name": "'$CHOSEN'"}}' http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/
echo "Assigned $PODNAME to $CHOSEN"
done
sleep 1
done
了解更多資訊
Kubernetes 1.6 發行說明提供了關於這些功能的更多資訊,包括如果您已在使用這些功能中的一項或多項的 Alpha 版本,如何變更您的設定的詳細資訊(這是必需的,因為從 Alpha 版移至 Beta 版對於這些功能來說是一項破壞性變更)。
誌謝
此處描述的功能,無論是 Alpha 版還是 Beta 版形式,都是社群共同努力的成果,其中涉及來自 Google、華為、IBM、Red Hat 等公司的工程師。
參與社群
在我們的每週 社群會議 上分享您的聲音
- 在 Stack Overflow 上發布問題(或回答問題)
- 在 Twitter 上追蹤我們 @Kubernetesio 以獲取最新更新
- 在 Slack 上與社群聯繫(#sig-scheduling 頻道)
非常感謝您的貢獻。