1. 程式人生 > >谷歌大神詳解 Kubernetes 配置管理最佳方法_Kubernetes中文社群

谷歌大神詳解 Kubernetes 配置管理最佳方法_Kubernetes中文社群

於夢琦 / 美國谷歌軟體工程師

嘉賓介紹:

美國谷歌 Kubernetes/Google Container Engine(GKE)組核心成員,主要從事CLI(kubectl)開發以及配置管理的研究與開發。 本科和碩士分別畢業於上海交通大學和 UCSD

大家好!我是來自谷歌的於夢琦。今天我來給大家介紹一下 —— Kubernetes 配置管理中的最佳方法。

Kubernetes Concepts

1、Kubernetes 中的概念

我們先來說一下 Kubernetes 中幾個重要的概念:第一個要說也是最重要的就是 Declarative,Declarative 的意思是申明的、陳述的,與之相反的是 Imperative,Imperative 意思是命令式的。首先說一下 Kubernetes 設計完全是按照 Declarative 設計的。但為了初學者的學習方便,也是支援 Imperative。Declarative 對

Kubernetes 的功能非常重要,比如說它的自育性、自治能力都完全是依賴於 Declarative。第二個概念是 Level-triggered,Edge-triggered 。第三個概念是非同步的。這些概念在後面會詳細的介紹。

2、Declarative 與 Imperrative 的對比

首先看一下 Declarative 和 Imperative 的區別和對比。Declarative 的定義是使用者設定期望的狀態,系統會知道它需要執行什麼操作,來達到期望的狀態。比如說我們看下面的例子。使用者期望的狀態是三個,系統觀察到的狀態是兩個。於是系統就會知道建立一個新的來達到最終狀態是三個。而對於 Imperative,需要使用者告訴系統需要做什麼。比如說使用者說建立一個新的 Container,系統才會建立一個新的 Container。

3、Spec 與 Status 的對比

大部分的 Kubernetes API 都有 Spec 和 Status 兩個 Field。Spec 是讓使用者寫入期望的狀態,系統可以通過 Spec 讀出使用者的期望。Status 是系統寫入觀察到的狀態,使用者可以從中讀出系統當前是什麼狀態。

4、Reconcile

這兩個 Field 對於 Reconciliation Loop 是非常重要的,接下來看一下什麼叫 Reconcile?

Reconcile 中文意思是 “調和”,“和解” 的意思,簡單的說就是它不斷使系統當的狀態,向用戶期望的狀態移動。比如說右邊的例子,使用者期望的 Replica 是三個,Controller 通過 Watch 發現期望的狀態是 3 個,但實際觀測到的 Replica 的是 2 個,所以它就會 Create 一個新的 Pod。然後 Controller 會繼續 Watch 這些 Pod,當它發現 Create 完成了,就會更新 Status 到 3 個,使 Status 和 Spec 達到一致的狀態。

5、Level-triggered 與 Edge-triggered 的對比

接下來再來說一下 Level–triggered 和 Edge-triggered 的區別。首先說一下 Kubernetes 是 Level-triggered。先看一下右邊的例子。最上面是使用者期望的狀態變化。中間的是某一個 Edge-triggered Controller 的變化。最下面是 Level-triggered 的 Controller 的狀態。在第二條虛線這個時刻,使用者希望更新應用的版本,從 V1 到 V2,然而在第一條虛線和第三條虛線中間,我們的 Controller 死了。

對於 Edge-triggered 的 Controller 就會錯過這個使用者的更新請求,狀態就沒有變化。對於我們的 Level-triggered 的 Controller 就會在重新啟動的時候發現使用者的請求變化,就會更新版本到 V2。在這個時候使用者想 Scale 他的應用的 Replica 到 3 個,這時候我們可以看到 Edge-triggered 的 Controller 就會直接 Scale V1,最終達到一個錯誤的狀態。Level-triggered 的 Controller 就會最終達到正確的狀態。

Kubelet Commands

Kubectl 的 Commands 可以被分為三類:Imperative Commands、Imperative Object Configuration 和 Declarative Object Configuration。

1、Imperative Commands

第一類是叫做 Imperative Commands。Imperative 的 Commands 主要就是通過命令列的 Flag 直接進行 Imperative 的操作,Create 或者是 delete 之類的。例子是 Kubectl run, Expose,或者是 Create deployment,它就會把底層對於物件配置的操作對使用者隱藏了起來。

接下來我們再來看一下它們優缺點的對比。Edge-triggered 的 Controller 優點就是直接、簡單,易於實現。缺點就是如果錯過了一個 Edge,可能就會導致一個最終的錯誤的狀態。Level-triggered Controller 的優點就是不用擔心這個,Edge-triggered Controller 另外一個缺點是不會採用直接的路徑達到當前的狀態,比如說當前的版本是 V1,使用者想要更新到 V2,緊接著使用者發現了什麼問題,馬上想更新到 V3。對一個 Edge-triggered 的 Controller 就會完成 V2 的更新,然後再去更新 V3。而如果對一個 Level-triggered 的 Controller 會在接收到 V3 的更新請求的時候直接開始更新,而不會等完成 V2 再更新 V3。

