###### tags: `CKA` [讀書會入口](/ma8jxJEdRiSwwwboYtHuAg) # Scheduler(上) [TOC] ## Scheduling 簡介 Scheduling: 能把 pods 被配到某 Nodes 上,讓 kubelet 可以執行它 ### K8s Scheduler - K8s 裡面 default scheduler 就是 kube-scheduler - 是control-plane 的其中一個元件 - 也可以自定義 Scheduler <!-- 流程: 1. pod definition 初始化時,NodeName 預設為空 2. Scheduler 利用一系列function 計算 scoring 幫你找到最適合配置 pod 的 node 3. 告訴 api server 要把這個 pod bind 到哪個 node 上面 --> ![](https://i.imgur.com/qUrgz99.jpg) <!-- kube-scheduler 備份機制:Raft protocal --> ### Manual Scheduling 使用 binding + api requests 自行模擬 scheduler 的工作,配置 pod 要去的 node ![](https://i.imgur.com/7M41kh3.png) 如果 pod 無法調度,pod status 就會是變 pending ![](https://i.imgur.com/kizjXvY.png) ![](https://i.imgur.com/01ld9Hu.png) ## Label & Selectors #### Label - 綁定到 k8s 資源的 key/value pair - 可以加到各種Node, Pod, Service等資源上 - 方便 user 更好識別 回憶上集提過的 labels ``` "release" : "stable", "release" : "canary" "environment" : "dev", "environment" : "qa", "environment" : "production" "tier" : "frontend", "tier" : "backend", "tier" : "cache" "partition" : "customerA", "partition" : "customerB" "track" : "daily", "track" : "weekly" ``` 推薦使用的 labels <table> <thead> <tr> <th>Key</th> <th>Description</th> <th>Example</th> <th>Type</th> </tr> </thead> <tbody><tr> <td><a class="vglnk" href="http://app.kubernetes.io/name" rel="nofollow"><span>app</span><span>.</span><span>kubernetes</span><span>.</span><span>io</span><span>/</span><span>name</span></a></td> <td>Application 名稱</td> <td>mysql</td> <td>string</td> </tr> <tr> <td><a class="vglnk" href="http://app.kubernetes.io/instance" rel="nofollow"><span>app</span><span>.</span><span>kubernetes</span><span>.</span><span>io</span><span>/</span><span>instance</span></a></td> <td>Application Instance 的識別用名稱</td> <td>wordpress-abcxzy</td> <td>string</td> </tr> <tr> <td><a class="vglnk" href="http://app.kubernetes.io/version" rel="nofollow"><span>app</span><span>.</span><span>kubernetes</span><span>.</span><span>io</span><span>/</span><span>version</span></a></td> <td>Application目前的版本號</td> <td>5.7.21</td> <td>string</td> </tr> <tr> <td><a class="vglnk" href="http://app.kubernetes.io/component" rel="nofollow"><span>app</span><span>.</span><span>kubernetes</span><span>.</span><span>io</span><span>/</span><span>component</span></a></td> <td>在 Application 架構中的元件</td> <td>database</td> <td>string</td> </tr> <tr> <td><a class="vglnk" href="http://app.kubernetes.io/part-of" rel="nofollow"><span>app</span><span>.</span><span>kubernetes</span><span>.</span><span>io</span><span>/</span><span>part</span><span>-</span><span>of</span></a></td> <td>更上層的 Application 名稱</td> <td>wordpress</td> <td>string</td> </tr> <tr> <td><a class="vglnk" href="http://app.kubernetes.io/managed-by" rel="nofollow"><span>app</span><span>.</span><span>kubernetes</span><span>.</span><span>io</span><span>/</span><span>managed</span><span>-</span><span>by</span></a></td> <td>用來管理 Application 佈署 &amp; 操作的工具</td> <td>helm</td> <td>string</td> </tr> </tbody></table> ```yaml= apiVersion: apps/v1 kind: StatefulSet metadata: labels: app.kubernetes.io/name: mysql app.kubernetes.io/instance: mysql-abcxzy app.kubernetes.io/version: "5.7.21" app.kubernetes.io/component: database app.kubernetes.io/part-of: wordpress app.kubernetes.io/managed-by: helm ``` - 其他範例: 把這個 pod 加上 environment=production, app=nginx 的label ```yaml= apiVersion: v1 kind: Pod metadata: name: label-demo labels: environment: production app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80 ``` #### Selectors - 透過 selector, user 可以找出對應的set的物件 - K8s 主要的core grouping primitive - API支援兩種形式: - equality-based (=,==,!=) - set-based (in,notin and exists) ```bash # 逗號代表是 AND(&&)的意思 # equality-based kubectl get pods -l environment=production,tier=frontend # set-based kubectl get pods -l 'environment in (production),tier in (frontend)' # 組合使用 kubectl get pods -l 'partition in (customerA, customerB),environment!=qa' ``` **Field Selectors** 找 k8s 對應資源狀態的清單,例如 ``` metadata.name=my-service metadata.namespace!=default status.phase=Pending ``` ```bash= #取所有狀態是 Running 的 pod kubectl get pods --field-selector status.phase=Running #chained selectors kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always # 一次取多個resource type kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default ``` Q. 試想如果流量只要導到環境為python的http server? ![](https://i.imgur.com/Pf5nl0S.png) ```yaml apiVersion: v1 kind: Service metadata: name: service-example namespace: default spec: selector: svc: http-server env: python ports: - name: https port: 443 targetPort: 443 protocol: TCP ``` ## Assign Pods 到 Nodes 的方法 ### 1.Node Selectors 最簡單的選 node 設定條件方法, 會依據設定的 labels 配置 pod ```yaml= #選擇帶有 accelerator=nvidia-tesla-p100 標籤的node apiVersion: v1 kind: Pod metadata: name: cuda-test spec: containers: - name: cuda-test image: "registry.k8s.io/cuda-vector-add:v0.1" resources: limits: nvidia.com/gpu: 1 nodeSelector: accelerator: nvidia-tesla-p100 ``` ### 2.Taints(污點) & Tolerations(容忍) ![](https://i.imgur.com/zSO8bQ4.png) 可以Taint Node,確保不會有不適合的pod 被配置到指定node 設定 Pod Tolerations, 讓 pod 也可以被配置到有 taint 的node 兩個搭配確保 pod 不會配到不適合的 node 上 **對node設定** - 給key/value pair,並指定 effect - 可以對一個node有多個taints - effect: `NoSchedule`: 不 match label 就不會配到 node 上 `PreferNoSchedule`: 比較 soft 的 NoSchedule, K8s 會避免把不相容的 pod 放到 node 上 `NoExecute`: 強制踢出,一旦 taint 生效,如果 Node 裡面正在運行的 pod 沒有對應 Tolerate 設定,就會被逐出 ```bash # set taint to node # 除非 pod 有相符的label, 否則不會assign 到node1 kubectl taint nodes node1 disk=ssd:NoSchedule # delete taint on node kubectl taint nodes node1 disk- ``` **對 pod 的設定** ```yaml= tolerations: - key: "key1" operator: "Equal" value: "value1" effect: "NoSchedule" ``` 使用時機: 1. 專屬某特定使用的 Node `kubectl taint nodes nodename dedicated=groupName:NoSchedule` 2. 有特殊硬體的 Node `kubectl taint nodes nodename special=true:NoSchedule` or `kubectl taint nodes nodename special=true:PreferNoSchedule` 3. Taint based Evictions 當 node 有問題時,可以針對 pod-level 設定 eviction behavior 像是 node controller 就有 build-in 的 taint, 例如: - node.kubernetes.io/not-ready: Node is not ready. This corresponds to the NodeCondition Ready being "False". - node.kubernetes.io/unreachable: Node is unreachable from the node controller. This corresponds to the NodeCondition Ready being "Unknown". - node.kubernetes.io/memory-pressure: Node has memory pressure. ```yaml= # tolerationSeconds define 她還可以待在這個不適合的pod多久 tolerations: - key: "key1" operator: "Equal" value: "value1" effect: "NoExecute" tolerationSeconds: 3600 ``` ### 3.Affinity(親和力) k8s最早只有Node Selector,Affinity是後面的版本才加進來的,延伸了可以定義的條件。 好處: - affinity/anti-affinity 更好表現選擇邏輯 - 指定 rule 是 soft/preferred, 條件不 match 還是可以配置 pod - 利用運行在 node 上 pod 存在的 label 限制要配置的 pod,可以讓你定義哪些 pod 可以一起配在同個 node 用法: `requiredDuringSchedulingIgnoredDuringExecution`: 除非條件符合否則不會 Schedule pod,跟 `nodeSelector`類似。 `preferredDuringSchedulingIgnoredDuringExecution`: 試著找到對應的 node ,但如果條件不 match,還是會 schedule. ```yaml= apiVersion: v1 kind: Pod metadata: name: nginx spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disktype operator: In values: - ssd containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent ``` ### Taints & Tolerations vs Affinity | Taint & Tolerations| Affinity (親和力) | | -------- | -------- | | 確保 node 資源被指定的 pod 使用 | 確保 pod 在配置時能被配到希望使用的 node | ## k8s-scheduler 機制 拿 create pod 流程舉例: 1. api server 先將create pod 資料寫入 etcd 2. Scheduler 知道有沒 assign Node 的 pod 3.算出結果後回 NodeName 給 api server 4. 結果 bind 回 pod definition / etcd 紀錄 5. kubelet 接手起pod ![](https://i.imgur.com/lNae96O.png) (https://harshanarayana.dev/2020/06/writing-a-custom-kubernets-scheduler/) 那 Scheduler 到底怎麼選出要配在哪個 pod 的? ![](https://i.imgur.com/j2Azzlh.png) 如果 pod 中有指定 NodeName 則不用 Scheduler 參與,直接被調度到指定節點 ![](https://i.imgur.com/d3cc9uO.png) ### kube-scheduler 利用兩步驟選擇適合的 node 利用 Scheduling Policies , Scheduling Profiles 實現 1. Filtering: predicates 2. Scoring: priority 後來Scheduling 又有了 Scheduling framework,是個 pluggable architecture for k8s scheduler. 分為 Scheduling Cycle (依序執行), Binding Cycle (可同時執行). ![](https://i.imgur.com/WEbPzl2.png) ### QueueSort 用來排序在 Scheduling queue 裡面的 pod, 一次只能 enable 一個 ### Filter - nodeSelector - nodeAffinity - affinitfy, anti-affinity Scheduler 中,可選的Predicates ![](https://i.imgur.com/Rbw2OiA.png) ### Score Scheduler 中,可選的Priotity ![](https://i.imgur.com/EwwMwPS.png) ![](https://i.imgur.com/ZCFkFLq.png) --- **其他擴展點:** `QueueSort`: 用在調度序列排序, 一次只有一個調度器Plugin可以運作 `PreFilter`: 在Pod調度前先對cluster或pod進行的檢查, 一個調度週期只會做一次, 如果檢查失敗會中斷調度 `Filter`: 過濾不能運行Pod的節點 `PostFilter`: 節點預選後調用的, 可以用來記錄log `PreScore`: 預先針對調度節點打分數, 如果檢查失敗會中斷調度 `Score`: 根據配置的權重針對調度節點打分數 `NormalizeScore`: 在結算節點排名之前修改節點分數, 如果檢查失敗會中斷調度 `Reserve`: 為防止資源競爭需要先幫選定的Pod預留節點上的資源 `Permit`: 針對Pod綁定前檢查或是延遲Pod綁定 `approve`: 所有Permit Plugin檢查成功後就將Pod發送出去綁定 `deny`: 只要有Permit Plugin檢查失敗, 就將Pod退回到調度序列, 並調用Unreserve `wait`: 如果有Permit Plugin返回等待中, Pod的週期就會一直停在等待的狀態, 如果在超時前都無法完成則會進行deny的流程 `PreBind`: 在Pod綁定到節點之前執行 `Bind`: 在所有PreBind都執行完後才執行 `PostBind`: Pod成功綁定後才執行, 是綁定(Binding Cycle)的最後一個擴展點 `Unreserve`: Pod保留後被拒絕才會執行 ## Reference - Udemy - K8s官方文件 - https://ithelp.ithome.com.tw/articles/10221929 - https://kubernetes.io/docs/concepts/scheduling-eviction/scheduling-framework/ - confluence - 啓維的DE talk ### 討論項目: Watch 通过 HTTP 协议与 Kubernetes API Server 建立长连接,接收 Kubernetes API Server 发来的资源变更事件。Watch 操作的实现机制使用 HTTP 协议的分块传输编码——当 client-go 调用 Kubernetes API Server 时,Kubernetes API Server 在 Response 的 HTTP Header 中设置 Transfer-Encoding 的值为 chunked,表示采用分块传输编码,客户端收到消息后,与服务端进行连接,并等待下一个数据块。 - https://cloudnative.to/blog/client-go-informer-source-code/#%E6%80%BB%E7%BB%93 - https://github.com/k8s-club/k8s-club/blob/main/articles/Informer%E6%9C%BA%E5%88%B6%20-%20%E6%A6%82%E8%BF%B0.md