使用 Kubernetes 時的 10 個常見錯誤

筆記。 翻譯。:本文作者是捷克一家小公司pipetail的工程師。 他們設法整理了一份精彩的清單,其中列出了與 Kubernetes 叢集操作相關的[有時很平庸,但仍然]非常緊迫的問題和誤解。

使用 Kubernetes 時的 10 個常見錯誤

在使用 Kubernetes 的這些年裡,我們使用了大量的叢集(包括託管和非託管 - 在 GCP、AWS 和 Azure 上)。 隨著時間的推移,我們開始注意到一些錯誤不斷重複。 然而,這並沒有什麼可恥的:其中大部分都是我們自己完成的!

本文包含最常見的錯誤,並提到如何修正這些錯誤。

1. 資源:請求與限制

這個項目絕對值得最密切的關注並排在列表的第一位。

通常CPU請求 要么根本沒有指定,要么具有非常低的值 (在每個節點上放置盡可能多的 Pod)。 因此,節點變得過載。 在高負載期間,節點的處理能力得到充分利用,特定工作負載僅接收其「請求」的內容 CPU節流。 這會導致應用程式延遲增加、逾時和其他令人不快的後果。 (在我們最近的其他翻譯中了解更多相關資訊:“Kubernetes 中的 CPU 限制和主動限制“ - 大約。 譯)

最大努力 (極度 沒有 受到推崇的):

resources: {}

極低的CPU請求(極 沒有 受到推崇的):

   resources:
      Requests:
        cpu: "1m"

另一方面,CPU 限制的存在可能會導致 Pod 不合理地跳過時脈週期,即使節點處理器未滿載。 同樣,這可能會導致延誤增加。 圍繞參數的爭議仍在繼續 CPU CFS 配額 在 Linux 核心中,CPU 節流取決於設定的限制,以及停用 CFS 配額...唉,CPU 限制可能會導致比其所能解決的問題更多的問題。 有關此內容的更多資訊可以在下面的連結中找到。

選擇過多 (過度投入) 記憶力問題可能會導致更大的問題。 達到 CPU 限制需要跳過時脈週期,而達到記憶體限制則需要殺死 pod。 你有沒有觀察過 OOMkill? 是的,這正是我們正在討論的。

您想盡量減少這種情況發生的可能性嗎? 不要過度分配內存,並透過將內存請求設為限制來使用保證的 QoS(服務品質)(如下例所示)。 閱讀有關此內容的更多信息 亨寧·雅各布斯演講 (Zalando 首席工程師)。

爆裂 (被 OOMkilled 的幾率更高):

   resources:
      requests:
        memory: "128Mi"
        cpu: "500m"
      limits:
        memory: "256Mi"
        cpu: 2

保證:

   resources:
      requests:
        memory: "128Mi"
        cpu: 2
      limits:
        memory: "128Mi"
        cpu: 2

設定資源時什麼可能有幫助?

指標伺服器 您可以查看 Pod(以及其中的容器)目前的 CPU 資源消耗和記憶體使用量。 最有可能的是,您已經在使用它了。 只需執行以下命令:

kubectl top pods
kubectl top pods --containers
kubectl top nodes

但是,它們僅顯示當前的使用情況。 它可以讓您粗略地了解數量級,但最終您需要 指標隨時間變化的歷史記錄 (回答諸如「CPU 尖峰負載是多少?」、「昨天早上的負載是多少?」等問題)。 為此,您可以使用 普羅米修斯, DataDog 和其他工具。 他們只是從指標伺服器獲取指標並儲存它們,用戶可以查詢它們並相應地繪製它們。

VerticalPodAutoscaler 它允許 自動化 這個流程。 它追蹤 CPU 和記憶體使用歷史記錄,並根據此資訊設定新的請求和限制。

有效地利用運算能力並不是一件容易的事。 就像一直在玩俄羅斯方塊一樣。 如果您為平均消耗較低(例如約 10%)的運算能力支付過高費用,我們建議您考慮基於 AWS Fargate 或 Virtual Kubelet 的產品。 它們建立在無伺服器/按使用付費的計費模型之上,在這種情況下可能會更便宜。

