轉載請注明出處,文章轉自FingerLiu
0x0 pre
彈性伸縮這種功能,不是很多系統都已經實現了,我們直接用就行了嗎,為什么還需要個指南呢。
因為。。。。我們先來看看都有哪些相關知識點吧。。。

彈性伸縮涉及到各種軟硬件,各色調度平台,策略和系統,其本身就是一個較復雜的課題。此外,kubernetes 不單單是一個容器調度平台,而是一個活躍,龐大的生態系統。
kubernetes 彈性伸縮這個課題涉及了諸多知識點,主要如下:
- 水平(Horizontal)伸縮
- 垂直(Vertical)伸縮
- 定時(Scheduled)伸縮
- 預測(Predictive)性伸縮
- 服務畫像
- node
- service
- CA
- cloudprovider
- VPA
- HPA
- HPA controller
- metric
- metric server
- heapster
- metric state
- prometheus
- CRD
- custom metric api
- prometheus adapter
- cronhpa controller
- cloud controller manager
在剛開始調研這個課題的時候,一上來看到這么多名詞和術語,肯定會一臉懵逼的。終於,
在知識的海洋中遨游了好一陣后,終於了摸索出一條路,爬上岸來。
本文旨在為想在 kubernetes 中使用彈性伸縮功能
的讀者解釋相關概念,並制定一條較為清晰的路線圖。

0x1 autoscaling
在畫路線圖之前,先來了解下彈性伸縮的基本概念。
目的
我們做工程都是結果導向的,就是說,我們做彈性伸縮,不是因為它看上去很酷,不是為了做彈性伸縮而做。
而是從公司的實際問題出發,為了解決什么問題,以及投入產出比是否合理,才決定是否要做彈性伸縮,以及怎么做。
一般來講,彈性伸縮主要用來解決兩個問題:
- 應對突發流量
- 節省資源
應對突發流量。這點很好理解,當系統由於外界因素(比如 XXX 突然結婚了, XXX 突然離婚了等突發的熱門話題),
導致系統負載激增,原有的配置已無法滿足需求,需要增加系統配置。這種增加可能是自動的,可能是需要手工調整的,
甚至是需要去購買機器,重啟服務等對服務比較不友好的行為。具體進行何種操作,需要以及目前公司的彈性伸縮級別來制定
相應的計划。關於彈性伸縮的級別,我們將在稍后說明。
節省資源。絕大多數情況下,我們不可能讓我們的工作負載跑滿服務器(即便能跑滿,為了服務穩定性,我們也不大敢這么做)。
在不損失服務穩定性的大前提下,盡可能地提高資源利用率,一直是各個彈性伸縮方案不懈的目標。減少單個服務資源占用,
通過合理調度資源減少服務器數量,及時增減服務數量等手段,都能直接或間接的達到節省資源的目的。
針對以上兩種目的,我們可能設計出完全不同的彈性伸縮方案。
例如,如果我們的需求是 在流量激增時能夠在數分鍾內為指定服務擴容,保證服務穩定可用,那么我們可能僅僅需要
把服務遷移到容器環境中,並配置一個延遲較低的監控報警系統,在出問題時能夠及時通知到運維人員,然后手動為服務擴容即可;
如果服務器成本在公司所有成本中已經占了很大的比重,要在不影響業務的情況下盡可能地減少服務器開支,秒級伸縮,則我們需要
從公司后端服務各個方面好好設計一番了。
節點和服務
節點是承載工作負載的單元,是集群中提供容器運行環境的一台機器(物理機或虛擬機)。
服務是具體的工作負載,具體在 kubernetes 中,就是 pod 以及 pod 所包含的容器。
kubernetes 上的彈性伸縮會在節點和服務兩個粒度進行。
兩個粒度之間會相互影響。例如,應對突發流量時,如果出發了服務粒度的擴容操作,就會占用部分節點資源,
如果碰巧擴容到一半時,集群所有資源都被用完了,那么此時只有節點也能進行自動擴容才能完成服務的擴容,
否則會導致服務由於沒有找到足夠的資源而擴容失敗。當然,現實情況中,我們一般不會等到集群資源完全用完
時才去出發節點擴容。一般會在資源超過某個特定閾值(90-95%)時,就會出發擴容。
同樣,在使用縮容來節省資源時,只有將負載低的服務縮容后,才能空閑出一批利用率較低的節點,此時節點彈性伸縮器檢測到
某些節點利用率低,關閉這些節點,或是從雲廠商取消續訂這些節點,才能達到節省資源的最終目的。
垂直伸縮與水平伸縮
垂直(Vertical)伸縮:調整節點或服務的資源配額。
水平(Horizontal)伸縮: 調整節點或服務的數量。
彈性伸縮的級別
借鑒業界對自動駕駛的分級規則,現定義服務的彈性伸縮級別如下:
| 級別 | 說明 | 擴展 | 收縮 | 業務是否感知 | 操作時間 |
|---|---|---|---|---|---|
| 0 | 系統不支持伸縮 | - | - | - | - |
| 1 | 運維手動添加機器 | 人工 | 人工 | 是 | x 小時 |
| 2 | 管理系統中修改配置 | 人工 | 人工 | 否 | x 分鍾 |
| 3 | 自動擴容 | 自動 | 人工 | 否 | 擴展 x 秒,收縮 x 分鍾 |
| 4 | 自動伸縮 | 自動 | 自動 | 否 | x 秒 |
| 5 | 按計划伸縮 | 自動 | 自動 | 否 | x 秒 |
| 6 | 基於預測的伸縮 | 自動 | 自動 | 否 | x 秒 |
level 0
系統還處在比較原始的階段,服務不支持任何形式的擴容縮容。
level 1
系統直接跑在自己維護的虛擬機或物理機上,能夠通過人工增加減少機器數量來實現擴縮容。
此種方式的通常方案是在服務上層搭建一個負載均衡,然后在增加、減少機器時使負載均衡感知。
這種方式擴縮容的時間通常較長,幾分鍾到幾小時不等。
level 2
- 服務已經容器化
- 有一套標准的容器調度管理平台(mesos/kubernetes)
- 有一套完整的監控數據(prometheus/zabbix/open-falcon/ELK)
由於容器的啟動速度比虛擬機和物理機要快很多,所以容器化后,可以將擴容速度提高很多倍,能夠達到秒級至分鍾級。
level 3
由於 level 2 中我們已經有個各個維度的監控數據,在此基礎之上,可以做一些自動化策略來減少人工操作的時間,
能夠將擴容速度穩定在秒級。
level 4
在彈性伸縮的級別中,自動縮容的等級(4)要比自動擴容(3)高一級,主要從兩個方面考慮:
- 從公司發展角度來看,肯定是先跑起來 --> 抗住大流量 --> 精細化控制成本。
如果溫飽問題都還沒有解決, 公司生死存亡的問題都還沒解決, 就去考慮節約成本,顯然是沒有抓對發展的大方向。 - 從實現的技術難度來講,縮容要考慮的細節要多一些。比如擴容時由於都是添加資源和服務,較容易做到業務無感知。
但是在縮容時,如果想做到業務無感知,可能需要結合網關、負載均衡、部署策略、服務優雅關閉、服務發現、
容器生性周期管理等多方面的知識才能實現。
此外,在制定彈性伸縮策略時,業界通常會采用 激進的擴容,謹慎的縮容 的大方針來最大限度地保障服務的穩定性。
level 5
level 3 和 level 4 雖說已經實現了秒級的彈性伸縮,但是他們都是被動式的,換句話說,只能在事情發生后,
再進行擴縮容。這樣做可能會導致出現少量的請求超時或請求出錯。一種更優雅的方案是在出現問題前,預先准備好充足的資源,
避免問題的發生,前置性地進行伸縮。
level 5 周期性的彈性伸縮是指基於對歷史數據和公司、服務業務的資源用量波峰波谷的周期性規律的總結,定時對服務和集群
規模進行擴縮容。
例如如果公司主要業務是一款公交出行類軟件,那么很顯然每天的早晚上班期間是公司業務的高峰,且流量是閑時流量的數倍。該公司
可以指定一個定時伸縮策略,每天早晨 5 點將集群規模擴大 x 倍,xx 業務規模擴大 y 倍,11 點再縮容至原規模,下午 4 點再進行擴容,
以此類推。level 5 的彈性伸縮的實現,依賴於 level 3 和 level 4 對基礎設施和中間件的優化與改造,只有在能保證擴縮容的過程中
不會對業務產生影響,才可以頻繁地進行擴縮容。
level 6
level 5 的周期性伸縮用一種簡單地預測性的方式很好的解決了公司常規業務的資源使用率的問題。但由於僅僅采集了時間維度對系統資源使用
情況影響的數據,其調整方式較為單一,無法應對突發情況。
通常情況下,在系統出現高流量,高負載前,都會有一些前置性信號。這些信號包括但不限於以下方面:
- 基於以往壓測數據和以往運營活動數據對即將開展的營銷活動的流量的預測
- 利用社會工程學手段對社會上即將發生的熱點事件和正在發生的熱點事件的走向的預測
- 利用機器學習對系統響應時間、流量走勢、系統負載走勢等多維度海量監控數據的分析對即將到來的高流量和高負載的預測(例如 netflx 的預測分析引擎Scryer)
技術手段能夠解決許多問題,但是並不能解決所有問題。在解決系統性問題時,尤其是較復雜的系統問題時,我們不能把思維完全限制在技術領域,
同時結合多種手段,可以使許多看似無法用技術手段解決的問題迎刃而解。
當然,隨着機器學習技術的興起,一些以往無法用傳統方式解決的技術問題,也變得可解。只不過需要專業人士投入較多的人力物力,在使用
這種方式時,要在充分考慮投入產出比后,再做決定。
Netflix發現,對於部分基礎架構和特定的工作負載,其預測分析引擎 Scryer 比傳統的被動自動縮放方法提供了更好的結果。它特別適合以下場景:
- 在不久的將來識別需求的巨大峰值,並提前一點准備好產能
- 處理大規模中斷,例如整個可用區和區域的故障
- 處理可變的流量模式,根據一天中不同時間的典型需求水平和變化率,在橫向擴展或縱向擴展速率上提供更大的靈活性
任何系統和工程都不是一蹴而就的,只有當基礎設施達到一定完善程度后,再逐級實現彈性伸縮,才是一個比較可行的路線。
本文假定目前系統已經達到了2 級的彈性伸縮。以下主要講述在此基礎上,實現更高級的彈性伸縮的相關知識點。
0x2 autoscaling in kubernetes
接下來我們開始逐步詳細講解在文章開頭繪制的路線圖。
service autoscaling
首先,按照伸縮粒度,分為服務伸縮和節點伸縮。我們先來看服務伸縮。
k8s 默認提供了多個服務粒度的彈性伸縮組件。主要有 VPA, addon resizer 和 HPA。
此外,各大雲廠商也積極貢獻提供了多種伸縮組件,例如阿里雲提供的 cronHPA。
以下我們分別詳細說明。
在服務粒度的伸縮中,依據執行觸發時機不同,可分為立即執行,定時執行和預測性執行。
先來看立即執行。
立即執行又細分為垂直伸縮和水平伸縮。
垂直伸縮
k8s 中的垂直伸縮一般是指調整 Pod 的內存和 CPU 配額(resource limit 和 request)。
k8s 官方 autoscaler 包中有兩個類型的垂直伸縮組件:
VPA(vertical pod autoscaler) 和 addon resizer。
addon resizer可以根據集群節點數量來動態地調整某個其他 deployment 中的 pod 的配額。
addon resizer 周期性地查看集群節點數量,然后計算出監控的 pod 需要分配的內存和 CPU,如果 pod 的實際 pod 配額
和所需配額超過某個閾值,則會修改 deployment 並觸發生成新的 pod。addon resizer 的這種特性決定了它用來伸縮與集群
規模密切相關的服務。一般, addon resizer 用來伸縮部署在集群內的 heapster, metrics-server, kube-state-metrics
等監控組件。
VPA的應用范圍要廣一些。設置 VPA 后,它能夠自動為 pod 設定 request 和 limit 配額值,然后動態地將 pod 調度
到合適的節點上去。
VPA 在 k8s 中定義類型為 VerticalPodAutoscaler 的 CRD,
為每個需要開啟垂直彈性伸縮功能的 deployment 創建一個 custom resource,然后 VPA 會定期查看對應 pod 的資源使用情況,結合歷史數據,
自動調整 pod 的配額。
VPA controller 由三部分組成。
- Recommender:它監視當前和過去的資源消耗,並基於此提供容器的cpu和內存請求的推薦值。
- Updater:它檢查哪個托管的pod設置了正確的資源,如果沒有,則殺死它們,以便它們的控制器可以用更新后的請求重新創建它們。
- Admission Plugin:它在新pod上設置正確的資源請求(由於Updater的活動,它們的控制器只是創建或重新創建了這些請求)。
使用 VPA 監控 deployment 時,它並不會去改變 deployment 的配置,而是使用 admission plugin 以類似 pre hook 的方式
在創建 pod 時動態為其配置配額值。
使用 VPA 之前需要注意以下問題:
- VPA 默認會從 metrics server 中采集歷史數據,因此使用 VPA 之前,需要配置好 metrics server。VPA 也支持從 prometheus 中
采集歷史數據,不過需要額外的配置。關於 metrics server、state metrics、prometheus 等監控服務的異同,我們將在 數據監控一節中
詳細介紹 - VPA 是在伸縮調整過程中,是通過重啟 pod 來使調整生效的,因此已經公司基礎架構不同,可能會引起服務閃斷,需要具體分析服務是否可接受
- 使用 VPA 擴容有上限,具體受 pod 所在宿主機影響。為 pod 分配的資源無法超過宿主機的大小。如果 recommender 計算出的 pod 所需資源
超過節點可用資源,將導致 pod 一直 pending。這點可與通過與 cluster autoscaler 共同使用來部分解決。 - VPA 目前不應與基於內存和 CPU 監控的水平Pod自動調度器(HPA)一起使用,否則可能產生預期外的行為。
水平伸縮
Horizontal Pod Autoscaler 是 k8s 內置的水平伸縮控制器。 它根據觀察到的CPU使用率(或使用自定義指標支持,基於某些其他應用程序提供的指標)
自動縮放 replication 控制器,deployment,副本集或狀態集中的 pod 數量。 需要注意的是,水平窗格自動縮放不適用於無法縮放的對象,
例如DaemonSets。
HPA 實現為Kubernetes API資源和控制器。資源決定控制器的行為。控制器定期(默認為 15 秒)調整復制控制器或部署中的副本數量,
以使所觀察到的平均CPU利用率與用戶指定的目標相匹配。
與 VPA 僅支持從 metrics server 中采集 CPU 和內存數據不同的是,HPA 支持多種數據維度和數據采集方式:
- 從 heapster 中采集 CPU 和內存數據(自 kubernetes 1.11 起廢除)
- 遵循特定格式記錄在 annotation 中的應用自定義 metrics(自 kubernetes 1.6 起廢除)
- 使用 metrics API 采集 metric server 中的數據
- 通過 custom metrics adapter 將 prometheus 等第三方中的數據采集提供給 custom metrics API 使用。
和 VPA 一樣,使用 HPA 一般需要先搭建 metrics server,具體方法可參考 kubernetes 官方指南。
在新版本(kubernetes 1.6 以后)的 metrics API 中,引入聚合層(aggregation layer),為應用查詢 metrics server
內部的數據和第三方數據提供了一致的抽象。第三方監控數據只需自己實現相應的 adapter,並在 metrcis API 中為其
監控的 metric 注冊相應的 custom metrics API 即可。
下圖展示了使用 HPA 根據 metrics server 和 prometheus 中的數據進行彈性伸縮的過程。
)
定時伸縮
kubernetes 官方並沒有提供定時伸縮相關的組件,但是其原理並不難,只需按照設定的時間調用 kubernetes 的 API 即可。
kubernetes-cronhpa-controller 是阿里雲工程師基於 go-cron
開源的定時伸縮組件。
預測性伸縮
目前暫無成熟的技術方案。
node autoscaling
垂直伸縮
與 kubernetes 本身關系不大,其功能主要取決於雲廠商。例如,廠商是否支持主機的升降配,以及升降配過程中是否
需要重啟主機等。如果需要重啟主機,那么在進行伸縮之前,我們需要先把節點上的 pod 驅逐到其他主機。
但是如果我們是由於機器資源不夠用而擴容的話,這樣會加劇資源不夠用的情況,造成更大的 pending 狀態的 pod。
因此,如果如果雲廠商不支持不重啟就能擴容的話,我們不建議采用這種方式進行節點擴容。
水平伸縮
CA(Cluster Autoscaler) 是 kubernetes 官方的節點水平伸縮組件。
它可以在下列條件之一為真時自動調整Kubernetes集群的大小:
- 集群中有 pod 由於資源不足而一直 pending
- 集群中有些節點在很長一段時間內沒有得到充分利用,且其上的 pod 可以被調度到其他節點上。
cluster autoscaler 雖然是 kubernetes 官方標准,但是由於其去雲廠商依賴較深,因此具體使用方法,功能以及限制以
雲廠商具體實現為准。目前支持以下雲廠商: Google Cloud Platform, AWS, AliCloud, Azure, BaiduCloud。
以 AliCloud 為例,默認單個用戶按量付費實例的配額是30台,單個VPC的路由表限額是50條;且每個可用區的同一類型的實例
庫存容量波動很大,如果短時間內大量購買同一區同一配置的實例,很容易出現庫存不足導致擴容失敗。
建議在伸縮組中配置多種同規格的實例類型,提高節點伸縮成功率。
實際使用中,一般為 node 建立多個 node group,專門配置幾個 group 來啟用彈性伸縮應對突發流量進行擴縮容。
主要的 group 並不進行擴縮容,來避免擴縮容導致對大范圍的服務的影響。
此外,節點水平伸縮能否成功實施,與調度策略密切相關。kubernetes 在為 pod 選擇可分配節點時,
是采用 LeastRequestedPriority 策略,簡單來說就是就是盡可能把資源打散,把 pod 分配到資源利用率低的節點。
這樣會倒是有一批利用率較低,但未到縮容閾值的節點,因此會導致無法成功縮容,資源利用率低。因此實際使用時,需要
調整 kubernetes 調度策略,來達到最優的結果。
定時伸縮
目前暫無成熟的技術方案。
數據監控
heapster
heapster 是一個數據收集器,從每個 node 上采集數據並進行匯總,然后轉存到第三方工具中,
它本身並不生成監控數據,也不負責存儲。
目前 heapster 已經被逐漸廢棄(從 kubernetes 1.11 開始),不再維護和推薦使用。
heapster 曾負責以下功能:
- 監控節點、pod 的 cpu 內存基礎數據。這部分數據采自每個節點的 cadvisor(新版本的 kubernetes 已集成到 kubelet)。
- 通用的數據項目監控。
- kubernetes 事件信號轉發。 heapster 調用 api-server 並將集群的 event 轉發處理。
目前這三個功能可依次由 metrics server、pormetheus operator、eventrouter 等獨立的組件替換。
metrics server
metrics server 是 heapster 的繼承者,kubernetes 1.8 開始,集群內監控數據可以通過 metrics API
從監控數據聚合器 metrics server 中獲取。
Metrics API相比於之前的監控采集方式(hepaster)是一種新的思路,官方希望核心指標的監控應該是穩定的,版本可控的,
可以直接被用戶訪問(kubectl top),也可以由組件通過 API 調用。由於監控數據量巨大,不適宜像其他資源一樣存到 etcd 中,
因此 Metrics server 將數據存在內存中,只提供實時數據的查詢,不提供歷史數據查詢功能。
除了可以通過 metrics API 查詢內部監控數據外,metrics server 提供了靈感的擴展方式。
系統可以為自定義監控數據實現對應的 adapter, 用戶就可以以 custom metrics API 的方式查詢數據,保持與查詢原生數據一致的體驗。
通常, metrics server 的數據可作為各個維度彈性伸縮的數據指標。
state metrics
kube-state-metrics 偵聽 Kubernetes API服務器並生成關於對象狀態的指標。它並不關注Kubernetes組件的健康狀況,而是關注內部各種對象
(如 deployment、node 和 pod)的健康狀況。
我們可以直接訪問 Kube-state-metrics 的 /metrics HTTP 接口來查看監控數據。 這些數據用來提供給 prometheus 獲取其它一些第三方程序
抓取使用。
關於 state metrics 和 metrics server 的差異:
metrics server 僅采集 kubernetes 中的 CPU、內存等核心指標,它周期性地調用所有節點的 kubelet
提供的 summary API。這些數據是按 pod、node 維度聚合的,存儲在內存中,並且以 metrics API
的格式提供。它僅存儲最新數據,並且不負責將這些數據導出提供給第三方使用。
kube state metrics 主要關注於從 deployment, replica set, stateful set 等 kubernetes
對象生成新的監控數據,將原始數據重新聚合、呈現。它在內存中存儲整個 kubernetes 集群的狀態快照,並在此之上持續生成新的監控數據。同樣,
它也負責將這些數據導出提供給第三方使用。
prometheus
用於存儲、聚合、查詢監控數據的時序型數據庫。
0x3 其他
最后分享一張我們分析彈性伸縮時用到的思維導圖:

好了,彈性伸縮相關路徑我們都已經基本熟悉了,我們又可以踏上征程,
去探索維斯特洛大陸(kubernetes)其他的版塊了。
