從零開始入門 K8s | 應用編排與管理

1、需求來源

背景問題

首先來看一下背景問題。以下圖所示:若是咱們直接管理集羣中全部的 Pod,應用 A、B、C 的 Pod,實際上是散亂地分佈在集羣中。node

file

如今有如下的問題:nginx

  • 首先,如何保證集羣內可用 Pod 的數量?也就是說咱們應用 A 四個 Pod 若是出現了一些宿主機故障,或者一些網絡問題,如何能保證它可用的數量?
  • 如何爲全部 Pod 更新鏡像版本?咱們是否要某一個 Pod 去重建新版本的 Pod?
  • 而後在更新過程當中,如何保證服務的可用性?
  • 以及更新過程當中,若是發現了問題,如何快速回滾到上一個版本?

Deployment:管理部署發佈的控制器

這裏就引入了咱們今天的主題:Deployment 管理部署發佈的控制器。api

file

能夠看到咱們經過 Deployment 將應用 A、B、C 分別規劃到不一樣的 Deployment 中,每一個 Deployment 實際上是管理的一組相同的應用 Pod,這組 Pod 咱們認爲它是相同的一個副本,那麼 Deployment 能幫咱們作什麼事情呢?微信

  1. 首先,Deployment 定義了一種 Pod 指望數量,好比說應用 A,咱們指望 Pod 數量是四個,那麼這樣的話,controller 就會持續維持 Pod 數量爲指望的數量。當咱們與 Pod 出現了網絡問題或者宿主機問題的話,controller 能幫咱們恢復,也就是新擴出來對應的 Pod,來保證可用的 Pod 數量與指望數量一致;網絡

  2. 配置 Pod 發佈方式,也就是說 controller 會按照用戶給定的策略來更新 Pod,並且更新過程當中,也能夠設定不可用 Pod 數量在多少範圍內;架構

  3. 若是更新過程當中發生問題的話,即所謂「一鍵」回滾,也就是說你經過一條命令或者一行修改可以將 Deployment 下面全部 Pod 更新爲某一箇舊版本 。app

2、用例解讀

Deployment 語法

下面咱們用一個簡單的用例來解讀一下如何操做 Deployment。less

file

上圖能夠看到一個最簡單的 Deployment 的 yaml 文件。dom

「apiVersion:apps/v1」,也就是說 Deployment 當前所屬的組是 apps,版本是 v1。「metadata」是咱們看到的 Deployment 元信息,也就是往期回顧中的 Labels、Selector、Pod.image,這些都是在往期中提到的知識點微服務

Deployment 做爲一個 K8s 資源,它有本身的 metadata 元信息,這裏咱們定義的 Deployment.name 是 nginx.Deployment。Deployment.spec 中首先要有一個核心的字段,即 replicas,這裏定義指望的 Pod 數量爲三個;selector 實際上是 Pod 選擇器,那麼全部擴容出來的 Pod,它的 Labels 必須匹配 selector 層上的 image.labels,也就是 app.nginx。

就如上面的 Pod 模板 template 中所述,這個 template 它其實包含了兩部份內容:

  • 一部分是咱們指望 Pod 的 metadata,其中包含了 labels,即跟 selector.matchLabels 相匹配的一個 Labels;

  • 第二部分是 template 包含的一個 Pod.spec。這裏 Pod.spec 實際上是 Deployment 最終建立出來 Pod 的時候,它所用的 Pod.spec,這裏定義了一個 container.nginx,它的鏡像版本是 nginx:1.7.9。

下面是遇到的新知識點:

  • 第一個是 replicas,就是 Deployment 中指望的或者終態數量;
  • 第二個是 template,也就是 Pod 相關的一個模板。

查看 Deployment 狀態

當咱們建立出一個 Deployment 的時候,能夠經過 kubectl get deployment,看到 Deployment 整體的一個狀態。以下圖所示:file

