Kubernetes 應用最佳實踐 - 開篇

概述

本文從應用開發人員的角度,總結一下 Kubernetes 的業務開發中的最佳實踐,本文不會展示任何相關的 kubelet 命令以及 yaml 格式文件,通過儘可能精簡篇幅來提高讀者閱讀體驗和效率。

面向開發人員的 Kubernetes 資源結構圖

Namespace

Kubernetes 通過 Namespace 將資源進行邏輯上的隔離,默認情況下集羣中有三個命名空間:default、kube-public 和 kube-system, 通常情況下,有一個非生產的 Kubernetes 集羣用於特定環境 (開發、測試、集成),如果出於成本和業務服務獨立性考慮,將生產和非生產環境混合到一個集羣中, 則應該建立新的 Namespace 來進行業務服務區分與隔離,例如常見的 Namespace 是 testprod

此外,建立新的 Namespace 之後,應該爲不同的 Namespace 來配置不同的資源配額和資源限制。例如給測試環境分配較少的資源額度,給生產環境分配較多的資源額度, 這樣即使在測試環境對某個服務進行壓測,也不會影響到生產環境。

阿里雲 Kubernetes 集羣命名空間與配額

使用 RBAC 進行訪問控制

Kubernetes 中支持使用 RBAC 爲集羣中的用戶和組定義細粒度的訪問控制規則,例如可以將無狀態應用、有狀態應用、路由管理的功能開放給開發人員,將命名空間、資源配額與限制的功能開放給運維人員。

Pod

將 Pod 視爲獨立的機器,並將單個應用或者多個緊密耦合的應用放入單個 Pod 中。

對於多層應用,應該分散放到多個 Pod 中,典型的如 Web 應用,應該將前端頁面、後端接口、緩存、數據庫分別放入不同的 Pod 中。此外,不要將應用部署到單獨的 Pod 中,在保持應用可以水平擴展的前提下:

如何判定單個 Pod 中是否應該放入多個容器?這裏給出幾個判斷條件:

組合大於繼承

這條軟件設計原則針對 Pod 依然適用,大多數情況下,單個應用容器都是最佳實踐,對於某些多應用容器場景,可以通過將輔助容器中的應用使用子進程的方式運行,這樣就可以將多個容器應用集成到一個容器應用中。


容器

每個容器中的應用都應該遵循單一職責,只負責一個功能或執行一項任務。如果應用內部發生不可恢復的錯誤,應該讓它崩潰並自動退出,Kubernetes 會自動重啓該容器。

避免以 root 身份運行

容器共享主機的內核,因此隔離程度無法到達虛擬機級別,安全的策略就是禁止使用 root 身份運行應用。

鏡像優化

應該儘可能縮小鏡像體積,因爲當集羣自動擴容添加新節點時,新節點必須下載容器鏡像。容器的鏡像體積越小,節點下載速度越快,應用的啓動速度也就越快。

如果 CRI 容器使用的是 Docker,可以參考之前的 Docker 最佳實踐 [1]。

鏡像倉庫

如果所在企業沒有自己的基礎設施團隊,建議鏡像倉庫和雲服務器的提供商保持一致,這樣保證鏡像倉庫高可用的同時,可以節省帶寬費用並提升鏡像拉取速度 (因爲走的是內網)。

鏡像標籤和拉取策略

網上大多數文章會提到 不要使用 latest 作爲鏡像標籤,筆者不是特別認同,還是需要具體情況具體分析,筆者的建議是:

  1. 首先保證鏡像倉庫的高可用和訪問速度,這是重中之重,如果這一點做到了,那麼可以使用 latest 標籤並將鏡像拉取策略 imagePullPolicy 設置爲 Always

  2. 對於測試環境,直接使用 latest 標籤並將 imagePullPolicy 設置爲 Always, 配置 CI 構建腳本在打包應用鏡像時,除了 latest 標籤鏡像外,再額外打包一個以 git commit 哈希值作爲標籤的鏡像 (回滾時專用鏡像)

  3. 對於生產環境,使用應用迭代版本號作爲鏡像的標籤

