五大 Kubernetes 最佳實踐

在最近的一次 Weave 用戶組在線會議 WOUG[1] 上兩個工程師做了 Kubernetes 相關的分享。

谷歌雲的開發者佈道師 Sandeep Dinesh(@SandeepDinesh)做了一個演講,給大家列舉了在 Kubernetes 上運行應用的最佳實踐清單;Jordan Pellizzari(@jpellizzari),是來自 Weaveworks 的工程師,隨後也做了一個分享,內容是在他們使用 Kubernetes 開發運行 SaaS Weave Cloud 兩年之後學到的經驗教訓。

Kubernetes 最佳實踐

這篇演講中的最佳實踐來源於 Sandeep 和團隊進行的關於在 Kubernetes 上以多種不同方式運行同一任務的討論。他們把討論的結果總結爲一個最佳實踐的清單。

這些最佳實踐被分成以下類別:

  1. 構建容器

  2. 容器內部

  3. 部署

  4. 服務

  5. 應用架構

1、構建容器

不要信任任意的基礎鏡像

不幸的是我們看到這個錯誤一直在發生, Pradeep 說到。人們從 DockerHub 上隨便拉一個某人做的基礎鏡像——這麼做的理由僅僅是第一眼看過去這個鏡像裏面打包有他們需要的包——接着他們就把這個隨便選的鏡像推到生產環境中。

這麼做是非常錯誤的:你使用的代碼可能有很多漏洞,bug,錯誤版本,或者本身就被人有意把惡意軟件打包進去——只是你不知道罷了。

要減輕這種風險,你可以使用靜態分析工具,比如 CoreOS’ Clair[2] 或者 Banyon Collector[3] 來對容器進行漏洞掃描。

保持基礎鏡像儘量小

基於最簡潔的可用基礎鏡像,然後基於它構建軟件包,這樣你就知道鏡像裏面到底有哪些東西。

越小的基礎鏡像開銷也越小。你的應用可能只要 5M, 但是如果你盲目的隨便找一個鏡像,比如 Node.js, 它裏面就包括了額外 500M 你根本要不到的庫文件。

使用小鏡像的其它優勢有:

使用構建器模式

這種模式對靜態語言特別有用,編譯類似 Go,C++ 或者 Typescript for Node.js 這些語言時。

在這種模式裏你有一個構建容器,裏面打包有編譯器,依賴包,以及單元測試。代碼通過第一步之後產出構建的 artifacts,這包括所有的文件,bundles 等。然後再通過一個運行時容器,包括有監控和調試工具等。

到最後, 你的 Dockerfile 裏面將會只包含你的基礎鏡像以及運行時環境容器。

2、容器內部

在容器的內部使用非 root 用戶

如果你在容器內使用 root 來更新包,那麼你要把用戶改成非 root 用戶。

原因很簡單,如果你的容器有後門被人利用了而且你還沒把它的用戶改成 root 之外的,那麼一個簡單的容器逃離將會導致你整個主機的 root 權限都被利用。但是如果你改成了非 root 用戶,黑客就沒那麼容易得到 root 用戶的權限了。

做爲最佳實踐,你要對你的基礎設施加多層外殼保護。

在 Kubernetes 裏面你可以通過設置安全上下文 [4]runAsNonRoot: true 來實現,這樣會對整個集羣 cluster 來生效。

文件系統只讀

這一個最佳實踐通過設置 readOnlyFileSystem: true 來實現。

每個容器裏面跑一個進程

你當然可以在一個容器裏面跑多個進程,但是推薦跑一個。這是由編排器的工作方式決定的。Kubernetes 基於一個進程是否健康來管理容器。如果你在一個容器裏面有 20 個進程,它如何知道容器是否健康呢?

不要使用 Restart on Failure, 而應當 Crash Cleanly

Kubernetes 會重新啓動失敗的容器,因此你應該乾淨的做崩潰退出(給出一個錯誤碼),這樣 Kubernetes 就可以不用你的人工干預來成功重起了。

日誌打到標準輸出和標準錯誤輸出(stdout & stderr)

Kubernetes 缺省會監聽這些管道,然後將輸出傳到日誌服務上面去。在谷歌雲上可以直接用 StackDriver 日誌系統。

3、部署

使用 record 選項來使回滾更方便

在引用一個 yaml 文件時,請使用 --record 選項:

kubectl apply -f deployment.yaml --record

帶了這個選項之後,每次升級的時都會保存到部署的日誌裏面,這樣就提供了回滾一個變更的能力。

多使用描述性的標籤 label

因爲標籤可以是任意的鍵值對,其表達力非常強。參考下圖,以有名字爲'Nifty‘的應用部署到四個容器裏面。通過選擇 BE 標籤你可以挑選出後端容器。

使用 sidecar 來做代理、監視器等

有時候你需要一組進程跟其它某個進程通訊。但是你又不希望把它們所有的都放進一個容器裏面(前面提到的一個容器跑一個進程), 你希望的是把相關的進程都放到一個 Pod 裏面。

常見情況是你需要運行進程依賴的一個代理或者監視器,比如你的進程依賴一個數據庫, 而你不希望把數據庫的密碼硬編碼進每個容器裏面,這個時會你可以把密碼放到一個代理程序裏面當作 sidecar,由它來管理數據庫連接:

不要使用 sidecar 來做啓動引導

儘管 sidecar 在處理集羣內外的請求時非常有用,Sandeep 不推薦使用它做啓動。再過去,引導啓動(bootstraping)是唯一選項,但是現在 Kubernetes 有了 “init 容器”。

當容器裏面的一個進程依賴於其它的一個微服務時, 你可以使用 init 容器來等到進程啓動以後再啓動你的容器。這可以避免當進程和微服務不同步時產生的很多錯誤。

基本原則就是:使用 sidecar 來處理總是發生的事件,而用 init 容器來處理一次性的事件。

不要使用:latest 或者無標籤

這個原則是很明顯的而且大家基本都這麼在用。如果你不給你的容器加標籤,那麼它會總是拉最新的,這個 “最新的” 並不能保證包括你認爲它應該有的那些更新。

善用 readiness、liveness 探針

使用探針可以讓 Kubernetes 知道節點是否正常,以此決定是否把流量發給它。缺省情況下 Kubernetes 檢查進程是否在運行。但是通過使用探針, 你可以在缺省行爲下加上你自己的邏輯。

4、服務

不要使用 type: Loadbalancer

每次你在部署文件裏面加一個公有云提供商的 loadbalancer(負載均衡器)的時候,它都會創建一個。它確實是高可用,高速度,但是它也有經濟成本。

使用 Ingress 來代替,同樣可以實現通過一個 end point 來負載均衡多個服務。這種方式不但更簡單,而且更經濟。當然這個策略只有你提供 http 和 web 服務時有用,對於普通的 TCP/UDP 應用就沒用了。

Type: Nodeport 可能已經夠用了

這個更多是個人喜好,並不是所有人都推薦。NodePort 把你的應用通過一個 VM 的特定端口暴露到外網上。問題就是它沒有像負載均衡器那樣有高可用。比如極端情況,VM 掛了你的服務也掛了。

使用靜態 IP, 它們免費!

在谷歌雲上很簡單,只需要爲你的 ingress 來創建全局 IP。類似的對你的負載均衡器可以使用 Regional IP。這樣當你的服務 down 了之後你不必擔心 IP 會變。

將外部服務映射到內部

Kubernetes 提供的這個功能不是所有人都知道。如果您需要羣集外部的服務,您可以做的是使用 ExternalName 類型的服務。這樣你就可以通過名字來調用這個服務,Kubernetes manager 會把請求傳遞給它,就好像它在集羣之中一樣。Kubernetes 對待這個服務就好像它在同一個內網裏面,即使實際上它不在。

5、應用架構

使用 Helm Charts

Helm 基本上就是打包 Kubernetes 應用配置的倉庫。如果你要部署一個 MongoDB, 存在一個預先配置好的 Helm chart,包括了它所有的依賴,你可以十分容易的把它部署到集羣中。

