目錄
引言
業務容器化後,如何將其部署在 K8S 上?如果僅僅是將它跑起來,很簡單,但如果是上生產,我們有許多地方是需要結合業務場景和部署環境進行方案選型和配置調優的。比如,
- 如何設置容器的 Request 與 Limit、
- 如何讓部署的服務做到高可用、
- 如何配置健康檢查、
- 如何進行彈性伸縮、
- 如何更好的進行資源調度、
- 如何選擇持久化存儲、
- 如何對外暴露服務等。
對於這一系列高頻問題,這裏將會出一個 Kubernetes 服務部署最佳實踐的系列的文章來爲大家一一作答,本文將先圍繞如何合理利用資源的主題來進行探討。
Request 與 Limit 怎麼設置纔好
如何爲容器配置 Request 與 Limit? 這是一個即常見又棘手的問題,這個根據服務類型,需求與場景的不同而不同,沒有固定的答案,這裏結合生產經驗總結了一些最佳實踐,可以作爲參考。
所有容器都應該設置 request
request 的值並不是指給容器實際分配的資源大小,它僅僅是給調度器看的,調度器會 "觀察" 每個節點可以用於分配的資源有多少,也知道每個節點已經被分配了多少資源。被分配資源的大小就是節點上所有 Pod 中定義的容器 request 之和,它可以計算出節點剩餘多少資源可以被分配(可分配資源減去已分配的 request 之和)。如果發現節點剩餘可分配資源大小比當前要被調度的 Pod 的 reuqest 還小,那麼就不會考慮調度到這個節點,反之,纔可能調度。所以,如果不配置 request,那麼調度器就不能知道節點大概被分配了多少資源出去,調度器得不到準確信息,也就無法做出合理的調度決策,很容易造成調度不合理,有些節點可能很閒,而有些節點可能很忙,甚至 NotReady。
所以,建議是給所有容器都設置 request,讓調度器感知節點有多少資源被分配了,以便做出合理的調度決策,讓集羣節點的資源能夠被合理的分配使用,避免陷入資源分配不均導致一些意外發生。
老是忘記設置怎麼辦
有時候我們會忘記給部分容器設置 request 與 limit,其實我們可以使用 LimitRange 來設置 namespace 的默認 request 與 limit 值,同時它也可以用來限制最小和最大的 request 與 limit。
示例:
apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
namespace: test
spec:
limits:
- default:
memory: 512Mi
cpu: 500m
defaultRequest:
memory: 256Mi
cpu: 100m
type: Container
重要的線上應用改如何設置
節點資源不足時,會觸發自動驅逐,將一些低優先級的 Pod 刪除掉以釋放資源讓節點自愈。
- 沒有設置 request,limit 的 Pod 優先級最低,容易被驅逐;
- request 不等於 limit 的其次;
- request 等於 limit 的 Pod 優先級較高,不容易被驅逐。
所以如果是重要的線上應用,不希望在節點故障時被驅逐導致線上業務受影響,就建議將 request 和 limit 設成一致。
怎樣設置才能提高資源利用率
如果給給你的應用設置較高的 request 值,而實際佔用資源長期遠小於它的 request 值,導致節點整體的資源利用率較低。當然這對時延非常敏感的業務除外,因爲敏感的業務本身不期望節點利用率過高,影響網絡包收發速度。所以對一些非核心,並且資源不長期佔用的應用,可以適當減少 request 以提高資源利用率。
如果你的服務支持水平擴容,單副本的 request 值一般可以設置到不大於 1 核,CPU 密集型應用除外。比如 coredns,設置到 0.1 核就可以,即 100m。
儘量避免使用過大的 request 與 limit
如果你的服務使用單副本或者少量副本,給很大的 request 與 limit,讓它分配到足夠多的資源來支撐業務,那麼某個副本故障對業務帶來的影響可能就比較大,並且由於 request 較大,當集羣內資源分配比較碎片化,如果這個 Pod 所在節點掛了,其它節點又沒有一個有足夠的剩餘可分配資源能夠滿足這個 Pod 的 request 時,這個 Pod 就無法實現漂移,也就不能自愈,加重對業務的影響。
相反,建議儘量減小 request 與 limit,通過增加副本的方式來對你的服務支撐能力進行水平擴容,讓你的系統更加靈活可靠。
避免測試 namespace 消耗過多資源影響生產業務
若生產集羣有用於測試的 namespace,如果不加以限制,可能導致集羣負載過高,從而影響生產業務。可以使用 ResourceQuota 來限制測試 namespace 的 request 與 limit 的總大小。
示例:
apiVersion: v1
kind: ResourceQuota
metadata:
name: quota-test
namespace: test
spec:
hard:
requests.cpu: "1"
requests.memory: 1Gi
limits.cpu: "2"
limits.memory: 2Gi
如何讓資源得到更合理的分配
設置 Request 能夠解決讓 Pod 調度到有足夠資源的節點上,但無法做到更細緻的控制。如何進一步讓資源得到合理的使用?
我們可以結合親和性、污點與容忍等高級調度技巧,讓 Pod 能夠被合理調度到合適的節點上,讓資源得到充分的利用。
使用親和性
- 對節點有特殊要求的服務可以用節點親和性 (Node Affinity) 部署,以便調度到符合要求的節點,比如讓 MySQL 調度到高 IO 的機型以提升數據讀寫效率。
- 可以將需要離得比較近的有關聯的服務用 Pod 親和性 (Pod Affinity) 部署,比如讓 Web 服務跟它的 Redis 緩存服務都部署在同一可用區,實現低延時。
- 也可使用 Pod 反親和 (Pod AntiAffinity) 將 Pod 進行打散調度,避免單點故障或者流量過於集中導致的一些問題。
使用污點與容忍
使用污點 (Taint) 與容忍 (Toleration) 可優化集羣資源調度:
- 通過給節點打污點來給某些應用預留資源,避免其它 Pod 調度上來。
- 需要使用這些資源的 Pod 加上容忍,結合節點親和性讓它調度到預留節點,即可使用預留的資源。
彈性伸縮
如何支持流量突發型業務
通常業務都會有高峯和低谷,爲了更合理的利用資源,我們爲服務定義 HPA,實現根據 Pod 的資源實際使用情況來對服務進行自動擴縮容,在業務高峯時自動擴容 Pod 數量來支撐服務,在業務低谷時,自動縮容 Pod 釋放資源,以供其它服務使用(比如在夜間,線上業務低峯,自動縮容釋放資源以供大數據之類的離線任務運行) 。
使用 HPA 前提是讓 K8S 得知道你服務的實際資源佔用情況(指標數據),需要安裝 resource metrics (metrics.k8s.io) 或 custom metrics (custom.metrics.k8s.io) 的實現,好讓 hpa controller 查詢這些 API 來獲取到服務的資源佔用情況。早期 HPA 用 resource metrics 獲取指標數據,後來推出 custom metrics,可以實現更靈活的指標來控制擴縮容。官方有個叫 metrics-server 的實現,通常社區使用的更多的是基於 prometheus 的 實現 prometheus-adapter,而云廠商託管的 K8S 集羣通常集成了自己的實現,比如 TKE,實現了 CPU、內存、硬盤、網絡等維度的指標,可以在網頁控制檯可視化創建 HPA,但最終都會轉成 K8S 的 yaml,示例:
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: nginx
spec:
scaleTargetRef:
apiVersion: apps/v1beta2
kind: Deployment
name: nginx
minReplicas: 1
maxReplicas: 10
metrics:
- type: Pods
pods:
metric:
name: k8s_pod_rate_cpu_core_used_request
target:
averageValue: "100"
type: AverageValue
如何節約成本
HPA 能實現 Pod 水平擴縮容,但如果節點資源不夠用了,Pod 擴容出來還是會 Pending。如果我們提前準備好大量節點,做好資源冗餘,提前準備好大量節點,通常不會有 Pod Pending 的問題,但也意味着需要付出更高的成本。通常雲廠商託管的 K8S 集羣都會實現 cluster-autoscaler,即根據資源使用情況,動態增刪節點,讓計算資源能夠被最大化的彈性使用,按量付費,以節約成本。在 TKE 上的實現叫做伸縮組,以及一個包含伸縮功能組但更高級的特性:節點池(正在灰度)
無法水平擴容的服務怎麼辦
對於無法適配水平伸縮的單體應用,或者不確定最佳 request 與 limit 超賣比的應用,可以嘗用 VPA 來進行垂直伸縮,即自動更新 request 與 limit,然後重啓 pod。不過這個特性容易導致你的服務出現短暫的不可用,不建議在生產環境中大規模使用。
參考資料
- Understanding Kubernetes limits and requests by example: https://sysdig.com/blog/kubernetes-limits-requests/
- Understanding resource limits in kubernetes: cpu time: https://medium.com/@betz.mark/understanding-resource-limits-in-kubernetes-cpu-time-9eff74d3161b
- Understanding resource limits in kubernetes: memory: https://medium.com/@betz.mark/understanding-resource-limits-in-kubernetes-memory-6b41e9a955f9
- Kubernetes best practices: Resource requests and limits: https://cloud.google.com/blog/products/gcp/kubernetes-best-practices-resource-requests-and-limits
- Kubernetes 資源分配之 Request 和 Limit 解析: https://cloud.tencent.com/developer/article/1004976
- Assign Pods to Nodes using Node Affinity: https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/
- Taints and Tolerations: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/
- metrics-server: https://github.com/kubernetes-sigs/metrics-server
- prometheus-adapter: https://github.com/DirectXMan12/k8s-prometheus-adapter
- cluster-autoscaler: https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler
- VPA: https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler