Kubernetes 系列(五):deployment


k8s的控制器類型

  • 控制器又被稱為工作負載,pod通過控制器實現應用的運維,比如伸縮、升級等
  • Kubernetes中內建了很多controller(控制器),這些相當於一個狀態機,用來控制Pod的具體狀態和行為

1、deployment:適合無狀態的服務部署

2、StatefullSet:適合有狀態的服務部署

3、DaemonSet:一次部署,所有的node節點都會部署,例如一些典型的應用場景:

  • 運行集群存儲 daemon,例如在每個Node上運行 glusterd、ceph
  • 在每個Node上運行日志收集 daemon,例如 fluentd、 logstash
  • 在每個Node上運行監控 daemon,例如 Prometheus Node Exporter

4、Job:一次性的執行任務

5、Cronjob:周期性的執行任務

 

Deployment控制器

Deployment實現了Pod 的“水平擴展 / 收縮”(horizontal scaling out/in),適合部署無狀態的應用服務,用來管理pod和replicaset,具有上線部署、副本設定、滾動更新、回滾等功能,還可提供聲明式更新,例如只更新一個新的Image。

Deployment如何控制pod

這些控制器被統一放在 kubernetes/pkg/controller 目錄下,因為它們都遵循 Kubernetes 項目中的一個通用編排模式,即:控制循環(control loop)。

比如,現在有一種待編排的對象 X,它有一個對應的控制器。

那么,我就可以用一段 Go 語言風格的偽代碼,為你描述這個控制循環:

for {
  實際狀態 := 獲取集群中對象X的實際狀態(Actual State)
  期望狀態 := 獲取集群中對象X的期望狀態(Desired State)
  if 實際狀態 == 期望狀態{
    什么都不做
  } else {
    執行編排動作,將實際狀態調整為期望狀態
  }
}

在具體實現中,實際狀態往往來自於 Kubernetes 集群本身。比如,kubelet 通過心跳匯報的容器狀態和節點狀態,或者監控系統中保存的應用監控數據,或者控制器主動收集的它自己感興趣的信息,這些都是常見的實際狀態的來源。

而期望狀態,一般來自於用戶提交的 YAML 文件。比如,Deployment 對象中 Replicas 字段的值。很明顯,這些信息往往都保存在 Etcd 中。

接下來,以 Deployment 為例,我和你簡單描述一下它對控制器模型的實現:

  • Deployment 控制器從 Etcd 中獲取到所有攜帶了“app: nginx”標簽的 Pod,然后統計它們的數量,這就是實際狀態;
  • Deployment 對象的 Replicas 字段的值就是期望狀態;Deployment 控制器將兩個狀態做比較,然后根據比較結果,確定是創建 Pod,還是刪除已有的 Pod。
  • 可以看到,一個 Kubernetes 對象的主要編排邏輯,實際上是在第三步的“對比”階段完成的。這個操作,通常被叫作調諧(Reconcile)。這個調諧的過程,則被稱作“Reconcile Loop”(調諧循環)或者“Sync Loop”(同步循環)。

Deployment依賴ReplicaSet控制pod

當你更新了yaml模板中關於容器的相關配置(比如,修改了容器的鏡像),那么 Deployment 就需要遵循一種叫作“滾動更新”(rolling update)的方式,來升級現有的容器。

而這個能力的實現,依賴的是 Kubernetes 項目中的一個非常重要的概念(API 對象):ReplicaSet。

在你通過yaml創建(API 對象)ReplicaSet的 Metadata 里,都有一個字段叫作 ownerReference,用於保存當前這個 API 對象的擁有者(Owner)的信息。