很多流行的軟件 / 組件都有寫好了的 Helm charts, 你可以直接用,省掉大量的時間和精力。

所有下游的依賴是不可靠的

你的應用應該有邏輯和錯誤信息負責審計你不能控制的所有依賴。Sandeep 建議說你可以使用 Istio 或者 Linkerd 這樣的服務網格來做下游管理。

使用 Weave Cloud

集羣是很難可視化管理的。使用 Weave Cloud[5] 可以幫你監視集羣內的情況和跟蹤依賴。

確保你的微服務不要太 “微小”

你需要的是邏輯組件,而不是每個單獨的功能 / 函數都變成一個微服務。

使用命名空間來分離集羣

例如, 你可以在同一個集羣裏面創建 prod、dev、test 這樣不同的命名空間,同時可以對不同的命名空間分配資源, 這樣萬一某個進程有問題也不會用盡所有的集羣資源。

基於角色的訪問控制 RBAC

實施時當的訪問控制來限制訪問量, 這也是最佳的安全實踐。

從運行 Weave Cloud 生產環境學到的教訓

接下來 Jordan Pellizzari 做了一個演講,題目是在過去兩年我們在 Kubernetes 上開發運行 Weave Cloud 學到的經驗。我們當前運行在 AWS EC2 上, 總共有 72 個 Kubernetes 部署運行在 13 個主機和 150 個容器裏面。我們所有的持續性存儲保存在 S3,DynamoDB 或者 RDS 裏面, 我們並不在容器裏面保存狀態信息。關於我們如何搭建基礎設施的細節可以參看這篇文檔 [6]。

挑戰 1:對基礎設施做版本控制

在 Weaveworks 我們把所有的基礎架構保存在 Git 中, 如果我們要對基礎設施做變更,要像代碼一樣提 Pull request。我們把這稱爲 GitOps,也寫了多篇博文。你可以從這篇讀起:GitOps - Pull Request 支撐的運維 [7]。

在 Weave, 所有的 Terraform 腳本,Ansible 以及 Kubernetes YAML 文件都被保存在 Git 裏面做版本控制。

把基礎架構放在 Git 裏面是一個最佳實踐,這有多個原因:

問題:當生產與版本控制不一致時該怎麼辦?

除了把所有內容保存在 Git 中之外,我們也有一個流程會檢查生產集羣中運行的狀態與版本控制中的內容差異。如果檢查到有不同,就會給我們的 Slack 頻道發一個報警。

我們使用一個叫 Kube-Diff[8] 的開源工具來檢查不同。

挑戰 2:自動化的持續交付

自動化你的 CI/CD 流水線,避免手工的 Kubernetes 部署。因爲我們一天內做多次部署,這種方式節約了團隊的時間也避免了手工容易發生錯誤的步驟。在 Weaveworks,開發人員只需要做一個 Git push,然後 Weave Cloud 會做以下的事情:

GitOps 流水線

這裏有一篇稍長的文章 [9],我們認爲的構建自動化 CI/CD 流水線的最佳實踐都在裏面描述了。

總結

Sandeep Dinesh 做了一個關於創建、部署、運行應用到 Kubernetes 裏面的五個最佳實踐的深度分享。隨後 Jordan Pellizzari 做了 Weave 如何在 kubernetes 中管理 SaaS 產品 Weave Cloud 和經驗教訓的分享。

相關鏈接:

  1. https://www.meetup.com/pro/weave/

  2. https://github.com/coreos/clair

  3. https://github.com/banyanops/collector

  4. https://kubernetes.io/docs/tasks/configure-pod-container/security-context/

  5. https://www.weave.works/features/troubleshooting-dashboard/

  6. https://www.weave.works/technologies/weaveworks-on-aws/

  7. https://www.weave.works/blog/gitops-operations-by-pull-request

  8. https://github.com/weaveworks/kubediff

  9. https://www.weave.works/blog/the-gitops-pipeline

鏈接: https://cloud.tencent.com/developer/article/1151963

原文: https://dzone.com/articles/top-5-kubernetes-best-practices-from-sandeep-dines

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