使用多維度而不是單維度的標籤

80wfiL

Deployment

對於無狀態應用,直接使用 Deployment 部署。

阿里雲 Kubernetes Deployment 滾動升級策略

下面主要說下幾個重要的參數。

replicas

Pod 的副本數量。

maxUnavailable

指定滾動更新過程中不可用的 Pod 數量上限,數值可以是絕對數字,也可以是百分比 (最終會轉換成數字),例如 Pod 數量爲 10, maxUnavailable 值的 30%, 那麼在滾動升級過程中,始終保證可用的 Pod 數量爲 7 。

maxSurge

指定可以創建超出 replicas 的 Pod 數量,數值可以是絕對數字,也可以是百分比 (最終會轉換成數字),例如 Pod 數量爲 10, maxSurge 值爲 30%, 那麼在滾動升級過程中,始終 Pod 最大數量不超過 13 。

在滾動升級期間設置合理的 maxUnavailable 和 maxSurge 值,在資源消耗和保證服務可用性之間取得平衡。

minReadySeconds

指定新創建的 Pod 的最小就緒時間,只有超過這個時間 Pod 纔會被認爲可用,使用該參數更好掌握升級部署過程,避免 Pod 中的進程啓動後立即接受流量導致錯誤,同時減緩滾動升級的速率。

通常情況下需要根據業務場景計算出應用的初始化時間上限,同時還要考慮到應用的就緒時間條件,通過兩者的綜合考量,將 minReadySeconds 值設置地儘量高一些。

progressDeadlineSeconds

默認情況下, 10 分鐘內無法完成滾動升級將被判定爲失敗,對於大多數無狀態應用來說這個時間太長了,可以更新 spec.progressDeadlineSeconds 來自定義超時時間。

Recreate 策略

Deployment 的默認策略是 RollingUpdate, 也就是滾動升級。

此外還可以將策略設置爲 Recreate, Recreate 策略首先終止當前版本中的所有 Pod,然後創建並啓動新 Pod, 兩個過程之間會有一定的服務不可用空窗期, 該策略的應用場景是: 無法接受應用在滾動升級策略中同時存在的兩個版本,並且可以接受一定的服務不可用空窗期 (例如金融相關業務),可以在業務低峯期採用這種策略,並在用戶界面給出對應的維護公告。

DaemonSet

將所有由 systemd 管理的進程使用 DaemonSet 管理

ConfigMap

針對不同的環境單獨配置 ConfigMap, 可以將多個小的配置文件合併到一個大的配置文件中,參考 這個示例 [2]。

配置熱更新

熱更新 ConfigMap 會導致應用重新加載新的配置,但不會觸發應用程序重啓。

  1. 創建一個 ConfigMap 對象,包含應用的配置數據

  2. 在 Pod 配置中,將 ConfigMap 掛載爲一個卷(Volume), 應用可以直接從卷中讀取配置數據

  3. 當配置數據需要更新時,修改 ConfigMap

  4. 修改 ConfigMap 後,Kubernetes 會自動觸發相關的事件,通知 Pod 更新其掛載的卷

  5. Pod 接收到更新事件,重新加載卷中的配置文件,從而實現配置熱更新

圖片來源: https://www.manning.com/books/kubernetes-in-action-second-edition

Secret

確保採用 Secret 的方式存儲敏感數據,禁止使用環境變量、自定義加密方式等毫無意義的奇技淫巧。同時限制特定容器集合才能訪問 Secret, 讀取 Secret 之後保持只讀狀態,避免以明文存儲 Secret 數據或將 Secret 傳輸給第三方。

注意: Secrets 默認情況下采用 base64 編碼存儲非加密的形式存儲在 etcd 中,需要配置爲靜態加密方式

Job