這個ownerReference的go 結構體定義如下:

 1 // OwnerReference contains enough information to let you identify an owning
 2 // object. Currently, an owning object must be in the same namespace, so there
 3 // is no namespace field.
 4 type OwnerReference struct {
 5     // API version of the referent.
 6     APIVersion string `json:"apiVersion" protobuf:"bytes,5,opt,name=apiVersion"` 7 // Kind of the referent. 8 // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds 9 Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"` 10 // Name of the referent. 11 // More info: http://kubernetes.io/docs/user-guide/identifiers#names 12 Name string `json:"name" protobuf:"bytes,3,opt,name=name"` 13 // UID of the referent. 14 // More info: http://kubernetes.io/docs/user-guide/identifiers#uids 15 UID types.UID `json:"uid" protobuf:"bytes,4,opt,name=uid,casttype=k8s.io/apimachinery/pkg/types.UID"` 16 // If true, this reference points to the managing controller. 17 // +optional 18 Controller *bool `json:"controller,omitempty" protobuf:"varint,6,opt,name=controller"` 19 // If true, AND if the owner has the "foregroundDeletion" finalizer, then 20 // the owner cannot be deleted from the key-value store until this 21 // reference is removed. 22 // Defaults to false. 23 // To set this field, a user needs "delete" permission of the owner, 24 // otherwise 422 (Unprocessable Entity) will be returned. 25 // +optional 26 BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"` 27 }

如果有如下yaml文件:

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.10
        ports:
        - containerPort: 80
ysml

我們可以用一張圖把它描述出來:通過這張圖,我們就很清楚地看到,一個定義了 replicas=3 的 Deployment,與它的 ReplicaSet,以及 Pod 的關系,實際上是一種“層層控制”的關系。

Deployment 控制器實際操縱的,是 ReplicaSet 對象,而不是 Pod 對象。

 

 

 

 

定義Deployment來創建Pod和ReplicaSet

創建一個yaml文件:nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

創建deployment,–-record 參數。它的作用,是記錄下你每次操作所執行的命令,以方便后面查看。

kubectl create -f nginx-deployment.yaml --record

通過標簽查找Pod

kubectl get pod -l app=nginx

查看deployment,可以實時查看pod的創建狀態

kubectl get deployments
在返回結果中,我們可以看到四個狀態字段,它們的含義如下所示。
DESIRED:用戶期望的 Pod 副本個數(spec.replicas 的值);
CURRENT:當前處於 Running 狀態的 Pod 的個數;
UP-TO-DATE:當前處於最新版本的 Pod 的個數,所謂最新版本指的是 Pod 的 Spec 部分與 Deployment 里 Pod 模板里定義的完全一致;
AVAILABLE:當前已經可用的 Pod 的個數,即:既是 Running 狀態,又是最新版本,並且已經處於 Ready(健康檢查正確)狀態的 Pod 的個數。
可以看到,只有這個 AVAILABLE 字段,描述的才是用戶所期望的最終狀態。

查看Deployment 所控制的 ReplicaSet

kubectl get rs
注意:Replica Set的名字總是<Deployment的名字>-<pod template的hash值>。

describe檢查

kubectl describe deployment/nginx-deployment

實時查看 Deployment 對象的狀態變化

kubectl rollout status deployment/nginx-deployment

創建一個Service對象,暴露Deployment端口,給外網訪問

kubectl expose deployment/nginx --port=88 --type=NodePort --target-port=80 --name=example-nginx-service
# 備注
--port=88:Service服務的端口
--target-port=80: 容器暴露的端口
--type=NodePort:會隨機開放一個宿主機端口(端口范圍在apiserver中定義)

查看/描述 服務

kubectl get svc
kubectl describe svc example-nginx-service
快速訪問測試: curl ip:port 

查看Pod日志

kubectl logs pods/podname
注意:這個podname來自於通過標簽查找的pod的name

擴容和縮容

kubectl scale deployment nginx-deployment --replicas=4

 

 

更新Deployment

kubectl edit deployment/nginx-deployment

注意:kubectl edit 指令,會幫你直接打開 nginx-deployment 的 API 對象。然后,你就可以修改這里的 Pod 模板部分了

kubectl edit 指令編輯完成后,保存退出,Kubernetes 就會立刻觸發“滾動更新”的過程。你還可以通過 kubectl rollout status 指令查看 nginx-deployment 的狀態變化:

kubectl rollout status deployment/nginx-deployment

繼續查看 Deployment 的 Events,看到這個“滾動更新”的流程:

kubectl describe deployment nginx-deployment

整個更新是滾動更新的:將一個集群中正在運行的多個 Pod 版本,交替地逐一升級的過程,就是“滾動更新”。

Deployment Controller 會使用這個修改后的 Pod 模板,創建一個新的 ReplicaSet,這個新的 ReplicaSet 的初始 Pod 副本數是:0。

更新過程交替進行,新 ReplicaSet 管理的 Pod 副本數,從 0 個變成 1 個,再變成 2 個,最后變成 3 個。而舊的 ReplicaSet 管理的 Pod 副本數則從 3 個變成 2 個,再變成 1 個,最后變成 0 個。這樣,就完成了這一組 Pod 的版本升級過程。

最后我們查看ReplicaSet

kubectl get rs

就會發現舊 ReplicaSet所管理的pod已經被“水平收縮”成了 0 個副本。

更新操作還有兩種方式:一種是,手動修改yaml文件,然后執行kubectl apply -f  nginx-deployment.yaml,另一種是通過 kubectl set image 命令。

另外補充:我們對 Deployment 進行的每一次更新操作,都會生成一個新的 ReplicaSet 對象,是不是有些多余,甚至浪費資源呢?

Kubernetes 項目還提供了一個指令,使得我們對 Deployment 的多次更新操作,最后 只生成一個 ReplicaSet。具體的做法是,在更新 Deployment 前,你要先執行一條 kubectl rollout pause 指令。

具體流程:

1.在更新這個Deployment時,執行kubectl rollout pause deployment/nginx-deployment

2.更新Deployment,使用kubectl edit 或者 kubectl set image 指令

3.等Deployment 上的更新完成以后,執行kubectl rollout resume deployment/nginx-deployment

 

回滾

回滾到上一個版本

kubectl rollout undo deployment/nginx-deployment

Deployment 的控制器,會讓這個舊 ReplicaSe再次“擴展”成 3 個 Pod,而讓新的 ReplicaSet重新“收縮”到 0 個 Pod。

回滾到更早之前的版本

查看此deployment每次變更對應的版本

kubectl rollout history deployment/nginx-deployment

查看對應版本的 Deployment 的 API 對象的細節

kubectl rollout history deployment/nginx-deployment --revision=2

確定以后我們就可以回滾到這個版本上

kubectl rollout undo deployment/nginx-deployment --to-revision=2

 

 

編寫Deployment Spec指南

在所有的Kubernetes配置中,Deployment也需要apiVersion,kind和metadata這些配置項。配置文件的通用使用說明查看部署應用,配置容器,和使用kubeclt管理資源文檔。

Deployment也需要 .spec section.

Pod Template

.spec.template 是 .spec中唯一要求的字段。

.spec.template 是 pod template. 它跟 Pod有一模一樣的schema,除了它是嵌套的並且不需要apiVersion 和 kind字段。

另外為了划分Pod的范圍,Deployment中的pod template必須指定適當的label(不要跟其他controller重復了)和適當的重啟策略。

.spec.template.spec.restartPolicy 可以設置為 Always , 如果不指定的話這就是默認配置。

Replicas

.spec.replicas 是可以選字段,指定期望的pod數量,默認是1。

Selector

.spec.selector是可選字段,用來指定 label selector ,圈定Deployment管理的pod范圍。

如果被指定, .spec.selector 必須匹配 .spec.template.metadata.labels,否則它將被API拒絕。如果 .spec.selector 沒有被指定, .spec.selector.matchLabels 默認是 .spec.template.metadata.labels。

在Pod的template跟.spec.template不同或者數量超過了.spec.replicas規定的數量的情況下,Deployment會殺掉label跟selector不同的Pod。

注意: 你不應該再創建其他label跟這個selector匹配的pod,或者通過其他Deployment,或者通過其他Controller,例如ReplicaSet和ReplicationController。否則該Deployment會被把它們當成都是自己創建的。Kubernetes不會阻止你這么做。

如果你有多個controller使用了重復的selector,controller們就會互相沖突並導致不正確的行為。

策略

.spec.strategy 指定新的Pod替換舊的Pod的策略。 .spec.strategy.type 可以是”Recreate”或者是 “RollingUpdate”。”RollingUpdate”是默認值。

  • Recreate Deployment

.spec.strategy.type==Recreate時,在創建出新的Pod之前會先殺掉所有已存在的Pod。

  • Rolling Update Deployment

.spec.strategy.type==RollingUpdate時,Deployment使用rolling update 的方式更新Pod 。你可以指定maxUnavailable 和maxSurge 來控制 rolling update 進程。

  • Max Unavailable

.spec.strategy.rollingUpdate.maxUnavailable 是可選配置項,用來指定在升級過程中不可用Pod的最大數量。該值可以是一個絕對值(例如5),也可以是期望Pod數量的百分比(例如10%)。通過計算百分比的絕對值向下取整。如果.spec.strategy.rollingUpdate.maxSurge 為0時,這個值不可以為0。默認值是1。

例如,該值設置成30%,啟動rolling update后舊的ReplicatSet將會立即縮容到期望的Pod數量的70%。新的Pod ready后,隨着新的ReplicaSet的擴容,舊的ReplicaSet會進一步縮容,確保在升級的所有時刻可以用的Pod數量至少是期望Pod數量的70%。

  • Max Surge

.spec.strategy.rollingUpdate.maxSurge 是可選配置項,用來指定可以超過期望的Pod數量的最大個數。該值可以是一個絕對值(例如5)或者是期望的Pod數量的百分比(例如10%)。當MaxUnavailable為0時該值不可以為0。通過百分比計算的絕對值向上取整。默認值是1。

例如,該值設置成30%,啟動rolling update后新的ReplicatSet將會立即擴容,新老Pod的總數不能超過期望的Pod數量的130%。舊的Pod被殺掉后,新的ReplicaSet將繼續擴容,舊的ReplicaSet會進一步縮容,確保在升級的所有時刻所有的Pod數量和不會超過期望Pod數量的130%。

  • Progress Deadline Seconds

.spec.progressDeadlineSeconds 是可選配置項,用來指定在系統報告Deployment的failed progressing ——表現為resource的狀態中type=Progressing、Status=False、 Reason=ProgressDeadlineExceeded前可以等待的Deployment進行的秒數。Deployment controller會繼續重試該Deployment。未來,在實現了自動回滾后, deployment controller在觀察到這種狀態時就會自動回滾。

如果設置該參數,該值必須大於 .spec.minReadySeconds。

  • Min Ready Seconds

.spec.minReadySeconds是一個可選配置項,用來指定沒有任何容器crash的Pod並被認為是可用狀態的最小秒數。默認是0(Pod在ready后就會被認為是可用狀態)。進一步了解什么什么后Pod會被認為是ready狀態,參閱 Container Probes。

  • Rollback To

.spec.rollbackTo 是一個可以選配置項,用來配置Deployment回退的配置。設置該參數將觸發回退操作,每次回退完成后,該值就會被清除。

  • Revision

.spec.rollbackTo.revision是一個可選配置項,用來指定回退到的revision。默認是0,意味着回退到歷史中最老的revision。

  • Revision History Limit

Deployment revision history存儲在它控制的ReplicaSets中。

.spec.revisionHistoryLimit 是一個可選配置項,用來指定可以保留的舊的ReplicaSet數量。該理想值取決於心Deployment的頻率和穩定性。如果該值沒有設置的話,默認所有舊的Replicaset都會被保留,將資源存儲在etcd中,是用kubectl get rs查看輸出。每個Deployment的該配置都保存在ReplicaSet中,然而,一旦你刪除的舊的RepelicaSet,你的Deployment就無法再回退到那個revison了。

如果你將該值設置為0,所有具有0個replica的ReplicaSet都會被刪除。在這種情況下,新的Deployment rollout無法撤銷,因為revision history都被清理掉了。

  • Paused

.spec.paused是可以可選配置項,boolean值。用來指定暫停和恢復Deployment。Paused和沒有paused的Deployment之間的唯一區別就是,所有對paused deployment中的PodTemplateSpec的修改都不會觸發新的rollout。Deployment被創建之后默認是非paused。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM