參考文章:
- 10 most common mistakes using kubernetes
- Kubernetes Failure Stories
- Kubernetes in production — PodDisruptionBudget
主要參考了網(wǎng)上的一些文章和自己日常使用的一些小結(jié),以下是一些我們經(jīng)??吹降腻e誤:
- 資源(resources)——requests和limits配置不合理
- Liveness和Readiness探針配置不合理
- 為每個http服務(wù)都設(shè)置負(fù)載均衡
- 使用非Kubernetes感知的集群自動擴(kuò)展
- 未定義pod的反親和性
- 沒有使用PodDisruptionBudget
- 多租戶或多環(huán)境共享集群
- 使用latest標(biāo)簽
1. 資源(resources)——requests和limits
這個絕對是最值得注意以及最先拿出來講解的。
使用建議:
- CPU通常只建議設(shè)置一個較為合理的
request,而無需設(shè)置limit; - Memory通常建議設(shè)置成為
Guaranteed QoS,即request==limit;
常見的錯誤場景及可能導(dǎo)致的結(jié)果
關(guān)于CPU的有:
- 不設(shè)置 CPU request; (會導(dǎo)致資源搶占,驅(qū)逐pod,乃至集群雪崩)
- 將CPU請求設(shè)置得很低;(這樣我們就可以在每個節(jié)點(diǎn)上容納很多Pod。但是當(dāng)pod負(fù)載過高時,pod僅能獲取他所請求的那部分資源,會導(dǎo)致資源的限制成為瓶頸)
- 給CPU 設(shè)置limit ; (在CPU負(fù)載高的時候,workload的CPU limit成為瓶頸,導(dǎo)致延遲、超時等問題;關(guān)于CPU的limit有一些圍繞CPU CFS和CPU limit的討論,但是通常認(rèn)為設(shè)置CPU limit會引發(fā)更多的問題)
關(guān)于Memory的有:
- 沒有把Memory設(shè)置成為 “Guaranteed QoS”
Memory不像CPU,CPU不夠的時候會成為瓶頸,而Memory不夠的時候會導(dǎo)致OOM錯誤;如果想盡量保證服務(wù)的穩(wěn)定性,就設(shè)置成 “Guaranteed QoS”;
關(guān)于具體值的設(shè)置可以通過prometheus這類監(jiān)控工具查看集群狀態(tài)和pod的指標(biāo)來判斷。 GCP的VerticalPodAutoScaler 也可以幫助自動化這個過程。
2. Liveness和Readiness探針
使用建議:
- 除非你有很明確的場景,或者非常了解后果的情況下,通常建議定義且只定義readiness,因?yàn)?a target="_blank">liveness prob是有風(fēng)險的。
- 確保readiness定義了
- 確保readiness能真實(shí)的反映webserver以及對應(yīng)端口的可用情況
- readiness包含了數(shù)據(jù)庫初始化和migration
- 理解readiness的默認(rèn)行為:interval: 10s,timeout: 1s,successThreshold: 1, failureThreshold: 3
- 使用一個不同的端口如果可能的話;與正常的流量區(qū)分開來
- 可以使用readiness probe作為prewarming / cache loading, 預(yù)熱節(jié)點(diǎn)
- Liveness和Readiness probe 盡量不要依賴于外部的dependencies(例如數(shù)據(jù)庫),
- 避免導(dǎo)致級聯(lián)失效;
- 低耦合,其他服務(wù)的狀態(tài)由他自己的探針負(fù)責(zé);
- 如果非要設(shè)置Liveness,不要使用和Readiness同一個接口
- 不要使用 "exec" probes
常見錯誤場景:
- 沒有配置readiness
- 錯誤的配置readiness探針
- readiness == liveness
- 沒有優(yōu)雅的關(guān)閉
- 優(yōu)雅的關(guān)閉不夠優(yōu)雅
Liveness和Readiness probe的關(guān)系容易混淆。他們都在pod的全生命周期執(zhí)行
- Liveness在探測失敗時候會重啟節(jié)點(diǎn);
- Readiness在探針失敗時候,會斷開pod與k8s service的連接,并且不會把請求往pod上發(fā)送,直到再次探測成功;
如果一個配置了readiness的節(jié)點(diǎn)在請求量過大的時候,readiness可能失效,于是該節(jié)點(diǎn)暫時不再處理更多的請求;但是當(dāng)節(jié)點(diǎn)負(fù)載慢慢降低,readiness恢復(fù)時候,節(jié)點(diǎn)又能夠正常的處理請求。
但是如果該節(jié)點(diǎn)配置了相同的liveness 探針并且也失效了,那么該節(jié)點(diǎn)就會重啟。為什么你需要重啟一個健康的、并且正在處理很多請求的節(jié)點(diǎn)呢?
3. 為每個http服務(wù)都設(shè)置負(fù)載均衡
使用建議:
如果需要對外暴露接口的時候,最好使用ingress;或者使用"type: NodePort" 類型的service。
不要把每個Service設(shè)置成"type: LoadBalancer" 類型,該類型會調(diào)用云提供商的接口創(chuàng)建額外的資源,通常包括IP以及一些額外的計算資源;如果所有Service都是用該類型通常會有很多額外的花費(fèi)
4. 使用非Kubernetes感知的集群自動擴(kuò)展
使用建議:
使用auto scaler的時候,使用官方或社區(qū)推薦的auto scaler
當(dāng)在集群里面添加/刪除node的時候,你不應(yīng)該只考慮CPU/Memory的限制這些指標(biāo),還要考慮k8s中一些調(diào)度的約束,比如:pod & node affinities,taints & tolerations, resource request, QoS等。Scaling-in 也就是移除節(jié)點(diǎn)的過程會更加的復(fù)雜,例如stateful的pod與pv有綁定,而pv通常又屬于某個特定的zone的時候。
在使用auto-scaler 時候,auto scaler也需要理解這些配置,否則會導(dǎo)致pod調(diào)度失敗。目前社區(qū)通常使用cluster-autoscaler 來做集群的自動擴(kuò)容縮容。
5. 未定義pod的反親和性
使用建議:
明確的聲明反親和性,確保pod會調(diào)度到不同的node上:
// omitted for brevity
labels:
app: zk
// omitted for brevity
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- zk
topologyKey: "kubernetes.io/hostname"
- 因?yàn)橹皇钦{(diào)度時候需要檢查,因此
requiredDuringSchedulingIgnoredDuringExecution。 - 以上配置會根據(jù)
"kubernetes.io/hostname"來適用反親和性,如果有更定制化的HA需求,比如多區(qū)域部署,可以go deeper。
常見錯誤場景:
如果一個deployment為了高可用聲明了三個pod,但是這三個pod被調(diào)度到一個node上了,那么當(dāng)這個node 掛掉的時候,這個deployment所有的服務(wù)都不可用了。
6. 沒有使用PodDisruptionBudget
使用建議:
對于有HA需求的pod, 設(shè)置PodDisruptionBudget。
PodDisruptionBudget 控制器來保證在主動銷毀應(yīng)用POD的時候,不會一次性銷毀太多的應(yīng)用pod,從而保證業(yè)務(wù)不中斷或業(yè)務(wù)SLA不降級。
Cluster Managers 或者h(yuǎn)ost provider 應(yīng)當(dāng)使用能識別 PodDisruptionBudget 的eviction API 而不是直接刪除 pod, 例如kubectl drain 命令。當(dāng)要drain一個node的時候, kubectl drain 會嘗試不停的evict對應(yīng)機(jī)器上所有的pods, 請求也許會被temporarily的失敗,但是會不停的重試直到所有的pod都Terminated,或者達(dá)到了配置的timeout時間。
更多的細(xì)節(jié)介紹。
1、下面的例子使用了minAvailable參數(shù):
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: zookeeper
2、下面的例子使用了maxUnavailable參數(shù):
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: zookeeper
當(dāng)zk-pdb對象副本數(shù)是3的時候,上面這兩個例子所表達(dá)的意思是一樣的。
7. 多租戶或多環(huán)境共享集群
使用建議:
k8s的namespace并不提供很強(qiáng)的隔離性,因此盡量不要使用namespace來做多環(huán)境多租戶的隔離,例如不要把dev,qa,staging,sandbox這些環(huán)境和prod部署到一個集群里面。
盡管有一些資源公平性的配置,如: resource requests/limits, quotas, priorityClasses; 以及一些隔離性的配置,如: affinities,tolerations, taints;但是為了達(dá)到隔離,通常需要極為復(fù)雜的配置。
因此大部分時候,使用多個集群會更加的易于維護(hù)。
8. 使用latest標(biāo)簽
使用建議:
不要在deployment中的鏡像使用:latest標(biāo)簽,而是使用固定的版本。
否則可能會導(dǎo)致部署時候,k8s node使用本地的舊版本的image, 導(dǎo)致線上環(huán)境出現(xiàn)版本問題。