上圖中能夠看到:

  • DESIRED:指望的 Pod 數量是 3 個;
  • CURRENT:當前實際 Pod 數量是 3 個;
  • UP-TO-DATE:實際上是到達最新的指望版本的 Pod 數量;
  • AVAILABLE:這個實際上是運行過程當中可用的 Pod 數量。後面會提到,這裏 AVAILABLE 並不簡單是可用的,也就是 Ready 狀態的,它其實包含了一些可用超過必定時間長度的 Pod;
  • AGE:deployment 建立的時長,如上圖 Deployment 就是已經建立了 80 分鐘。

查看 Pod

最後咱們能夠查看一下 Pod。以下圖所示:

file

上圖中有三個 Pod,Pod 名字格式咱們不難看到。

最前面一段:nginx-deployment,實際上是 Pod 所屬 Deployment.name;中間一段:template-hash,這裏三個 Pod 是同樣的,由於這三個 Pod 其實都是同一個 template 中建立出來的。

最後一段,是一個 random 的字符串,咱們經過 get.pod 能夠看到,Pod 的 ownerReferences 即 Pod 所屬的 controller 資源,並非 Deployment,而是一個 ReplicaSet。這個 ReplicaSet 的 name,實際上是 nginx-deployment 加上 pod.template-hash,後面會提到。全部的 Pod 都是 ReplicaSet 建立出來的,而 ReplicaSet 它對應的某一個具體的 Deployment.template 版本。

更新鏡像

接下來咱們能夠看一下,如何對一個給定的 Deployment 更新它全部Pod的鏡像版本呢?這裏咱們能夠執行一個 kubectl 命令:

kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1

  • 首先 kubectl 後面有一個 set image 固定寫法,這裏指的是設定鏡像;

  • 其次是一個 deployment.v1.apps,這裏也是一個固定寫法,寫的是咱們要操做的資源類型,deployment 是資源名、v1 是資源版本、apps 是資源組,這裏也能夠簡寫爲 deployment 或者 deployment.apps,好比說寫爲 deployment 的時候,默認將使用 apps 組 v1 版本。

  • 第三部分是要更新的 deployment 的 name,也就是咱們的 nginx-deployment;再日後的 nginx 其實指的是 template,也就是 Pod 中的 container.name;這裏咱們能夠注意到:一個 Pod 中,其實可能存在多個 container,而咱們指定想要更新的鏡像的 container.name,就是 nginx。

  • 最後,指定咱們這個容器指望更新的鏡像版本,這裏指的是 nginx: 1.9.1。以下圖所示:當執行完這條命令以後,能夠看到 deployment 中的 template.spec 已經更新爲 nginx: 1.9.1。

file

快速回滾

若是咱們在發佈過程當中遇到了問題,也支持快速回滾。經過 kubectl 執行的話,實際上是「kubectl rollout undo」這個命令,能夠回滾到 Deployment 上一版本;經過「rollout undo」加上「to-revision」來指定能夠回滾到某一個具體的版本。

file

DeploymeStatus

最後咱們來看一下 DeploymeStatus。每個資源都有它的 spec.Status。這裏能夠看一下,deploymentStatus 中描述的三個實際上是它的 conversion 狀態,也就是 Processing、Complete 以及 Failed。

file

以 Processing 爲例:Processing 指的是 Deployment 正在處於擴容和發佈中。好比說 Processing 狀態的 deployment,它全部的 replicas 及 Pod 副本所有達到最新版本,並且是 available,這樣的話,就能夠進入 complete 狀態。而 complete 狀態若是發生了一些擴縮容的話,也會進入 processing 這個處理工做狀態。

若是在處理過程當中遇到一些問題:好比說拉鏡像失敗了,或者說 readiness probe 檢查失敗了,就會進入 failed 狀態;若是在運行過程當中即 complete 狀態,中間運行時發生了一些 pod readiness probe 檢查失敗,這個時候 deployment 也會進入 failed 狀態。進入 failed 狀態以後,除非全部點 replicas 均變成 available,並且是 updated 最新版本,deployment 纔會從新進入 complete 狀態。

3、操做演示

Deployment 建立及狀態

下面咱們來進行操做演示:這裏鏈接一個阿里雲服務集羣。咱們能夠看到當前集羣已經有幾個可用的 node。

file

首先建立對應的 deployment。能夠看到 deployment 中的 desired、current、up-to-date 以及 available 已經都達到了可用的指望狀態。

file

Deployment 的結構

這裏看到 spec 中的 replicas 是三個,selector 以及 template labels中定義的標籤都是 app:nginx,spec 中的 image 是咱們指望的 nginx: 1.7.9;status 中的 available.replicas,readReplicas 以及 updatedReplicas 都是 3 個。

file

Pod 狀態

咱們能夠再選擇一個 Pod 看一下狀態:

能夠看到:Pod 中 ownerReferences 的功能是 ReplicaSet;pod.spec.container 裏的鏡像是 1.7.9。這個 Pod 已是 Running 狀態,並且它的 conditions.status 是「true」,表示它的服務已經可用了。

file

更新升級

當前只有最新版本的 replicaset,那麼如今嘗試對 deployment 作一次升級。

file

「kubectl set image」這個操做命令,後面接 「deployment」,加 deployment.name,最後指定容器名,以及咱們指望升級的鏡像版本。

file

接下來咱們看下 deployment 中的 template 中的 image 已經更新爲 1.9.1。

file

這個時候咱們再 get pod 看一下狀態。

file

三個 pod 已經升級爲新版本,pod 名字中的 pod-template-hash 也已更新。

file

能夠看到:舊版本 replicaset 的 spec 數量以及 pod 數量是都是 0,新版本的 pod 數量是 3 個。

假設又作了一次更新,這個時候 get.pod 其實能夠看到:當前的 pod 實際上是有兩個舊版本的處於 running,另外一箇舊版本是在刪除中;而兩個新版本的 pod,一個已經進入 running,一個還在 creating 中。

這時咱們可用的 pod 數量即非刪除狀態的 pod 數量,實際上是 4 個,已經超過了 replica 原先在 deployment 設置的數量 3 個。這個緣由是咱們在 deployment 中有 maxavailable 和 maxsugar 兩個操做,這兩個配置能夠限制咱們在發佈過程當中的一些策略。在後面架構設計中會講到這個問題。

file

歷史版本保留 revisionHistoryLimit

上圖看到,咱們當前最新版本的 replicaset 是 3 個 pod,另外還有兩個歷史版本的 replicaset,那麼會不會存在一種狀況:隨着 deployment 持續的更新,這個舊版本的 replicaset 會越積越多。其實 deployment 提供了一個機制來避免這個問題:在 deployment spec 中,有一個 revisionHistoryLimit,它的默認值爲 10,它其實保證了保留歷史版本的 replicaset 的數量,咱們嘗試把它改成 1。

file file

由上面第二張圖,能夠看到兩個 replicaset,也就是說,除了當前版本的 replicaset 以外,舊版本的 replicaset 其實只保留了一個。

回滾

最後再嘗試作一下回滾。首先再來看一下 replicaset,這時發現舊版本的 replicaset 數量從 0 個增到 2 個,而新版本的 replicaset 數量從 3 個削減爲 1 個,表示它已經開始在作回滾的操做。而後再觀察一下, 舊版本的數量已是 3 個,即已經回滾成功,而新版本的 pod 數量變爲 0 個。

file 咱們最後再 get pod 看一下:

file

這時,3 個 pod.template-hash 已經更新爲舊版本的 hash,但其實這 3 個 pod 都是從新建立出來的,而並不是咱們在前一版本中建立的 3 個 pod。換句話說,也就是咱們回滾的時候,實際上是建立了 3 箇舊版本的 pod,而並不是把先前的 3 個 pod 找回來。

4、架構設計

管理模式

file

咱們來看一下架構設計。首先簡單看一下管理模式:Deployment 只負責管理不一樣版本的 ReplicaSet,由 ReplicaSet 來管理具體的 Pod 副本數,每一個 ReplicaSet 對應 Deployment template 的一個版本。在上文的例子中能夠看到,每一次修改 template,都會生成一個新的 ReplicaSet,這個 ReplicaSet 底下的 Pod 其實都是相同的版本。

