本文分享自華爲雲社區《kube-apiserver限流機制原理》,作者:可以交個朋友。
背景
apiserver是kubernetes中最重要的組件,一旦遇到惡意刷接口或請求量超過承載範圍,apiserver服務可能會崩潰,導致整個kubernetes集羣不可用。所以我們需要對apiserver做限流處理來提升kubernetes的健壯性。
k8s-apiserver限流能力發展過程
apiserver限流能力的發展分爲兩個階段:
kubernetes 1.18版本之前kube-apiserver只是將請求分成了變更類型(create、update、delete、patch)和非變更類型(get、list、watch),並通過啓動參數設置了兩種類型的最大併發數。
--max-requests-inflight ## 限制同時運行的非變更類型請求的個數上限,0表示無限制。 --max-mutating-requests-inflight ## 限制同時運行的變更類型請求的個數上限。0 表示無限制。
此時的apiserver限流能力較弱,若某個客戶端錯誤的向kube-apiserver發起大量的請求時,必然會阻塞kube-apiserver,影響其他客戶端的請求,因此高階的限流APF就誕生了。
kubernetes1.18版本之後APF( APIPriorityAndFairness )成爲kubernetes的默認限流方式。 APF以更細粒度的方式對請求進行分類和隔離,根據優先級和公平性進行處理。
--enable-priority-and-fairness ## 該值作爲APF特性開關,默認爲true --max-requests-inflight、--max-mutating-requests-inflight ## 當開啓APF時,倆值相加確定kube-apiserver的總併發上限
兩個階段限流能力對比
限流能力 | 1.18版本前 | 1.18版本後(APF) |
---|---|---|
顆粒度 | 僅根據是否變更做分類 | 可以根據請求對象、請求者身份、命名空間等做分類 |
隔離性 | 一個壞用戶可能堵塞整個系統 | 爲請求分配固定隊列,壞請求只能撐爆其使用的隊列 |
公平性 | 會出現餓死 | 用公平性算法從隊列中取出請求 |
優先級 | 無 | 有特權級別,可讓重要請求不被限制 |
APF關鍵資源介紹
APF通過FlowSchema 和 PriorityLevelConfiguration兩個資源配置限流策略。
FlowSchema:解決老版本分類顆粒度粗的問題。根據rules字段匹配請求,匹配規則包含:請求對象、執行操作、請求者身份和命名空間
apiVersion: flowcontrol.apiserver.k8s.io/v1beta2 kind: FlowSchema # 一個kubernetes集羣中可以定義多個FlowSchema metadata: name: myfl spec: distinguisherMethod: # 可選值爲:ByNamespace或ByUser,用於把請求分組。屬於同組的請求會分配到固定的queue中,如果省略該參數,則該FlowSchema匹配的所有請求都將視爲同一個分組。 type: ByUser matchingPrecedence: 90 # 數字越小代表FlowSchema的匹配順序越在前,取值範圍:1~10000。 priorityLevelConfiguration: # FlowSchema關聯的priorityLevelConfiguration name: mypl rules: - nonResourceRules: # 匹配非資源型:匹配接口URL - nonResourceURLs: - '*' resourceRules: # 匹配資源型:匹配apigroup、namespace、resources、verbs - apiGroups: - '*' namespaces: - '*' resources: - '*' verbs: - get - create - list - update subjects: # 匹配請求者主體:可選Group、User、ServiceAccount - group: name: '*' kind: Group - kind: User user: name: '*' - kind: ServiceAccount serviceAccount: name: myserviceaccount namespace: demo
PriorityLevelConfiguration:解決老版本隔離性差的問題和優先級問題,並定義了限流細節(總隊列數、隊列長度、是否可排隊)。當請求與某個FlowSchema匹配後,該請求會關聯FlowSchema中指定的PriorityLevelConfiguration資源,每個PriorityLevelConfiguration相互隔離,且能承受的併發請求數也不一樣
apiVersion: flowcontrol.apiserver.k8s.io/v1beta2 kind: PriorityLevelConfiguration ## 每個PriorityLevelConfiguration有自己獨立的限流配置, PriorityLevelConfiguration之間是完全隔離的。 metadata: name: mypl spec: type: Limited # 設置是否爲特權級別,如果爲Exempt則不進行限流,如果爲Limited則進行限流 limited: assuredConcurrencyShares: 2 # 值越大,PriorityLevelConfiguration的併發上限越高。若當前併發執行數未達到併發上限,則PL處於空閒狀態。 limitResponse: # 定義如何處理當前無法被處理的請求 type: Queue # 類型,Queue或者Reject,Reject直接返回429並拒絕,Queue將請求加入隊列 queuing: handSize: 1 # 根據ByNamespace或ByUser對請求分組,每個分組對應queues的數量, queueLengthLimit: 20 # 此PriorityLevelConfiguration中每個隊列的長度 queues: 2 # 此PriorityLevelConfiguration中的隊列數
一個FlowSchema只能關聯一個priorityLevelConfiguration,多個FlowSchema可以關聯同一個priorityLevelConfiguration
PriorityLevelConfiguration併發上限 = assuredConcurrencyShares / 所有assuredConcurrencyShares之和 * apiserver總併發數
APF處理過程
請求與集羣中的FlowSchema列表按照順序依次匹配,每個FlowSchema的matchingPrecedence字段決定其在列表中的順序,matchingPrecedence字段值越小,越靠前,越先進行匹配請求。
根據FlowSchema資源中的rules規則進行匹配,匹配方式可以是 “請求的資源類型”、“請求的動作類型”、“請求者的身份”、“請求的命名空間” 等多個維度。
若請求與某個FlowSchema成功匹配,匹配就會結束。FlowSchema關聯着一個PriorityLevelConfiguration,每個PriorityLevelConfiguration中包含許多queue,根據FlowSchema.spec.Distinguisher字段將請求進行"分組",根據分組來分配queue,分配queue數量由PriorityLevelConfiguration資源的handSize字段決定,如果省略該參數,則該FlowSchema匹配的所有請求都將視爲同一個"分組"。
每個PriorityLevelConfiguration資源都有獨立的併發上限,assuredConcurrencyShares字段爲apiserver總併發數的權重佔比,值越大分配的併發上限就越高,當PriorityLevelConfiguration達到併發上限後,請求會根據所屬的"分組"寫入固定的queue中,請求被阻塞等待。請求與queue的固定關聯可以讓惡意用戶隻影響其使用的queue,而不會影響同PriorityLevelConfiguration中的其他queue。
當PriorityLevelConfiguration未達到併發上限時,fair queuing算法從所有queue中選擇一個合適的queue取出請求,解除請求的阻塞,執行這個請求。fair queuing算法能保證同一個 PriorityLevelConfiguration 中的所有queue被處理機會平等。
APF實戰
kubernetes原生自帶了一些FlowSchema和PriorityLevelConfiguration規則,我們選擇一個查看,如下圖:
下面我們創建新的APF規則:當請求對象是apf命名空間中的deployment,則進行"apfpl"限流規則。
apiVersion: flowcontrol.apiserver.k8s.io/v1beta2 kind: FlowSchema metadata: name: apffl spec: matchingPrecedence: 150 priorityLevelConfiguration: name: apfpl ## 關聯名爲apfpl的PriorityLevelConfiguration rules: - resourceRules: - apiGroups: - apps clusterScope: true namespaces: - apf ## 匹配apf命名空間 resources: - deployments ## 匹配操作deployment的請求 verbs: - '*' ## 匹配任意操作類型 subjects: - kind: Group group: name: '*' ## 匹配任意組身份 --- apiVersion: flowcontrol.apiserver.k8s.io/v1beta2 kind: PriorityLevelConfiguration metadata: name: apfpl spec: limited: assuredConcurrencyShares: 2 limitResponse: ## 設置限流處理細節 queuing: handSize: 1 queueLengthLimit: 20 queues: 2 type: Queue type: Limited ## 對請求做限流處理
接着在apf命名空間和default命名空間分別創建deployment進行測試。apf_fs爲請求被分類到的 FlowSchema 的名稱,apf_pl爲該請求的優先級名稱。查看apiserver日誌信息,見下圖:
循環操作deployment,我們可以使用命令查看是否觸發限流等待
kubectl get --raw /debug/api_priority_and_fairness/dump_priority_levels
返回waitingRequests非0,則代表觸發最大併發數,有請求被限流進入等待隊列。PriorityLevelConfiguration資源不爲空閒表示已達到併發上限