2、Imperative Object Configuration

第二類要說的是 Imperative 物件配置,Object configuration 的意思是一個物件可以被定義為 yaml 或者 json 的格式儲存在檔案中,我們叫它 Object Configuration。Imperative Object Configuration 是直接對 Object Configuration 進行 Imperative 的操作,比如說 Kubectl create, Replace,Delete。

然後說一下第一類和第二類的對比。第一種 Imperative 的 Commands 優點是簡單直接易學易記,缺點是沒有辦法使用 Change review,就是先更改再去批准流程,而且沒有辦法使用 Audittrails。Audit trails 的意思就是審計追蹤又可以叫做 Audit log,它會記錄系統的各種變化。第三個缺點是沒法提供一個模板。相對於 Imperative Commands, Imperative Object Configuration 的優點是可以進行版本控制,比如說可以用 Git,也可以用 Change review 這個流程,也可以用 Autid trails,它的確定就是需要使用者對於物件的 Schema 有一個基本的理解,另一個缺點就是需要使用者寫 yaml 檔案。

3、Declarative Object Configration

第三類是 Kubectl 的 Commands 是 Declarative 的物件配置。它也是直接對 Object Configuration 進行操作的。使用者不需要指定要 Create,Update 還是Delete,Commands 直接會幫你決定誰用哪一種。例子就是 ubectl apply –prune-f。

接下來對比第二類和第三類的區別。Imperative Object Configuration 對於 Declarative 的優點就是簡單一些,稍微易理解一些。但它的缺點就是對於一個目錄的配置檔案只能選擇同一種操作。它的第二個缺點就是其他使用者的更新,必須被反應在配置檔案中,不然其他使用者的更新就會在下一次的更新中被丟掉。

接下來說一下 Declarative Object Configuration的優點。它的優點就是其他使用者的更新可以被保留下來,比如說我的 Autoscaler 幫你管理的 Replica 這個 Field,你管理其他的 Field,你們之間就不會有衝突。第二個優點是對同一個目錄下的物件可以有不同的操作,可以是 Create、Patch、Delete,缺點就是這個行為稍微難理解一些,需要時間來學習。

Configuration Management

1、Configuration Management

接下來說一下配置的管理。先從簡單的說,如果是一個新手使用者,我們建議他從最開始的 Imperative Commands 開始用起、學起,接下來可以嘗試使用 Imperative Object Configuration。而對於比較高階的使用者,我們推薦用 Declarative Object Configuration 來管理他的配置,同時要使用版本控制和 Change review 的流程。

對於新手使用者來說,Kubernetes API 就是存放期望狀態唯一的地方,它能支援一定數量的版本歷史,但無法支援很長的歷史,也不支援跨物件的歷史。

對於高階使用者,我們推薦將期望的狀態存在版本控制中,好處就是可以支援跨物件的歷史。比如說有多個 Deployment,或者是其他 API 的 Resource,他們之間有互相的依賴關係。我們可以在整個系統在一個好的狀態下,把整個 Repo 做一個 Tag。如果我們在後面的生產中遇到重大的問題需要回滾,可以直接回滾到之前的好的狀態的 Tag 上。

另外是推薦使用 Autoreconciliation。你把配置存放到 Git Repo 中,然後有一個工具可以自動的把配置應用到 Kubernetes 叢集上。這裡有一個 BOX 做的開源專案,就是 Kube-applier.

2、Workflow

隨後,於夢琦老師在現場做了一個Demo, 感興趣的同學,可以戳“閱讀原文”觀看。

Declarative APP、Management

1、Declarative Application Management

Declarative APP Management,這是我們正在開發的一個工作流程,比之前的只多了前面的一個步驟。最下面的東西相當是一個應用商店,後面的三個步驟基本上是一樣的。有了這個應用商店,可以從中找到通用的應用,比如說找一個 Redis、Cassandra,然後可以把這個配置的 Repo Fork 下來,可以做一些你想要的改變,到時候會有一些工具幫助你做改變。

比如說所有的 Resource 前面都加 Label,或者是加上一些名字字首,後面一步可選擇就是可以建立一個 Overlay,前三步可以到一個 Base 的 Layer,第四步可以把自定義的 Ovrelay 應用到前面三步產生的 Base。可以把這個 Qverlay 想象成一個 Patch。比如說有 3 個不同的 Production,Staging 和開發的環境,就有三個不同的 Overlay,要應用到相同的 Base 上,可以拿到三個不同的 Configuration,可以應用於三個不同的環境。後面步驟是一樣的。

2、Package Management

