作者:shengdong
阿裡雲K8S叢集的一個重要特性,是叢集的節點可以動态的增加或減少。有了這個特性,叢集才能在計算資源不足的情況下擴容新的節點,同時也可以在資源使用率降低的時候,釋放節點以節省費用。
這篇文章,我們讨論阿裡雲K8S叢集擴容與縮容的實作原理。了解實作原理,在遇到問題的時候,我們就可以高效地排查并定位原因。我們的讨論基于目前的1.12.6版本。
節點增加原理
阿裡雲K8S叢集可以給叢集增加節點的方式有,添加已有節點,叢集擴容,和自動伸縮。其中,添加已有節點又可分為手動添加已有節點和自動添加已有節點。節點的增加涉及到的元件有,節點準備,彈性伸縮(ESS),管控,Cluster Autoscaler以及排程器。

手動添加已有節點
節點準備,其實就是把一個普通的ECS執行個體,安裝配置成為一個K8S叢集節點的過程。這個過程僅靠一條指令就可以完成。這條指令使用curl下載下傳attach_node.sh腳本,然後以openapi token為參數,在ECS上運作。
curl
http:///public/pkg/run/attach//attach_node.sh| bash -s -- --openapi-token
這裡token是一個對的key,而value是目前叢集的基本資訊。阿裡雲K8S叢集的管控,在接到手動添加已有節點請求的時候,會生成這個對,并把key作為token傳回給使用者。
這個token(key)存在的價值,是其可以讓attach_node.sh腳本,以匿名身份在ECS上索引到叢集的基本資訊(value),而這些基本資訊,對節點準備至關重要。
總體上來說,節點準備就做兩件事情,讀和寫。讀即資料收集,寫即節點配置。
這裡的讀寫過程,絕大部分都很基礎,大家可以通過閱讀腳本來了解細節。唯一需要特别說明的是,kubeadm join把節點注冊到Master的過程。此過程需要新加節點和叢集Master之間建立互信。
一邊,新加節點從管控處擷取的bootstrap token(與openapi token不同,此token是value的一部分内容),實際上是管控通過可信的途徑從叢集Master上擷取的。新加節點使用這個bootstrap token連接配接Master,Master則可通過驗證這個bootstrap token來建立對新加節點的信任。
另一邊,新加節點以匿名身份從Master kube-public命名空間中擷取叢集cluster-info,cluster-info包括叢集CA憑證,和使用叢集bootstrap token對這個CA做的簽名。新加節點使用從管控處擷取的bootstrap token,對CA生成b新的簽名,然後将此簽名與cluster-info内簽名做對比,如果兩個簽名一緻,則說明cluster-info和bootstrap token來自同一叢集。新加節點因為信任管控,是以建立對Master的信任。
自動添加已有節點
自動添加已有節點,不需要人為拷貝黏貼腳本到ECS指令行來完成節點準備的過程。管控使用了ECS userdata的特性,把類似以上節點準備的腳本,寫入ECS userdata,然後重新開機ECS并更換系統盤。當ECS重新開機之後,會自動執行Userdata裡邊的腳本,來完成節點添加的過程。這部分内容,大家其實可以通過檢視節點userdata來确認。
!/bin/bash
mkdir -p /var/log/acs
http:///public/pkg/run/attach/1.12.6-aliyun.1/attach_node.sh| bash -s -- --docker-version --token --endpoint --cluster-dns > /var/log/acs/init.log
這裡我們看到,attach_node.sh的參數,與前一節的參數有很大的不同。其實這裡的參數,都是前一節value的内容,即管控建立并維護的叢集基本資訊。自動添加已有節點省略了通過key擷取value的過程。
叢集擴容
叢集擴容與以上添加已有節點不同,此功能針對需要新購節點的情形。叢集擴容的實作,在添加已有節點的基礎上,引入了彈性伸縮ESS元件。ESS元件負責從無到有的過程,而剩下的過程與添加已有節點類似,即依靠ECS userdata腳本來完成節點準備。下圖是管控通過ESS從無到有建立ECS的過程。
自動伸縮
前邊三種方式是需要人為幹預的伸縮方式,而自動伸縮的本質不同,是它可以在業務需求量增加的時候,自動建立ECS執行個體并加入叢集。為了實作自動化,這裡引入了另外一個元件Cluster Autoscaler。叢集自動伸縮包括兩個獨立的過程。
其中第一個過程,主要用來配置節點的規格屬性,包括設定節點的使用者資料。這個使用者資料和手動添加已有節點的腳本類似,不同的地方在于,其針對自動伸縮這種場景,增加了一些專門的标記。attach_node.sh腳本會根據這些标記,來設定節點的屬性。
!/bin/sh
| bash -s -- --openapi-token --ess true --labels k8s.io/cluster-autoscaler=true,workload_type=cpu,k8s.aliyun.com=true
而第二個過程,是實作自動增加節點的關鍵。這裡引入了一個新的元件Autoscaler,它以Pod的形式運作在K8S叢集中。理論上來說,我們可以把這個元件當做一個控制器。因為它的作用與控制器類似,基本上還是監聽Pod狀态,以便在Pod因為節點資源不足而不能被排程的時,去修改ESS的伸縮規則來增加新的節點。
這裡有一個知識點,叢集排程器衡量資源是否充足的标準,是“預訂率”,而不是“使用率”。這兩者的差别,類似酒店房價預訂率和實際入住率:完全有可能有人預訂了酒店,但是并沒有實際入住。在開啟自動伸縮功能的時候,我們需要設定縮容門檻值,就是“預訂率”的下線。之是以不需要設定擴容門檻值。是因為Autoscaler擴容叢集,依靠的是Pod的排程狀态:當Pod因為節點資源“預訂率”太高無法被排程的時候,Autoscaler就會擴容叢集。
節點減少原理
與增加節點不同,叢集減少節點的操作隻有一個移除節點的入口。但對于用不同方法加入的節點,其各自移除方式略有不同。
首先,通過添加已有節點加入的節點,需要三步去移除:管控通過ECS API清楚ECS userdata;管控通過K8S API從叢集中删除節點;管控通過ECS InvokeCommand在ECS上執行kubeadm reset指令清理節點。
其次,通過叢集擴容加入的節點,則在上邊的基礎上,增加了斷開ESS和ECS關系的操作。此操作由管控調用ESS API完成。
最後,經過Cluster Autoscaler動态增加的節點,則在叢集CPU資源“預訂率”降低的時候,由Cluster Autoscaler自動移除釋放。其觸發點是CPU“預訂率”,即上圖寫Metrics的原因。
總結
總體上來說,K8S叢集節點的增加與減少,主要涉及四個元件,分别是Cluster Autoscaler,ESS,管控以及節點本身(準備或清理)。根據場景不同,我們需要排查不同的元件。其中Cluster Autoscaler是一個普通的Pod,其日志的擷取和其他Pod無異;ESS彈性伸縮有其專門的控制台,我們可以在控制台排查其伸縮配置、伸縮規則等相關子執行個體日志和狀态;而管控的日志,可以通過檢視日志功能來檢視;最後,對于節點的準備與清理,其實就是排查對應的腳本的執行過程。
以上講道理居多,希望對大家排查問題有所幫助。