2. 活躍度和就緒度探測

預設情況下,Kubernetes 中未啟用活動性和就緒性檢查。 有時他們忘記打開它們......

但是,如果發生致命錯誤,您還能如何啟動服務重新啟動呢? 負載平衡器如何知道 Pod 已準備好接受流量? 或者它可以處理更多流量?

這些測試經常相互混淆:

  • 活潑 —「生存性」檢查,如果失敗則重新啟動 pod;
  • 準備就緒 — 準備狀況檢查,如果失敗,它會中斷 pod 與 Kubernetes 服務的連線(可以使用下列指令進行檢查) kubectl get endpoints)並且在下一次檢查成功完成之前流量不會到達它。

這兩項檢查 在 Pod 的整個生命週期內執行。 這是非常重要的。

一個常見的誤解是,就緒探針僅在啟動時運行,以便平衡器可以知道 pod 已準備就緒(Ready)並且可以開始處理流量。 然而,這只是它們的使用選項之一。

另一個是可能會發現 pod 上的流量過多且 使其超載 (或 Pod 執行資源密集型運算)。 在這種情況下,準備檢查會有所幫助 減少吊艙的負載並“冷卻”它。 成功完成未來的準備檢查可以 再次增加 Pod 的負載。 在這種情況下(如果就緒測試失敗),活性測試的失敗將產生非常適得其反的效果。 為什麼要重啟一個健康且努力工作的 Pod?

因此,在某些情況下,完全不進行檢查比使用錯誤配置的參數啟用它們要好。 如上所述,如果 活性檢查複製就緒檢查,那麼你就有大麻煩了。 可能的選項是配置 僅準備就緒測試危險的活力 撇開。

當公共依賴項失敗時,這兩種類型的檢查都不應該失敗,否則這將導致所有 pod 的級聯(類似雪崩)失敗。 換句話說, 不要傷害自己.

3.每個HTTP服務的LoadBalancer

最有可能的是,您的叢集中有 HTTP 服務,您希望將其轉送到外部世界。

如果您將服務開啟為 type: LoadBalancer,它的控制器(取決於服務提供者)將提供並協商一個外部LoadBalancer(不一定運行在L7上,甚至可以運行在L4上),這可能會影響成本(外部靜態IPv4位址、運算能力、每秒計費) )由於需要創建大量此類資源。

在這種情況下,使用一個外部負載平衡器更為合乎邏輯,將服務開放為 type: NodePort。 或者更好的是,擴展類似的東西 nginx 入口控制器 (或者 特拉菲克),誰將是唯一的一個 節點端口 與外部負載平衡器關聯的端點,並將使用以下方式在叢集中路由流量 入口-Kubernetes 資源。

其他相互互動的集群內(微)服務可以使用諸如 集群IP 以及透過 DNS 的內建服務發現機制。 只是不要使用他們的公共 DNS/IP,因為這會影響延遲並增加雲端服務的成本。

4. 在不考慮集群特性的情況下自動擴展集群

在向叢集新增節點或從叢集中刪除節點時,不應依賴一些基本指標,例如這些節點上的 CPU 使用率。 Pod 規劃必須考慮許多因素 限制,例如 Pod/節點親和性、污點和容忍度、資源請求、QoS 等。 使用不考慮這些細微差別的外部自動縮放器可能會導致問題。

想像一下,應該調度某個 pod,但所有可用的 CPU 功率都被請求/分解,並且該 pod 陷入某種狀態 Pending。 外部自動縮放器看到平均當前 CPU 負載(不是請求的負載)並且不會啟動擴展 (向外擴展) - 不新增另一個節點。 因此,該 Pod 將不會被調度。

在這種情況下,反向縮放 (縮小) — 從叢集中刪除節點總是更難實現。 想像一下,您有一個有狀態的 Pod(連接了持久性儲存)。 持久卷 通常屬於 特定可用區 並且不會在該地區複製。 因此,如果外部自動縮放程式刪除了具有該 pod 的節點,則排程器將無法將該 pod 調度到另一個節點上,因為這只能在持久性儲存所在的可用區中完成。 Pod 將停留在狀態 Pending.