再簡單說一下 Package Management。現在 Kubernetes 中的 Package Management有一個 Helm。一些應用被寫成了 Chart 的格式,可以用 Helm 跑起來。但現在遇到的挑戰,就是很難在此之中找到平衡,既使我們有足夠多的引數滿足所有的使用者的需求,又能使這個應用不會因為引數過多導致難以維護,所以我們想推前面的 Declarative Application Management。下面是一個 Jenkins 的例子說明引數多過導致的難以維護。這是一個 Recap,有 Declarative 和 Imperative,還有 Spec 和 Status,還有 Commands 和 Configuration Management。

Q&A

Q1:我有兩個問題,剛才介紹了一個 Level-Triggered 是怎麼實現的?第二個問題是一個應用商店,不知道應用在什麼場景下?我們通常也是拉一些 Redis,取得一些映象,做一些更改,跑一些 Pod 之類的。

A:先說下你的第二個問題,我們的應用商店想說的是關於某一個應用的 Kubernetes 應用商店,是配置檔案,不是說這些 Image,你可以 Fork 這個 Repo,它可以直接跑起來,也可以做自定義的修改。

Q2:就是已經配置好的?

A:它是完整的配置檔案。有很多 yaml 檔案。

Q3:第一個問題是 Level Trap Kubernetes 怎麼實現的,現在 Kubernetes 是這樣做的是嗎?

A:基本上是。現在 Kubernetes 每個 Controller 會有一個 Local Store。它會不斷把變數拿過來,定期做 sync,sync 就會拿到使用者期望的狀態,它會不斷的向 API Server請求。

Q4:我想提一下問題是剛才你用 Git 替換它的版本,這就會產生一個問題,如果用灰度釋出或者是其他的釋出,我們都是一節一節的提上去,這樣 Git 全部提上去,不會產生很大的危險嗎?

A:我覺得你應該可以這樣做,比如說要一步一步,第一步做一個 Commit,再第二步做一個 Commit,最後進行釋出,如果釋出成功了,Monitor 了一段時間,發現沒有問題可以打一個 Tag,如果中間有問題可以 Roll back 到最初的版本。

Q5: 你現在寫了一個指令碼,Git update 的形式,把一個版本跟蹤上去,後端有一個服務不斷的查詢是否有更新,更新了之後把自己的服務更新上去,用自己的 Git Control Apply?這樣就產生一個問題,我的商業服務比較多,這樣不能產生灰度的更新,就是一部分一部分地更新。

A:我這個指令碼只是用簡單的例子想說明概念,這個指令碼只是用最簡單的 Kubectl Apply 來做的。你可以自己寫一些自定義的 Apply 的工具,你規定它只會看到新的 Tag 才會更新,普通的 Push 不會每次更新,可以做這樣的自定義的修改。你說的分散式的更新,比如說這個 Region 有一個對應的 Applier,可以根據你的規則只更新這個區域,不同的區域有不同的更新規則,我覺得應該可以這樣做。

Q6: 等於說這些過程需要完成,而不是 Kubernetes 後期有這樣相應的東西支撐?

A:後期應該會有,在 Slides 放了一個例子,就是 Auto Applier,這是在美國的一個叫做 BOX 的公司應用在生產環境的工具,就是自動的把這個配置應用到他們的叢集。這是一個開源的工具。

Q7: 申明式要做很多比對工作,會不會影響 Kubernetes 擴充套件性的規模?如果叢集的Pod 數量以及資源數量比較多,會對它的效能會造成很大的影響,有想過達到最上馬的最大規模數嗎?因為申明式需要做很多的比對,而且要不停的做比對,會不會對造成很大的影響,就像剛才說的 Controller 掛了,再重新起來,還要把這個配置做比對,再發現目標的 Design 的數目和當前數目的差異,再去做調整。

A:我覺得,如果跑非常非常多的 Pod,有可能會成為一個瓶頸,但目前 Kubernetes Native 的原生 API 都是這樣做的。你想提的問題是你們寫一個自定義的 API,比如說一個 PPR,一個 CRD,要寫一個自定義的 Controller,會擔心不斷的比對,會影響它的效能,對嗎?

Q8:或者說整個 Kubernetes 能支援的 Pod 的最大數量,或者說整個叢集最大的規模數目,會不會在這個點會成為瓶頸?

A:目前 Kubernetes 是保證支援 5000 個 Node,具體能跑多少個 Pod 不是很確定,但目前不會成為瓶頸問題。它做了一個小的優化,每個 Controller 都有一個 Cache,會不斷的變化,定期 Sync,不會因為死了就錯過了一個使用者的請求。

內容來源:2017 年 10 月 15 日,美國谷歌軟體工程師於夢琦在 “首屆Kubernetes 中國使用者大會” 進行的《Kubernetes中應用的管理與實踐》為主題的演講分享。「K8sMeetup 中國社群」經演講者審閱授權釋出。