正常情況下,Job 會在 Pod 中運行直到完成,但是,如果節點發生故障,或者當 Pod 在運行時被驅逐出節點時,調度器會將 Job 放在一個新的節點上重新運行。所以 Job 要保證冪等性 (例如創建數據表時的 IF NOT EXISTS 判斷語句),並且在狀態爲失敗時自動停止,不再重新啓動 (將 Restart Policy 配置爲 Never)。對於涉及到網絡請求的 Job, 筆者的建議是 將請求重試等策略放在應用代碼中,而不是通過配置 Job 的 Restart Policy 來完成

比較重要的兩個聲明字段:

CronJob

將週期性的自動化任務通過 CronJob 來執行,例如數據備份、迴歸測試、日誌分析、運營數據郵件、已有 Cron 任務的遷移等。

注意: 因爲工作節點本身受到資源使用和調度的限制,所以可能發生 Job 運行時間延時的情況 (和在應用層使用的定時任務組件延時情況類似),如果 Job 本身有較高的時間敏感度, 可以通過指定 startingDeadlineSeconds 字段來指定 Job 運行截止日期,這樣的話,如果 Job 運行時會檢測,如果運行時間大於截止日期,Job 將不再運行並且直接標記狀態爲失敗。

此外,不應該將 Job 的運行時間作爲業務時間序列屬性,對於多副本運行的 Job 要保證冪等性

不管是傳統的 Linux Cron 任務,還是通過守護進程 + 定時器組件模式,亦或是 Kubernetes 中的 Job, 這些都只是具體的運行時載體,最重要的是保證業務代碼的健壯性。

日誌

將 Pod 內應用日誌輸出到文件或標準輸出,然後通過日誌採集組件,最後將日誌數據發送到自建日誌倉庫或雲服務商提供的日誌聚合服務。

防火牆和網絡策略

大多數情況下,使用雲服務商提供的默認配置就可以滿足需求,如果需要自定義網絡策略,可以參考 官方文檔 [3]。

監控集羣

監控 Kubernetes 集羣對於確保應用程序的運行狀況和性能至關重要,使用 Prometheus、Grafana 等工具或雲服務商提供的原生監控解決方案,來收集和分析集羣中的指標, 例如 CPU 使用率、內存使用率和網絡流量等,同時設置警報和主動通知方式,保證集羣出現任何問題時收到通知。

集羣伸縮

集羣伸縮可以自動添加或者刪除工作節點,需要主要的是: 如果遇到業務在某個時刻流量突增的場景,則最好提前手動添加節點,畢竟新節點從啓動到加入到集羣並開始接收流量也需要時間,提前增加節點可以避免突增的流量造成負載過大甚至宕機。

升級到穩定的新版本

新的版本除了引入新功能之外,還包括漏洞和安全修復,可以定期升級到最新的穩定版本,享受官方升級帶來的紅利。擔心已有組件和新版本不兼容?主流雲服務商都有兼容性自動檢測功能,直接使用即可。

小結

隨着微服務架構的普及,實現一個簡單的服務都需要對雲原生中涉及到的分佈式技術棧和容器編排基礎知識有較深入的理解。因此,開發人員必須精通現代編程語言來實現業務功能,並且同時精通雲原生技術來解決非功能性需求。

Reference

擴展閱讀

鏈接

[1] Docker 最佳實踐: https://dbwu.tech/posts/docker_best_practices/

[2] 這個示例: https://github.com/kubernetes/examples/blob/master/guestbook/all-in-one/guestbook-all-in-one.yaml

[3] 官方文檔: https://kubernetes.io/docs/concepts/services-networking/network-policies/

[4] Kubernetes production best practices: https://learnk8s.io/production-best-practices

[5] Best practices for operating containers: https://cloud.google.com/architecture/best-practices-for-operating-containers

[6] Kubernetes best practices: Organizing with Namespaces: https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-organizing-with-namespaces

[7] Kubernetes best practices: How and why to build small container images: https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-how-and-why-to-build-small-container-images

[8] Kubernetes 實踐及常用技巧: https://kubernetes.feisky.xyz/practice/index

[9] 基於角色的訪問控制良好實踐: https://kubernetes.io/zh-cn/docs/concepts/security/rbac-good-practices/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/6fWdTsKe52VQtc00psrbhw