如上圖所示:Deployment 建立 ReplicaSet,而 ReplicaSet 建立 Pod。他們的 OwnerRef 其實都對應了其控制器的資源。

Deployment 控制器

咱們先簡單看一下控制器實現原理。

首先,咱們全部的控制器都是經過 Informer 中的 Event 作一些 Handler 和 Watch。這個地方 Deployment 控制器,實際上是關注 Deployment 和 ReplicaSet 中的 event,收到事件後會加入到隊列中。而 Deployment controller 從隊列中取出來以後,它的邏輯會判斷 Check Paused,這個 Paused 實際上是 Deployment 是否須要新的發佈,若是 Paused 設置爲 true 的話,就表示這個 Deployment 只會作一個數量上的維持,不會作新的發佈。

file

如上圖,能夠看到若是 Check paused 爲 Yes 也就是 true 的話,那麼只會作 Sync replicas。也就是說把 replicas sync 同步到對應的 ReplicaSet 中,最後再 Update Deployment status,那麼 controller 這一次的 ReplicaSet 就結束了。

那麼若是 paused 爲 false 的話,它就會作 Rollout,也就是經過 Create 或者是 Rolling 的方式來作更新,更新的方式其實也是經過 Create/Update/Delete 這種 ReplicaSet 來作實現的。

ReplicaSet 控制器

file

當 Deployment 分配 ReplicaSet 以後,ReplicaSet 控制器自己也是從 Informer 中 watch 一些事件,這些事件包含了 ReplicaSet 和 Pod 的事件。從隊列中取出以後,ReplicaSet controller 的邏輯很簡單,就只管理副本數。也就是說若是 controller 發現 replicas 比 Pod 數量大的話,就會擴容,而若是發現實際數量超過時望數量的話,就會刪除 Pod。

上面 Deployment 控制器的圖中能夠看到,Deployment 控制器其實作了更復雜的事情,包含了版本管理,而它把每個版本下的數量維持工做交給 ReplicaSet 來作。

擴/縮容模擬

下面來看一些操做模擬,好比說擴容模擬。這裏有一個 Deployment,它的副本數是 2,對應的 ReplicaSet 有 Pod1 和 Pod2。這時若是咱們修改 Deployment replicas, controller 就會把 replicas 同步到當前版本的 ReplicaSet 中,這個 ReplicaSet 發現當前有 2 個 Pod,不知足當前指望 3 個,就會建立一個新的 Pod3。

file

發佈模擬

咱們再模擬一下發布,發佈的狀況會稍微複雜一點。這裏能夠看到 Deployment 當前初始的 template,好比說 template1 這個版本。template1 這個 ReplicaSet 對應的版本下有三個 Pod:Pod1,Pod2,Pod3。

這時修改 template 中一個容器的 image, Deployment controller 就會新建一個對應 template2 的 ReplicaSet。建立出來以後 ReplicaSet 會逐漸修改兩個 ReplicaSet 的數量,好比它會逐漸增長 ReplicaSet2 中 replicas 的指望數量,而逐漸減小 ReplicaSet1 中的 Pod 數量。

那麼最終達到的效果是:新版本的 Pod 爲 Pod四、Pod5 和 Pod6,舊版本的 Pod 已經被刪除了,這裏就完成了一次發佈。

file

回滾模擬

來看一下回滾模擬,根據上面的發佈模擬能夠知道 Pod四、Pod五、Pod6 已經發布完成。這時發現當前的業務版本是有問題的,若是作回滾的話,不論是經過 rollout 命令仍是經過回滾修改 template,它其實都是把 template 回滾爲舊版本的 template1。

這個時候 Deployment 會從新修改 ReplicaSet1 中 Pod 的指望數量,把指望數量修改成 3 個,且會逐漸減小新版本也就是 ReplicaSet2 中的 replica 數量,最終的效果就是把 Pod 從舊版本從新建立出來。

file

發佈模擬的圖中能夠看到,其實初始版本中 Pod一、Pod二、Pod3 是舊版本,而回滾以後實際上是 Pod七、Pod八、Pod9。就是說它的回滾並非把以前的 Pod 從新找出來,而是說從新建立出符合舊版本 template 的 Pod。

spec 字段解析

最後再來簡單看一些 Deployment 中的字段解析。首先看一下 Deployment 中其餘的 spec 字段:

  • MinReadySeconds:Deployment 會根據 Pod ready 來看 Pod 是否可用,可是若是咱們設置了 MinReadySeconds 以後,好比設置爲 30 秒,那 Deployment 就必定會等到 Pod ready 超過 30 秒以後才認爲 Pod 是 available 的。Pod available 的前提條件是 Pod ready,可是 ready 的 Pod 不必定是 available 的,它必定要超過 MinReadySeconds 以後,纔會判斷爲 available;

  • revisionHistoryLimit:保留歷史 revision,即保留歷史 ReplicaSet 的數量,默認值爲 10 個。這裏能夠設置爲一個或兩個,若是回滾可能性比較大的話,能夠設置數量超過 10;

  • paused:paused 是標識,Deployment 只作數量維持,不作新的發佈,這裏在 Debug 場景可能會用到;

  • progressDeadlineSeconds:前面提到當 Deployment 處於擴容或者發佈狀態時,它的 condition 會處於一個 processing 的狀態,processing 能夠設置一個超時時間。若是超過超時時間還處於 processing,那麼 controller 將認爲這個 Pod 會進入 failed 的狀態。

file

升級策略字段解析

最後來看一下升級策略字段解析。

Deployment 在 RollingUpdate 中主要提供了兩個策略,一個是 MaxUnavailable,另外一個是 MaxSurge。這兩個字段解析的意思,能夠看下圖中詳細的 comment,或者簡單解釋一下:

  • MaxUnavailable:滾動過程當中最多有多少個 Pod 不可用;
  • MaxSurge:滾動過程當中最多存在多少個 Pod 超過預期 replicas 數量。

上文提到,ReplicaSet 爲 3 的 Deployment 在發佈的時候可能存在一種狀況:新版本的 ReplicaSet 和舊版本的 ReplicaSet 均可能有兩個 replicas,加在一塊兒就是 4 個,超過了咱們指望的數量三個。這是由於咱們默認的 MaxUnavailable 和 MaxSurge 都是 25%,默認 Deployment 在發佈的過程當中,可能有 25% 的 replica 是不可用的,也可能超過 replica 數量 25% 是可用的,最高能夠達到 125% 的 replica 數量。

這裏其實能夠根據用戶實際場景來作設置。好比當用戶的資源足夠,且更注重發佈過程當中的可用性,可設置 MaxUnavailable 較小、MaxSurge 較大。但若是用戶的資源比較緊張,能夠設置 MaxSurge 較小,甚至設置爲 0,這裏要注意的是 MaxSurge 和 MaxUnavailable 不能同時爲 0。

理由不難理解,當 MaxSurge 爲 0 的時候,必需要刪除 Pod,才能擴容 Pod;若是不刪除 Pod 是不能新擴 Pod 的,由於新擴出來的話,總共的 Pod 數量就會超過時望數量。而二者同時爲 0 的話,MaxSurge 保證不能新擴 Pod,而 MaxUnavailable 不能保證 ReplicaSet 中有 Pod 是 available 的,這樣就會產生問題。因此說這兩個值不能同時爲 0。用戶能夠根據本身的實際場景來設置對應的、合適的值。

file

本文總結

這裏爲你們簡單總結一下本文的主要內容:

  • Deployment 是 Kubernetes 中常見的一種 Workload,支持部署管理多版本的 Pod;
  • Deployment 管理多版本的方式,是針對每一個版本的 template 建立一個 ReplicaSet,由 ReplicaSet 維護必定數量的 Pod 副本,而 Deployment 只須要關心不一樣版本的 ReplicaSet 裏要指定多少數量的 Pod;
  • 所以,Deployment 發佈部署的根本原理,就是 Deployment 調整不一樣版本 ReplicaSet 裏的終態副本數,以此來達到多版本 Pod 的升級和回滾。

「 阿里巴巴雲原生微信公衆號(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的技術公衆號。」

相關文章
相關標籤/搜索