在 Kubernetes 社群中非常受歡迎 集群自動縮放器。 它在叢集上運行,支援主要雲端提供者的 API,考慮到所有限制,並且可以在上述情況下進行擴展。 它還能夠在保持所有設定限制的同時進行縮減,從而節省資金(否則這些資金將花在未使用的容量上)。

5.忽視IAM/RBAC能力

小心使用具有持久金鑰的 IAM 用戶 機器和應用。 使用角色和服務帳戶組織臨時訪問 (服務帳戶).

我們經常遇到這樣的情況:存取金鑰(和機密)在應用程式配置中被硬編碼,並且儘管可以存取 Cloud IAM,但仍忽略機密的輪換。 在適當的情況下使用 IAM 角色和服務帳戶而不是使用者。

使用 Kubernetes 時的 10 個常見錯誤

忘記 kube2iam 並直接進入服務帳戶的 IAM 角色(如中所述 同名註釋 什捷潘‧弗拉尼 (Štěpán Vraný):

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
  name: my-serviceaccount
  namespace: default

一注。 沒那麼難吧?

另外,不要授予服務帳戶和實例設定檔權限 admin и cluster-admin如果他們不需要的話。 這實現起來有點困難,尤其是在 RBAC K8s 中,但絕對值得付出努力。

6. 不要依賴 pod 的自動反親和力

想像一下,您在一個節點上擁有某個部署的三個副本。 該節點崩潰了,所有副本也隨之崩潰。 情況不愉快吧? 但為什麼所有副本都在同一個節點上? Kubernetes 不是應該提供高可用性(HA)嗎?!

不幸的是,Kubernetes調度器,主動地不遵守單獨存在的規則 (反親和力) 對於豆莢。 必須明確說明:

// опущено для краткости
      labels:
        app: zk
// опущено для краткости
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"

就這樣。 現在 Pod 將被調度到不同的節點上(僅在調度期間檢查此條件,而不是在其操作期間檢查 - 因此 requiredDuringSchedulingIgnoredDuringExecution).

這裡我們談論的是 podAntiAffinity 在不同節點上: topologyKey: "kubernetes.io/hostname", - 而不是關於不同的可用區域。 要實現成熟的 HA,您必須更深入地研究這個主題。

7. 忽略 PodDisruptionBudgets

假設您的 Kubernetes 叢集上有生產負載。 節點和叢集本身必須定期更新(或停用)。 PodDisruptionBudget (PDB) 類似於叢集管理員和使用者之間的服務保障協定。

PDB可以讓您避免因缺少節點而導致的服務中斷:

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

在此範例中,您作為叢集的用戶,向管理員聲明:“嘿,我有一個 Zookeeper 服務,無論您做什麼,我都希望該服務的至少 2 個副本始終可用。”

您可以閱讀更多相關內容 這裡.

8. 公共集群中的多個使用者或環境

Kubernetes 命名空間 (命名空間) 不提供強絕緣.

一個常見的誤解是,如果您將非產品負載部署到一個名稱空間中,並將產品負載部署到另一個名稱空間中,那麼它們 不會以任何方式互相影響...但是,可以使用資源請求/限制、設定配額和設定優先權來實現一定程度的隔離。 資料平面中的一些「物理」隔離是由親和力、容忍度、污點(或節點選擇器)提供的,但這種隔離是相當有效的。 實施。

那些需要將兩種類型的工作負載組合在同一叢集中的人將不得不應對複雜性。 如果沒有這樣的需要,而且你有能力擁有一個 又一簇 (例如,在公有雲中),那麼最好這樣做。 這將實現更高水平的絕緣。

9. 外部流量策略:集群

我們經常看到叢集內的所有流量都通過 NodePort 之類的服務,並為其設定了預設策略 externalTrafficPolicy: Cluster... 這意味著 節點端口 在叢集中的每個節點上都是開放的,您可以使用其中任何一個節點與所需的服務(pod 集)進行互動。

使用 Kubernetes 時的 10 個常見錯誤

同時,與上述NodePort服務關聯的真實Pod通常僅在某個特定的連接埠上可用。 這些節點的子集。 換句話說,如果我連接到一個沒有所需 Pod 的節點,它會將流量轉送到另一個節點, 添加一跳 增加延遲(如果節點位於不同的可用區域/資料中心,延遲可能會相當高;此外,出口流量成本也會增加)。

另一方面,如果某個 Kubernetes 服務設定了策略 externalTrafficPolicy: Local,那麼 NodePort 僅在所需 pod 實際運行的節點上開啟。 使用檢查狀態的外部負載平衡器時 (健康檢查) 端點(它是如何做的 AWS彈性負載均衡器), 他 只會將流量傳送到必要的節點,這將對延遲、計算需求、出口費用產生有益的影響(常識也是如此)。

您很有可能已經在使用類似的東西 特拉菲克nginx 入口控制器 作為 NodePort 端點(或 LoadBalancer,也使用 NodePort)來路由 HTTP 入口流量,設定此選項可以顯著減少此類請求的延遲。

В 本出版品 您可以詳細了解externalTrafficPolicy及其優點和缺點。

10. 不要與叢集綁定,不要濫用控制平面

以前,習慣上用專有名稱來呼叫伺服器: 安東、HAL9000 和 Colossus...如今它們已被隨機產生的識別碼所取代。 然而,這個習慣仍然存在,現在專有名稱變成了叢集。

一個典型的故事(基於真實事件):這一切都始於概念驗證,因此該集群有一個自豪的名字 測試……很多年過去了,它仍然在生產中使用,每個人都不敢碰它。

簇變成寵物並沒有什麼樂趣,所以我們建議在練習時定期將它們移除 災難復原 (這將有助於 混沌工程 - 大約。 譯)。 另外,在控制層工作也不會有什麼壞處 (控制平面)。 害怕碰他可不是什麼好兆頭。 死的? 夥計們,你們真的有麻煩了!

另一方面,您不應該因操縱它而得意忘形。 隨著時間的推移 控制層可能會變慢。 最有可能的是,這是由於在沒有旋轉的情況下創建了大量物件(在預設設定下使用Helm 時的常見情況,這就是為什麼它在configmaps/secrets 中的狀態沒有更新- 因此,數千個物件累積在控制層)或不斷編輯 kube-api 物件(用於自動擴展、CI/CD、監控、事件日誌、控制器等)。

此外,我們建議檢查與託管 Kubernetes 提供者的 SLA/SLO 協定並注意保證。 賣家可以保證 控制層可用性 (或其子元件),但不是您發送給它的請求的 p99 延遲。 換句話說,您可以輸入 kubectl get nodes,並在10分鐘後收到答复,這不會違反服務協議的條款。

11. 獎勵:使用最新標籤

但這已經是經典了。 最近,我們很少遇到這種技術,因為許多人從痛苦的經歷中吸取了教訓,並且已經停止使用該標籤 :latest 並開始固定版本。 萬歲!

ECR 保持影像標籤的不變性; 我們建議您熟悉這個顯著的功能。

總結

不要指望一切都能在一夜之間奏效:Kubernetes 不是萬能藥。 糟糕的應用程式 即使在 Kubernetes 中也將保持這種方式 (而且情況可能會變得更糟)。 粗心大意會導致控制層過於複雜、緩慢且工作壓力大。 此外,您還可能面臨沒有災難復原策略的風險。 不要指望 Kubernetes 能夠提供開箱即用的隔離和高可用性。 花一些時間讓您的應用程式真正實現雲端原生。

你可以熟悉各個團隊不成功的經歷 這本故事集 作者:亨寧‧雅各布斯。

那些希望添加到本文中給出的錯誤清單的人可以在 Twitter 上聯繫我們(@馬雷克巴蒂克, @MstrsObserver).

譯者PS

另請閱讀我們的博客:

來源: www.habr.com

添加評論