Kubernetes之Pod控制器


 

Kubernetes中內建了很多controller(控制器),這些相當於⼀個狀態機,⽤來控制Pod的具體狀態和⾏為。Pod控制器有很多種類型,但是目前kubernetes中常用的控制器有: ReplicationController(RC)、ReplicaSet(RS)、Deployment、DaemouSet(DS)、StatefulSet、Job、CronJob。下面說說這幾個控制器的使用:

Replication Controller(RC):是Kubernetes系統中的核心概念,用於定義Pod副本的數量。在Master內,RC進程通過RC的定義來完成Pod的創建、監控、啟停等操作。根據RC定義,Kubernetes能夠確保在任意時刻都能運行用於指定的Pod"副本"(Replica)數量。隨着kubernetes的迭代更新,RC即將被廢棄,逐漸被ReplicaSet替代

ReplicaSet(RS:它的核心作用是代用戶創建指定數量的Pod副本,並確定Pod副本一直處於滿足用戶期望數量的狀態,多退少補,同時支持擴縮容機制。主要有三個組件:用戶期望的Pod副本數量標簽選擇器,選擇屬於自己管理和控制的Pod當前Pod數量不滿足用戶期望數量時,根據資源模板進行新建

Deployment:工作在ReplicaSet之上,用於管理無狀態應用,除了ReplicaSet的機制,還增加了滾動更新和回滾功能,提供聲明式配置。

DaemonSet:用於確保集群中的每一個節點只運行特定的pod副本,通常用於實現系統級后台任務。比如ELK服務。要求:服務是無狀態的;服務必須是守護進程

Job:只要完成就立即退出,不需要重啟或重建。

Cronjob:周期性任務控制,不需要持續后台運行。

StatefulSet管理有狀態應用,如mysql,redis等。

kubelet對非主進程崩潰類的容器錯誤無法感知,需要借助存活性探測livenessProbe。如下圖:

Pod控制器通常包含三個組成部分標簽選擇器期望的副本數Pod模板

Pod 模板資源 
spec.template 不需要apiVersion和kind字段,和自主式Pod對象支持的字段幾乎完全一樣。配置示例如下:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: rs-example
spec:
  replicas: 2
  selector:
    matchLabels:
      app: rs-demo
  template:
    metadata:
      labels:
        app: rs-demo
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80

一、 Replication Controller (RC) 
Replication Controller 簡稱RC,它能確保任何時候Kubernetes集群中有指定數量的pod副本(replicas)在運行, 如果少於指定數量的pod副本(replicas),Replication Controller會啟動新的Container,反之會殺死多余的以保證數量不變。Replication Controller使用預先定義的pod模板創建pods,一旦創建成功,pod 模板和創建的pods沒有任何關聯,可以修改pod 模板而不會對已創建pods有任何影響,也可以直接更新通過Replication Controller創建的pods。對於利用pod 模板創建的pods,Replication Controller根據label selector來關聯,通過修改pods的label可以刪除對應的pods。

最初Replication Controller 是用於復制和在異常時重新調度節點的唯一kubernetes組件,后來逐漸被replicaSet代替了。現在基本上很少見到Replication Controller,它即將被廢棄。但是有的kubernates容器環境還是有可能會有RC,所以還是有必要知道它的用法。

根據Replication Controller的定義,Kubernetes能夠確保在任意時刻都能運行用於指定的Pod"副本"(Replica)數量。如果有過多的Pod副本在運行,系統就會停掉一些Pod;如果運行的Pod副本數量太少,系統就會再啟動一些Pod,總之,通過RC的定義,Kubernetes總是保證集群中運行着用戶期望的副本數量。

同時,Kubernetes會對全部運行的Pod進行監控和管理,如果有需要(例如某個Pod停止運行),就會將Pod重啟命令提交給Node上的某個程序來完成(如Kubelet或Docker)。通過對Replication Controller的使用,Kubernetes實現了應用集群的高可用性。簡單來說,RC可以保證在任意時間運行Pod的副本數量,能夠保證Pod總是可用的。如果實際Pod數量比指定的多那就結束掉多余的,如果實際數量比指定的少就新啟動一些Pod,當Pod失敗、被刪除或者掛掉后,RC都會去自動創建新的Pod來保證副本數量,所以即使只有一個Pod,也應該使用RC來管理我們的Pod。

應用托管到Kubernetes之后,需要保證應用能夠持續的運行,Replication Controller就是這個保證的key,主要的功能如下:
--  確保pod數量:它會確保Kubernetes中有指定數量的Pod在運行。如果少於指定數量的pod,Replication Controller會創建新的,反之則會刪除掉多余的以保證Pod數量不變。
--  確保pod健康:當pod不健康,運行出錯或者無法提供服務時,Replication Controller也會殺死不健康的pod,重新創建新的。
--  彈性伸縮 :在業務高峰或者低峰期的時候,可以通過Replication Controller動態的調整pod的數量來提高資源的利用率。同時,配置相應的監控功能(Hroizontal Pod Autoscaler),會定時自動從監控平台獲取Replication Controller關聯pod的整體資源使用情況,做到自動伸縮。
--  滾動升級:滾動升級為一種平滑的升級方式,通過逐步替換的策略,保證整體系統的穩定,在初始化升級的時候就可以及時發現和解決問題,避免問題不斷擴大。

對Replication Controller的定義使用Yaml或Json格式的配置文件來完成。如下:master服務上創建文件php-controller.yaml,為了避免同一個rc定義的pod在同一個node上生成多個pod時,端口沖突,文件中不指定hostPort。replicas指定pod的數量。

apiVersion: v1  
kind: ReplicationController  
metadata:  
   name: php-controller  
   labels:   
     name: php-controller  
spec:  
  replicas: 2  
  selector:  
     name: php-test-pod  
  template:   
    metadata:  
     labels:  
       name: php-test-pod  
    spec:  
      containers:  
      - name: php-test  
        image: 172.16.60.131:5000/php-base:1.0  
        env:  
        - name: ENV_TEST_1  
          value: env_test_1  
        - name: ENV_TEST_2  
          value: env_test_2  
        ports:  
        - containerPort: 80

執行操作

[root@localhost k8s]# kubectl create -f php-controller.yaml   
replicationcontroller "php-controller" created 

查詢
[root@localhost k8s]# kubectl get rc
[root@localhost k8s]# kubectl get rc php-controller
[root@localhost k8s]# kubectl describe rc php-controller 

[root@localhost k8s]# kubectl get pods
[root@localhost k8s]# kubectl get pods -o wide
NAME                   READY     STATUS    RESTARTS   AGE       NODE  
php-controller-8x5wp   1/1       Running   0          5m        172.16.60.131  
php-controller-ynzl7   1/1       Running   0          5m        172.16.60.130  

可見在131和130的2台node服務器上都創建了Pod。

更改副本數量
在文件中pod副本數量是通過replicas來控制的,kubernetes允許通過kubectl scal命令來動態控制Pod的數量。比如更改replicas的數量為3
[root@localhost k8s]# kubectl scale rc php-controller --replicas=3  
replicationcontroller "php-controller" scaled  

刪除
通過更改replicas=0,可以把該RC下的所有Pod都刪掉。另外kubeclt也提供了stop和delete命令來完成一次性刪除RC和RC控制的全部Pod。
需要注意的是:單純的刪除RC,並不會影響已創建好的Pod。
# kubectl delete rc rcName     #這樣刪除rc,但是pod不會收到影響
# kubectl delete -f rcfile     #這樣刪除rc的yml文件,也會會刪除rc,也會刪除rc下的所有pod

[root@localhost k8s]# kubectl delete -f php-controller.yaml    #刪除rc,也會刪除rc下的所有pod

滾動升級
可以對RC使用滾動升級,來發布新功能或修復BUG。但是注意:RC在rolling-update升級成功后不能直接回滾!!!(使用deployment可以回滾到上一個版本,需加上--revision參數,下面會說到)
當Pod中只有一個容器時,通過--image參數指定新的Tag,如果有多個容器或其他字段修改時,需要指定yaml文件。
[root@master01 deploy_yaml]# kubectl rolling-update php-controller --image=ikubernetes/myapp:v2

二、ReplicaSet (RS)
ReplicaSet是Replication Controller的替代者,確保Pod副本數在任一時刻都精確滿足期望值。用來確保容器應用的副本數始終保持在用戶定義的副本數,即如果有容器異常退出,會自動創建新的Pod來替代;而如果異常多出來的容器也會自動回收。雖然ReplicaSet可以獨立使用,但一般還是建議使用 Deployment 來自動管理ReplicaSet,這樣就無需擔心跟其他機制的不兼容問題(比如ReplicaSet不支持rolling-update但Deployment支持)。也就是說Replicaset通常不會直接創建,而是在創建最高層級的deployment資源時自動創建。

隨着Kubernetes的高速發展,官方已經推薦我們使用RS和Deployment來代替RC了,實際上RS和RC的功能基本一致,目前唯一的一個區別就是RC只支持基於等式的selector(env=dev或environment!=qa),但RS還支持基於集合的selector(version in (v1.0, v2.0)),這對復雜的運維管理就非常方便了。

ReplicaSet 主要功能
--  確保Pod數量:它會確保Kubernetes中有指定數量的Pod在運行,如果少於指定數量的Pod,RC就會創建新的,反之這會刪除多余的,保證Pod的副本數量不變。
--  確保Pod健康:當Pod不健康,比如運行出錯了,總之無法提供正常服務時,RC也會殺死不健康的Pod,重新創建新的。
--  彈性伸縮:在業務高峰或者低峰的時候,可以用過RC來動態的調整Pod數量來提供資源的利用率,當然我們也提到過如果使用HPA這種資源對象的話可以做到自動伸縮。
--  滾動升級:滾動升級是一種平滑的升級方式,通過逐步替換的策略,保證整體系統的穩定性。

kubectl命令行工具中關於RC的大部分命令同樣適用於RS資源對象。不過我們也很少會去單獨使用RS,它主要被Deployment這個更加高層的資源對象使用,除非用戶需要自定義升級功能或根本不需要升級Pod,在一般情況下,我們推薦使用Deployment而不直接使用Replica Set。

雖說不用直接創建ReplicaSet,為了學習,這里還是手動創建replicaset,如下:

[root@master ~]# vim rs-demo.yaml
apiVersion: apps/v1        #群組/版本
kind: ReplicaSet           #類型 注意大小寫
metadata:                  #元數據
  name: myapp
  namespace: default  
spec:   
  replicas: 2              #定義ReplicaSet副本數量
  selector:                #定義選擇器
    matchLabels:           #匹配 ,創建的模板的標簽需要包含此處的標簽,不然無法被創建
      app: myapp
      release: canary
  template:                #定義模板
    metadata:              #模板元數據
      name: myapp-pod      #此處名稱定義沒有用,被控制器創建的pod都會以控制器的名稱后加隨機字符串來命名
      labels:              #此處要包含replicaset處定義的標簽選擇器
        app: myapp
        release: canary
    spec:
      containers:                    #定義模板的容器
      - name: myapp-container
        image: ikubernetes/myapp:v1
        ports:                       #端口
        - name: http
          containerPort: 80
          
#運行后驗證
[root@master ~]# kubectl create -f rs-demo.yaml

#replicaset 簡寫rs
[root@master ~]# kubectl get rs 

#查看pod
[root@master ~]# kubectl get pods
#可以使用"kubectl describe pods <POD_NAME>"來查看詳細信息

修改Pod的副本數量:
[root@master ~]# kubectl edit rs myapp
  replicas: 5
[root@master ~]# kubectl get pods

修改Pod的鏡像版本:
[root@master ~]# kubectl edit rs myapp
      - image: ikubernetes/myapp:v2

此時查看原來存在的pod副本還是v1版本,只有重建后的pod才會是V2版本。
[root@master ~]# kubectl delete -f rs-demo.yaml && kube create -f rs-demo.yaml

根據上面的yml文件可以看出,ReplicaSet和Replication Controller的template部分是一樣的,但是selector處不一樣Replication Controller用selector , ReplicaSet用 selector.matchLables選擇器 ,這樣更簡單,更具有表達力!

spec.selector 支持matchLabels和matchExpressions兩種匹配機制
spec.minReadySeconds,Pod對象啟動后多長時間認為就緒,默認0s

ReplicaSet 與 Replication Controller 的區別
目前,二者的區別只是支持的selector標簽不同,后續肯定會加入更多功能。
ReplicaSet 標簽選擇器的能力更強。
Replication Controller只能指定標簽名:標簽值
Replicaset 可以指定env=pro,env=devel ,也可以指定只要包含env標簽就行,理解為env=*。

Replicaset 的matchlables 是精確匹配,說Replicaset支持更強表達能力的選擇器,是因為Replicaset還有matchExpressions選擇器。比如:

selector:
    matchExpressions:
        - key: app
           operator: In 
           values:
               - kubia

查看 (如下rs名稱為rs-example。 下面的replicasets可以寫為rs)
# kubectl get replicasets
# kubectl describe replicasets rs-example

缺少Pod副本
刪除某Pod后,replicaset會自動重建
刪除某Pod標簽后,replicaset會創建新的Pod(如下pod名稱為rs-example-b59gz)
# kubectl label pod rs-example-b59gz app= --overwrite                 # 修改已有標簽
若修改標簽后的Pod不隸屬於任何控制器,則會成為自主式Pod

多出Pod副本
自動刪除多余的Pod

改動Pod模板定義對已存在的活動對象無效,僅影響新建的Pod
此時分批次刪除舊有Pod或更改其標簽,可實現滾動升級的效果,更替期間新舊版本共存

擴縮容(下面的replicaset可以寫為rs)
# kubectl scale replicaset rs-example --replicas=5
# kubectl scale replicaset rs-example --current-replicas=1 --replicas=2         # 當目前副本數量為1時 擴展到2
# kubectl delete replicasets rs-example --cascade=false                               # 僅刪除控制器,不級聯刪除所屬Pod

或者直接修改yaml下Pod的副本數量進行擴縮容
[root@master ~]# vim rs-demo.yaml
.....
replicas: 3

[root@master ~]# kubectl apply -f rs-demo.yaml
[root@master ~]# kubectl get rs

###########   總結下關於RC/RS的一些特性和作用  #############
-  大部分情況下,可以通過定義一個RC/RS實現的Pod的創建和副本數量的控制
-  RC/RS中包含一個完整的Pod定義模塊(不包含apiversion和kind)
-  RC/RS是通過label selector機制來實現對Pod副本的控制的
-  通過改變RC/RS里面的Pod副本數量,可以實現Pod的擴縮容功能
-  通過改變RC/RS里面的Pod模板中鏡像版本,可以實現Pod的滾動升級功能(但是不支持一鍵回滾,需要用相同的方法去修改鏡像地址)

三、Deployment     
Deployment構建於ReplicaSet之上,支持事件和狀態查看、回滾、版本記錄、暫停和啟動升級;Deployment有多種自動更新方案:Recreate,先刪除再新建;RollingUpdate,滾動升級,逐步替換。Deployment為Pod和Replica Set(下一代Replication Controller)提供聲明式更新,它可以更加方便的管理Pod和Replica Set。只需要在 Deployment 中描述想要的目標狀態是什么,Deployment controller 就會幫您將 Pod 和ReplicaSet 的實際狀態改變到您的目標狀態。此外,也可以定義一個全新的 Deployment 來創建 ReplicaSet 或刪除已有Deployment 並創建一個新的來替換。

Deployment控制器典型的用例如下:
--  使用Deployment來創建ReplicaSet (即RS)。RS在后台創建pod。檢查啟動狀態,看它是成功還是失敗。
--  接着通過更新Deployment的PodTemplateSpec字段來聲明Pod的新狀態。這會創建一個新的RS,Deployment會按照控制的速率將pod從舊的RS移動到新的RS中。
--  如果當前狀態不穩定,回滾到之前的Deployment revision。每次回滾都會更新Deployment的revision。
--  擴容Deployment以滿足更高的負載。
--  暫停Deployment來應用PodTemplateSpec的多個修復,然后恢復上線。
--  根據Deployment 的狀態判斷上線是否hang住了。
--  清除舊的不必要的 ReplicaSet。

Deployment和RC同樣為Kubernetes的一個核心內容,主要職責同樣是為了保證pod的數量和健康,90%的功能與Replication Controller完全一樣,可以看做新一代的Replication Controller。但是,它又具備了Replication Controller之外的新特性:
--  RC全部功能:Deployment繼承了上面描述的RC全部功能。
--  事件和狀態查看:可以查看Deployment的升級詳細進度和狀態。
--  回滾:當升級pod鏡像或者相關參數的時候發現問題,可以使用回滾操作回滾到上一個穩定的版本或者指定的版本。
--  版本記錄: 每一次對Deployment的操作,都能保存下來,給予后續可能的回滾使用。
--  暫停和啟動:對於每一次升級,都能夠隨時暫停和啟動。
--  多種升級方案:Recreate:刪除所有已存在的pod,重新創建新的; RollingUpdate:滾動升級,逐步替換的策略,同時滾動升級時,支持更多的附加參數,例如設置最大不可用pod數量,最小升級間隔時間等等。

Kubernetes是一個高速發展的項目,在新的版本中,官方推薦使用 Replica Set 和 Deployment 來代替 RC。那么它們優勢在哪里呢?
--  RC只支持基於等式的selector(env=dev或environment!=qa),但RS還支持新的, 基於集合的selector(version in (v1.0, v2.0)或env notin (dev, qa)),這給運維管理帶來很大方便。
--  使用Deployment升級Pod,只需要定義Pod的最終狀態,k8s會為你執行必要的操作,雖然能夠使用命令 # kubectl rolling-update 完成升級,但它是在客戶端與服務端多次交互控制RC完成的,所以REST API中並沒有rolling-update的接口,這為定制自己的管理系統帶來了一些麻煩。
--  Deployment擁有更加靈活強大的升級、回滾功能。
目前,Replica Set與RC的區別只是支持的selector不同,后續肯定會加入更多功能。Deployment使用了Replica Set,是更高一層的概念。除非需要自定義升級功能或根本不需要升級Pod,一般情況下,我們推薦使用Deployment 而不直接使用 Replica Set

首先看一個官方的nginx-deployment.yaml的例子: 

apiVersion: v1
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.7.9
        ports:
        - containerPort: 80

Deployment會自動創建名稱為 DeploymentName-PodTemplateHashValue格式ReplicaSet 在所有的 Kubernetes 配置中,Deployment 也需要apiVersion,kind和metadata這些配置項。如下:

[root@k8s-master ~]# kubectl explain deployment
KIND:     Deployment
VERSION:  extensions/v1beta1

DESCRIPTION:
     DEPRECATED - This group version of Deployment is deprecated by
     apps/v1beta2/Deployment. See the release notes for more information.
     Deployment enables declarative updates for Pods and ReplicaSets.

FIELDS:
   apiVersion    <string>
     APIVersion defines the versioned schema of this representation of an
     object. Servers should convert recognized schemas to the latest internal
     value, and may reject unrecognized values. More info:
     https://git.k8s.io/community/contributors/devel/api-conventions.md#resources

   kind    <string>
     Kind is a string value representing the REST resource this object
     represents. Servers may infer this from the endpoint the client submits
     requests to. Cannot be updated. In CamelCase. More info:
     https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds

   metadata    <Object>
     Standard object metadata.

   spec    <Object>
     Specification of the desired behavior of the Deployment.

   status    <Object>
     Most recently observed status of the Deployment.

使用kubectl explain deployment.spec查看具體Deployment spec的配置選項,解析如下:

[root@k8s-master ~]# kubectl explain deployment.spec
KIND:     Deployment
VERSION:  extensions/v1beta1

RESOURCE: spec <Object>

DESCRIPTION:
     Specification of the desired behavior of the Deployment.

     DeploymentSpec is the specification of the desired behavior of the
     Deployment.

FIELDS:
   minReadySeconds    <integer>
     Minimum number of seconds for which a newly created pod should be ready
     without any of its container crashing, for it to be considered available.
     Defaults to 0 (pod will be considered available as soon as it is ready)

   paused    <boolean>
     Indicates that the deployment is paused and will not be processed by the
     deployment controller.

   progressDeadlineSeconds    <integer>
     The maximum time in seconds for a deployment to make progress before it is
     considered to be failed. The deployment controller will continue to process
     failed deployments and a condition with a ProgressDeadlineExceeded reason
     will be surfaced in the deployment status. Note that progress will not be
     estimated during the time a deployment is paused. This is not set by
     default.

   replicas    <integer>
     Number of desired pods. This is a pointer to distinguish between explicit
     zero and not specified. Defaults to 1.

   revisionHistoryLimit    <integer>
     The number of old ReplicaSets to retain to allow rollback. This is a
     pointer to distinguish between explicit zero and not specified.

   rollbackTo    <Object>
     DEPRECATED. The config this deployment is rolling back to. Will be cleared
     after rollback is done.

   selector    <Object>
     Label selector for pods. Existing ReplicaSets whose pods are selected by
     this will be the ones affected by this deployment.

   strategy    <Object>
     The deployment strategy to use to replace existing pods with new ones.

   template    <Object> -required-
     Template describes the pods that will be created.

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。

Pod Template(Pod模板)
  .spec.template 是 .spec中唯一要求的字段。

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

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

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

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

  Recreate: 重建式更新,就是刪一個建一個。類似於ReplicaSet的更新方式,即首先刪除現有的Pod對象,然后由控制器基於新模板重新創建新版本資源對象。

  rollingUpdate滾動更新,簡單定義 更新期間pod最多有幾個等。可以指定maxUnavailable 和 maxSurge 來控制 rolling update 進程。

  maxSurge.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%。

  maxUnavailable.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%。

需要注意:maxSurge和maxUnavailable的屬性值不可同時為0,否則Pod對象的副本數量在符合用戶期望的數量后無法做出合理變動以進行更新操作。

在配置時,用戶還可以使用Deployment控制器的spec.minReadySeconds屬性來控制應用升級的速度。新舊更替過程中,新創建的Pod對象一旦成功響應就緒探測即被認為是可用狀態,然后進行下一輪的替換。而spec.minReadySeconds能夠定義在新的Pod對象創建后至少需要等待多長的時間才能會被認為其就緒,在該段時間內,更新操作會被阻塞。

revisionHistoryLimit(歷史版本記錄)
  Deployment revision history存儲在它控制的ReplicaSets中。默認保存記錄10個  

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

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

需要注意:為了保存版本升級的歷史,需要再創建Deployment對象時,在命令中使用"--record"選項

rollbackTo    

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

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

progressDeadlineSeconds  

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

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

paused

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

1.  創建Deployment

[root@k8s-master ~]# vim deploy-demo.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
    name: myapp-deploy
    namespace: default
spec:
    replicas: 2
    selector:
        matchLabels:
            app: myapp
            release: canary
    template:
        metadata:
            labels: 
                app: myapp
                release: canary
        spec:
            containers:
            - name: myapp
              image: ikubernetes/myapp:v1
              ports:
              - name: http
                containerPort: 80

[root@k8s-master ~]# kubectl apply -f deploy-demo.yaml
[root@k8s-master ~]# kubectl get deploy
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
myapp-deploy       2         0         0            0           1s
[root@k8s-master ~]# kubectl get rs

輸出結果表明我們希望的repalica數是2(根據deployment中的.spec.replicas配置)當前replica數( .status.replicas)是0, 最新的replica數(.status.updatedReplicas)是0,可用的replica數(.status.availableReplicas)是0。過幾秒后再執行get命令,將獲得如下輸出:

[root@k8s-master ~]# kubectl get deploy
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
myapp-deploy       2         2         2            2           10s

可以看到Deployment已經創建了2個 replica,所有的 replica 都已經是最新的了(包含最新的pod template),可用的(根據Deployment中的.spec.minReadySeconds聲明,處於已就緒狀態的pod的最少個數)。執行kubectl get rskubectl get pods會顯示Replica Set(RS)和Pod已創建。

[root@k8s-master ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
myapp-deploy-2035384211       2         2         0       18s

ReplicaSet 的名字總是<Deployment的名字>-<pod template的hash值>

[root@k8s-master ~]# kubectl get pods --show-labels
NAME                            READY     STATUS    RESTARTS   AGE       LABELS
myapp-deploy-2035384211-7ci7o   1/1       Running   0          10s       app=myapp,release=canary,pod-template-hash=2035384211
myapp-deploy-2035384211-kzszj   1/1       Running   0          10s       app=myapp,release=canary,pod-template-hash=2035384211

剛創建的Replica Set將保證總是有2個myapp的 pod 存在。

需要注意:在 Deployment 中的 selector 指定正確的 pod template label(在該示例中是 app = myapp,release=canary),不要跟其他的 controller 的 selector 中指定的 pod template label 搞混了(包括 Deployment、Replica Set、Replication Controller 等)。

上面示例輸出中的 pod label 里的 pod-template-hash label。當 Deployment 創建或者接管 ReplicaSet 時,Deployment controller 會自動為 Pod 添加 pod-template-hash label。這樣做的目的是防止 Deployment 的子ReplicaSet 的 pod 名字重復。通過將 ReplicaSet 的PodTemplate 進行哈希散列,使用生成的哈希值作為 label 的值,並添加到 ReplicaSet selector 里、 pod template label 和 ReplicaSet 管理中的 Pod 上。

2.  Deployment更新升級
1)通過直接更改yaml的方式進行升級,如下:

[root@k8s-master ~]# vim deploy-demo.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
    name: myapp-deploy
    namespace: default
spec:
    replicas: 2
    selector:
        matchLabels:
            app: myapp
            release: canary
    template:
        metadata:
            labels: 
                app: myapp
                release: canary
        spec:
            containers:
            - name: myapp
              image: ikubernetes/myapp:v2
              ports:
              - name: http
                containerPort: 80
[root@k8s-master ~]# kubectl apply -f deploy.yaml

升級過程(我們看到,是停止一台,升級一台的這種循環。

[root@k8s-master ~]# kubectl get pods -l app=myapp -w
NAME                           READY     STATUS    RESTARTS   AGE
myapp-deploy-f4bcc4799-cs5xc   1/1       Running   0          23m
myapp-deploy-f4bcc4799-cwzd9   1/1       Running   0         14m
myapp-deploy-f4bcc4799-k4tq5   1/1       Running   0         23m
myapp-deploy-f4bcc4799-shbmb   1/1       Running   0         14m
myapp-deploy-f4bcc4799-vtk6m   1/1       Running   0         14m


myapp-deploy-f4bcc4799-shbmb   1/1       Terminating   0         16m
myapp-deploy-869b888f66-mv5c6   0/1       Pending   0         0s
myapp-deploy-869b888f66-mv5c6   0/1       Pending   0         0s
myapp-deploy-869b888f66-vk9j6   0/1       Pending   0         0s
myapp-deploy-869b888f66-vk9j6   0/1       Pending   0         0s
myapp-deploy-869b888f66-mv5c6   0/1       ContainerCreating   0         0s
myapp-deploy-869b888f66-r57t5   0/1       Pending   0         0s
myapp-deploy-869b888f66-r57t5   0/1       Pending   0         0s
myapp-deploy-869b888f66-vk9j6   0/1       ContainerCreating   0         1s
myapp-deploy-869b888f66-r57t5   0/1       ContainerCreating   0         1s
myapp-deploy-869b888f66-r57t5   0/1       ContainerCreating   0         1s
myapp-deploy-869b888f66-mv5c6   0/1       ContainerCreating   0         1s
myapp-deploy-869b888f66-vk9j6   0/1       ContainerCreating   0         2s
myapp-deploy-f4bcc4799-shbmb   0/1       Terminating   0         16m
myapp-deploy-f4bcc4799-shbmb   0/1       Terminating   0         16m
myapp-deploy-869b888f66-r57t5   1/1       Running   0         4s
myapp-deploy-f4bcc4799-vtk6m   1/1       Terminating   0         16m
myapp-deploy-869b888f66-rxzbb   0/1       Pending   0         1s
myapp-deploy-869b888f66-rxzbb   0/1       Pending   0         1s
myapp-deploy-869b888f66-rxzbb   0/1       ContainerCreating   0         1s
myapp-deploy-869b888f66-vk9j6   1/1       Running   0         5s
myapp-deploy-f4bcc4799-cwzd9   1/1       Terminating   0         16m
myapp-deploy-869b888f66-vvwwv   0/1       Pending   0         0s
myapp-deploy-869b888f66-vvwwv   0/1       Pending   0         0s

查看一下 rs的情況,以下可以看到原的rs作為備份,而現在是啟動新的rs

[root@k8s-master ~]# kubectl get rs -o wide
NAME                      DESIRED   CURRENT   READY     AGE       CONTAINER(S)       IMAGE(S)               SELECTOR
myapp-deploy-869b888f66   2         2         2         3m        myapp-containers   ikubernetes/myapp:v2   app=myapp,pod-template-hash=4256444922,release=canary
myapp-deploy-f4bcc4799    0         0         0         29m       myapp-containers   ikubernetes/myapp:v1   app=myapp,pod-template-hash=906770355,release=canary

2)通過set 命令直接修改image的版本進行升級,如下:

[root@k8s-master ~]# kubectl set image deployment/myapp-deploy myapp=ikubernetes/myapp:v2

3.  Deployment擴容
1)使用以下命令擴容 Deployment:

[root@k8s-master ~]# kubectl scale deployment myapp-deploy --replicas 5

2)直接修改yaml文件的方式進行擴容:

[root@k8s-master ~]# vim demo.yaml
# 修改.spec.replicas的值
spec:
  replicas: 5
[root@k8s-master ~]# kubectl apply -f demo.yaml

3)通過打補丁的方式進行擴容:

[root@k8s-master ~]# kubectl patch deployment myapp-deploy -p '{"spec":{"replicas":5}}'
[root@k8s-master ~]# kuebctl get pods

4.  Deployment修改滾動更新策略
可以通過打補丁的方式進行修改更新策略,如下:

[root@k8s-master ~]# kubectl patch deployment myapp-deploy -p '{"spec":{"strategy":{"rollingupdate":{"maxsurge“:1,"maxUnavailable":0}}}}'
[root@k8s-master ~]# kubectl describe deploy myapp-deploy
Name:            myapp-deploy
Namespace:        default
CreationTimestamp:    Tue, 28 Aug 2018 09:52:03 -0400
Labels:            app=myapp
            release=canary
Annotations:        deployment.kubernetes.io/revision=4
            kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"myapp-deploy","namespace":"default"},"spec":{"replicas":3,"selector":{...
Selector:        app=myapp,release=dev
Replicas:        3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    0 max unavailable, 1 max surge
Pod Template:
....

5.  金絲雀發布
Deployment控制器支持自定義控制更新過程中的滾動節奏,如"暫停(pause)"或"繼續(resume)"更新操作。比如等待第一批新的Pod資源創建完成后立即暫停更新過程,此時,僅存在一部分新版本的應用,主體部分還是舊的版本;接着再篩選一小部分的用戶請求路由到新版本的Pod應用,繼續觀察能否穩定地按期望的方式運行。確定沒問題之后再繼續完成余下的Pod資源滾動更新,否則立即回滾更新操作。這就是所謂的金絲雀發布(Canary Release)。也就是說,當我們要發布應用的新版本時,我們通常只會將部分流量切到新版本,當測試新版本應用沒有問題時,再慢慢加大切向新版本流量的比例,這種發布方式稱之為"金絲雀發布"。(金絲雀發布是灰度發布的一種方式)。如下命令演示:

1)更新deployment的v3版本,並配置暫停deployment
[root@k8s-master ~]# kubectl set image deployment myapp-deploy myapp=ikubernetes/myapp:v3 && kubectl rollout pause deployment myapp-deploy    
deployment "myapp-deploy" image updated
deployment "myapp-deploy" paused
[root@k8s-master ~]# kubectl rollout status deployment myapp-deploy  #觀察更新狀態

2)監控更新的過程,可以看到已經新增了一個資源,但是並未按照預期的狀態去刪除一個舊的資源,就是因為使用了pause暫停命令
[root@k8s-master ~]# kubectl get pods -l app=myapp -w 
NAME                            READY     STATUS    RESTARTS   AGE
myapp-deploy-869b888f66-dpwvk   1/1       Running   0          24m
myapp-deploy-869b888f66-frspv   1/1       Running   0         24m
myapp-deploy-869b888f66-sgsll   1/1       Running   0         24m
myapp-deploy-7cbd5b69b9-5s4sq   0/1       Pending   0         0s
myapp-deploy-7cbd5b69b9-5s4sq   0/1       Pending   0         0s
myapp-deploy-7cbd5b69b9-5s4sq   0/1       ContainerCreating   0         1s
myapp-deploy-7cbd5b69b9-5s4sq   0/1       ContainerCreating   0         2s
myapp-deploy-7cbd5b69b9-5s4sq   1/1       Running   0         19s

3)確保更新的pod沒問題了,繼續更新
[root@k8s-master ~]# kubectl rollout resume deploy  myapp-deploy

4)查看最后的更新情況
[root@k8s-master ~]# kubectl get pods -l app=myapp -w 
NAME                            READY     STATUS    RESTARTS   AGE
myapp-deploy-869b888f66-dpwvk   1/1       Running   0          24m
myapp-deploy-869b888f66-frspv   1/1       Running   0         24m
myapp-deploy-869b888f66-sgsll   1/1       Running   0         24m
myapp-deploy-7cbd5b69b9-5s4sq   0/1       Pending   0         0s
myapp-deploy-7cbd5b69b9-5s4sq   0/1       Pending   0         0s
myapp-deploy-7cbd5b69b9-5s4sq   0/1       ContainerCreating   0         1s
myapp-deploy-7cbd5b69b9-5s4sq   0/1       ContainerCreating   0         2s
myapp-deploy-7cbd5b69b9-5s4sq   1/1       Running   0         19s
myapp-deploy-869b888f66-dpwvk   1/1       Terminating   0         31m
myapp-deploy-7cbd5b69b9-p6kzm   0/1       Pending   0         1s
myapp-deploy-7cbd5b69b9-p6kzm   0/1       Pending   0         1s
myapp-deploy-7cbd5b69b9-p6kzm   0/1       ContainerCreating   0         1s
myapp-deploy-7cbd5b69b9-p6kzm   0/1       ContainerCreating   0         2s
myapp-deploy-869b888f66-dpwvk   0/1       Terminating   0         31m
myapp-deploy-869b888f66-dpwvk   0/1       Terminating   0         31m
myapp-deploy-869b888f66-dpwvk   0/1       Terminating   0         31m
myapp-deploy-7cbd5b69b9-p6kzm   1/1       Running   0         18s
myapp-deploy-869b888f66-frspv   1/1       Terminating   0         31m
myapp-deploy-7cbd5b69b9-q8mvs   0/1       Pending   0         0s
myapp-deploy-7cbd5b69b9-q8mvs   0/1       Pending   0         0s
myapp-deploy-7cbd5b69b9-q8mvs   0/1       ContainerCreating   0         0s
myapp-deploy-7cbd5b69b9-q8mvs   0/1       ContainerCreating   0         1s
myapp-deploy-869b888f66-frspv   0/1       Terminating   0         31m
myapp-deploy-869b888f66-frspv   0/1       Terminating   0         31m
myapp-deploy-869b888f66-frspv   0/1       Terminating   0         31m
myapp-deploy-869b888f66-frspv   0/1       Terminating   0         31m
......

6.  Deployment版本回退
kubernetes默認會在系統中保存前兩次的 Deployment 的 rollout 歷史記錄,以便可隨時回退(可修改revision history limit來更改保存的revision數)。

需要注意: 只要 Deployment 的 rollout 被觸發就會創建一個 revision。也就是說當且僅當 Deployment 的 Pod template(如.spec.template)被更改,例如更新template 中的 label 和容器鏡像時,就會創建出一個新的 revision。

其他的更新,比如擴容 Deployment 不會創建 revision——因此我們可以很方便的手動或者自動擴容。這意味着當您回退到歷史 revision 時,只有 Deployment 中的 Pod template 部分才會回退。(即使用"rollout history"命令,查看Deployment的歷史信息)

[root@k8s-master ~]#  kubectl rollout history deploy  myapp-deploy  #檢查Deployment升級記錄
deployments "myapp-deploy"
REVISION    CHANGE-CAUSE
0        <none>
3        <none>
4        <none>
5        <none>

這里在創建deployment時沒有增加--record參數,故不能看到revision變化。在創建 Deployment 的時候使用了--record參數可以記錄命令,就可以方便的查看每次 revision 的變化。因此記住:使用--record參數將記錄后續創建對象的操作,方便管理與問題追溯!

查看Deployment歷史詳細信息(即使用"rollout history"查看Deployment的歷史信息

[root@k8s-master ~]# kubectl rollout history deployment/myapp-deploy                  #查看Deployment的歷史信息
[root@k8s-master ~]# kubectl rollout history deployment/myapp-deploy --revision=2     #查看單個revision 的詳細信息

RC在rolling-update升級成功后不能直接回滾,但Deployment可以回滾歷史版本,默認是回退到上一個版本(即使用"rollout undo"回滾到上一版本):

[root@k8s-master ~]# kubectl rollout undo deployment/myapp-deploy
deployment "myapp-deploy" rolled back

回滾到某個指定的歷史版本(即可以使用"--to-revision"參數回滾到某個指定的歷史版本):

[root@k8s-master ~]# kubectl rollout undo deployment/myapp-deploy --to-revision=2
deployment "myapp-deploy" rolled back

###########  總結下關於 Deployment 控制器的用法細節  #############
更新策略
滾動更新 rolling update 和 重新創建 recreate,默認為滾動更新!

spec.strategy.rollingUpdate.maxSurge
升級期間允許的總Pod數超出期望值的個數,可以是具體數值或百分比,若為1,則表示可超出1個

spec.strategy.rollingUpdate.maxUnavailable
升級期間正常可用的Pod副本數,包含新舊版本,最多不能低於期望值的個數,可為具體數值或百分比

spec.minReadySeconds
可控制更新速度,此時間內更新操作會被阻塞

千萬注意:maxSurge和maxUnavailable不能同時為0,否則無法升級!!

保留滾動更新歷史版本,下面參數表示控制保存歷史版本的數量
spec.revisionHistoryLimit

歷史版本可用於回滾,要記錄歷史版本需要在創建Deployment時使用 --record選項
kubectl apply -f b.yaml --record

修改間隔時間
# kubectl patch deployment myapp-deploy -p '{"spec":{"minReadySeconds":5}}'

修改鏡像 觸發滾動更新
# kubectl set image deployment myapp-deploy myapp=ikubernetes/myapp:v2

查看滾動更新過程中狀態信息
# kubectl rollout status deployment myapp-deploy
# kubectl get deployment myapp-deploy --watch

舊的replicasets會保留 但此前管理的Pod對象會刪除
# kubectl get rs -l app=myapp

訪問
# curl $(kubectl get pods myapp-deploy-79d4d5d95f-k2b7x -o go-template={{.status.podIP}})

金絲雀發布
發布時,第一批更新完成后,暫停升級,新舊同時提供服務,其中新的很少,待確認沒有問題后,完成余下部分的升級

先添加一個新Pod,設置maxSurge為1,maxUnavailable為0
# kubectl patch deployment myapp-deploy -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'

啟動更新 在啟動后,在maxReadySeconds屬性設置的時間內執行暫停 通常使用&&
#kubectl set image deployment myapp-deploy myapp=ikubernetes/myapp:v3 && kubectl rollout pause deployment myapp-deploy

查看進度
# kubectl rollout status deployment myapp-deploy
# kubectl get replicasets.apps
此時,可將一部分流量引入新Pod上,進行驗證。

繼續升級
# kubectl rollout resume deployment myapp-deploy

回滾到之前的版本
# kubectl rollout undo deployment myapp-deploy

查看歷史版本
# kubectl rollout history deployment myapp-deploy

查看當前版本
# kubectl describe deployments.apps myapp-deploy

回滾到第1個版本
# kubectl rollout undo deployment myapp-deploy --to-revision=1

若此前滾動更新過程處於暫停狀態,那么回滾操作需要先將Pod模板的版本改回到之前的版本,然后繼續更新,否則會一直處理暫停狀態而無法回滾

擴容和縮容
修改spec.replicas的值,可用kubectl apply、kubectl edit、kubectl scale等命令實現

Replication Controller 和ReplicaSet 都用於在kubernetes集群上運行指定數量的pod,但它們不關心在哪個節點上運行。有種情況是讓每個節點都運行某一個pod比如日志收集,和監控應用。這時候要用到另外一個控制器了DaemonSet。DaemonSet也使用Pod模版,默認是將模版中的pod,在每個節點中創建pod,但也有特殊情況,只在某特定節點上創建pod,比如不同的硬盤類型。這可以用pod模版的nodeSelector屬性指定。

四、DaemonSet(DS)
DaemonSet確保全部(或者一些)Node節點上運行一個Pod 的副本,可使用節點選擇器及節點標簽指定Pod僅在部分Node節點運行。當有Node加入集群時,會為他們新增一個Pod;當有Node從集群移除時,這些Pod也會被回收。刪除 DaemonSet將會刪除它創建的所有Pod。DaemonSet常用於存儲、日志、監控類守護進程。

DaemonSet 典型用法 [ Linux守護進程通常就指Daemon或服務。Daemon名稱通常為相應服務后加上一個d。如cron服務的daemon為crond,http服務的daemon為httpd ] :
 在每個節點上運行存儲Daemon,比如glusterd 或 ceph。
-  在每個Node上運行日志收集 daemon。例如fluentd、logstash。
在每個Node上運行監控 daemon。例如 Prometheus Node Exporter 或 collectd。

DeamonSet簡單的用法是,在所有的 Node 上都存在一個 DaemonSet,將被作為每種類型的 daemon 使用。 一個稍微復雜的用法可能是,對單獨的每種類型的 daemon 使用多個 DaemonSet,但具有不同的標志,和/或對不同硬件類型具有不同的內存、CPU要求。

1.  編寫DaemonSet Spec
1)必需字段
和其它所有 Kubernetes 配置一樣,DaemonSet 需要 apiVersion、kind 和 metadata字段。

[root@k8s-master ~]# kubectl explain daemonset
KIND:     DaemonSet
VERSION:  extensions/v1beta1

DESCRIPTION:
     DEPRECATED - This group version of DaemonSet is deprecated by
     apps/v1beta2/DaemonSet. See the release notes for more information.
     DaemonSet represents the configuration of a daemon set.

FIELDS:
   apiVersion    <string>
     APIVersion defines the versioned schema of this representation of an
     object. Servers should convert recognized schemas to the latest internal
     value, and may reject unrecognized values. More info:
     https://git.k8s.io/community/contributors/devel/api-conventions.md#resources

   kind    <string>
     Kind is a string value representing the REST resource this object
     represents. Servers may infer this from the endpoint the client submits
     requests to. Cannot be updated. In CamelCase. More info:
     https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds

   metadata    <Object>
     Standard object's metadata. More info:
     https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata

   spec    <Object>
     The desired behavior of this daemon set. More info:
     https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status

   status    <Object>
     The current status of this daemon set. This data may be out of date by some
     window of time. Populated by the system. Read-only. More info:
     https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status

2)Pod模板
.spec 唯一必需的字段是 .spec.template。
.spec.template 是一個 Pod 模板。 它與 Pod 具有相同的 schema,除了它是嵌套的,而且不具有 apiVersion 或 kind 字段。
Pod 除了必須字段外,在 DaemonSet 中的 Pod 模板必須指定合理的標簽。
在 DaemonSet 中的 Pod 模板必需具有一個值為 Always 的 RestartPolicy,或者未指定它的值,默認是 Always。

[root@k8s-master ~]# kubectl explain daemonset.spec.template.spec

3)Pod Seletor
.spec.selector 字段表示 Pod Selector,它與 Job 或其它資源的 .sper.selector 的原理是相同的。
spec.selector 表示一個對象,它由如下兩個字段組成:
    matchLabels - 與 ReplicationController 的 .spec.selector 的原理相同。
    matchExpressions - 允許構建更加復雜的 Selector,可以通過指定 key、value 列表,以及與 key 和 value 列表的相關的操作符。
當上述兩個字段都指定時,結果表示的是 AND 關系。

如果指定了 .spec.selector,必須與 .spec.template.metadata.labels 相匹配。如果沒有指定,它們默認是等價的。如果與它們配置的不匹配,則會被 API 拒絕。

如果 Pod 的 label 與 selector 匹配,或者直接基於其它的 DaemonSet、或者 Controller(例如 ReplicationController),也不可以創建任何 Pod。 否則 DaemonSet Controller 將認為那些 Pod 是它創建的。Kubernetes 不會阻止這樣做。一個場景是,可能希望在一個具有不同值的、用來測試用的 Node 上手動創建 Pod。

4)Daemon Pod通信
與 DaemonSet 中的 Pod 進行通信,幾種可能的模式如下:
Push:配置 DaemonSet 中的 Pod 向其它 Service 發送更新,例如統計數據庫。它們沒有客戶端。
-  NodeIP 和已知端口:DaemonSet 中的 Pod 可以使用 hostPort,從而可以通過 Node IP 訪問到 Pod。客戶端能通過某種方法知道 Node IP 列表,並且基於此也可以知道端口。
-  DNS:創建具有相同 Pod Selector 的 Headless Service,然后通過使用 endpoints 資源或從 DNS 檢索到多個 A 記錄來發現 DaemonSet。
Service:創建具有相同 Pod Selector 的 Service,並使用該 Service 訪問到某個隨機 Node 上的 daemon。(沒有辦法訪問到特定 Node)

DaemonSet的pod調度策略與RC類似,除了使用系統內置的算法在每個node上進行調度,也可以在pod定義中使用nodeselector或nodeaffinity來指定滿足條件的node范圍進行調度。

2.  創建DaemonSet
DaemonSet 不支持 replicas參數,默認不會部署到master節點!

1)編輯daemonSet的yaml文件
可以在同一個yaml文件中定義多個資源,這里將redis和filebeat定在一個文件當中

[root@k8s-master mainfests]# vim ds-demo.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
      role: logstor
  template:
    metadata:
      labels:
        app: redis
        role: logstor
    spec:
      containers:
      - name: redis
        image: redis:4.0-alpine
        ports:
        - name: redis
          containerPort: 6379
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: filebeat-ds
  namespace: default
spec:
  selector:
    matchLabels:
      app: filebeat
      release: stable
  template:
    metadata:
      labels: 
        app: filebeat
        release: stable
    spec:
      containers:
      - name: filebeat
        image: ikubernetes/filebeat:5.6.5-alpine
        env:
        - name: REDIS_HOST
          value: redis.default.svc.cluster.local
        - name: REDIS_LOG_LEVEL
          value: info

2)創建pods                
[root@k8s-master mainfests]# kubectl apply -f ds-demo.yaml
deployment.apps/redis created
daemonset.apps/filebeat-ds created

3)暴露端口
[root@k8s-master mainfests]# kubectl expose deployment redis --port=6379
service/redis exposed
[root@k8s-master mainfests]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP        16d
myapp        NodePort    10.106.67.242    <none>        80:32432/TCP   13d
nginx        ClusterIP   10.106.162.254   <none>        80/TCP         14d
redis        ClusterIP   10.107.163.143   <none>        6379/TCP       4s

[root@k8s-master mainfests]# kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
filebeat-ds-rpp9p        1/1       Running   0          5m
filebeat-ds-vwx7d        1/1       Running   0          5m
pod-demo                 2/2       Running   6          5d
redis-5b5d6fbbbd-v82pw   1/1       Running   0          36s

4)測試redis是否收到日志
[root@k8s-master mainfests]# kubectl exec -it redis-5b5d6fbbbd-v82pw -- /bin/sh
/data # netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 0.0.0.0:6379            0.0.0.0:*               LISTEN      
tcp        0      0 :::6379                 :::*                    LISTEN      

/data # nslookup redis.default.svc.cluster.local
nslookup: can't resolve '(null)': Name does not resolve

Name:      redis.default.svc.cluster.local
Address 1: 10.107.163.143 redis.default.svc.cluster.local

/data # redis-cli -h redis.default.svc.cluster.local
redis.default.svc.cluster.local:6379> KEYS *  #由於redis在filebeat后面才啟動,日志可能已經發走了,所以查看key為空
(empty list or set)

[root@k8s-master mainfests]# kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
filebeat-ds-rpp9p        1/1       Running   0          14m
filebeat-ds-vwx7d        1/1       Running   0          14m
pod-demo                 2/2       Running   6          5d
redis-5b5d6fbbbd-v82pw   1/1       Running   0          9m
[root@k8s-master mainfests]# kubectl exec -it filebeat-ds-rpp9p -- /bin/sh
/ # cat /etc/filebeat/filebeat.yml 
filebeat.registry_file: /var/log/containers/filebeat_registry
filebeat.idle_timeout: 5s
filebeat.spool_size: 2048

logging.level: info

filebeat.prospectors:
- input_type: log
  paths:
    - "/var/log/containers/*.log"
    - "/var/log/docker/containers/*.log"
    - "/var/log/startupscript.log"
    - "/var/log/kubelet.log"
    - "/var/log/kube-proxy.log"
    - "/var/log/kube-apiserver.log"
    - "/var/log/kube-controller-manager.log"
    - "/var/log/kube-scheduler.log"
    - "/var/log/rescheduler.log"
    - "/var/log/glbc.log"
    - "/var/log/cluster-autoscaler.log"
  symlinks: true
  json.message_key: log
  json.keys_under_root: true
  json.add_error_key: true
  multiline.pattern: '^\s'
  multiline.match: after
  document_type: kube-logs
  tail_files: true
  fields_under_root: true

output.redis:
  hosts: ${REDIS_HOST:?No Redis host configured. Use env var REDIS_HOST to set host.}
  key: "filebeat"

[root@k8s-master mainfests]# kubectl get pods -l app=filebeat -o wide
NAME                READY     STATUS    RESTARTS   AGE       IP            NODE
filebeat-ds-rpp9p   1/1       Running   0          16m       10.244.2.12   k8s-node02
filebeat-ds-vwx7d   1/1       Running   0          16m       10.244.1.15   k8s-node01

3.  DaemonSet的滾動更新
Kubernetes 1.6版本后,DaemonSet也能支持滾動升級了,支持 RollingUpdate 滾動更新 和 OnDelete 刪除時更新(刪除后重建啟用新版本)。DaemonSet的滾動更新策略是先終止舊的pod,再創建一個新的pod,逐步進行替換的

DaemonSet有兩種更新策略類型:
- OnDelete:這是向后兼容性的默認更新策略。使用 OnDelete更新策略,在更新DaemonSet模板后,只有在手動刪除舊的DaemonSet pod時才會創建新的DaemonSet pod。
這與Kubernetes 1.5或更早版本中DaemonSet的行為相同。
- RollingUpdate:使用RollingUpdate更新策略,在更新DaemonSet模板后,舊的DaemonSet pod將被終止,並且將以受控方式自動創建新的DaemonSet pod。

DaemonSet更新配置定義在 spec.updateStrategy 下,要啟用DaemonSet的滾動更新功能,必須將其設置 .spec.updateStrategy.type為RollingUpdate。

在更新一個DaemonSet模板的時候,舊的pod副本會被自動刪除,同時新的pod副本會被自動創建,此時daemonset的更新策略(updateStrategy)為RollingUpdate,如下所示:

apiVersion: apps/v1
kind: DaemonSet
metadata:
   name: filebeat-ds
   namespace: default
spec:
  updateStrategy:
    type: RollingUpdate

updateStrategy 的另外一個值是OnDelete,即只有手動刪除了DaemonSet創建的pod副本,新的Pod副本才會被創建出來。如果不設置updateStrategy的值,則在k8s 1.6之后的版本中會被默認設置為RollingUpdate。

查看daemonset
# kubectl describe daemonsets filebeat-ds
# kubectl get pods -l app=filebeat -o custom-columns=NAME:metadata.name,NODE:spec.nodeName

spec.template.spec.nodeSelector    #可用於指定節點
# kubectl patch daemonsets.apps filebeat-ds -p '{"spec":{"template":{"spec":{"nodeSelector":{"disktype":"ssd"}}}}}'    #補丁命令

1)查看當前的更新策略:
[root@k8s-master mainfests]# kubectl get ds/filebeat-ds -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}'
RollingUpdate

2)更新DaemonSet模板
對RollingUpdateDaemonSet的任何更新都.spec.template將觸發滾動更新。這可以通過幾個不同的kubectl命令來完成。

聲明式命令方式:
如果使用配置文件進行更新DaemonSet,可以使用kubectl aapply:
# kubectl apply -f ds-demo.yaml

補丁式命令方式:
# kubectl edit ds/filebeat-ds
# kubectl patch ds/filebeat-ds -p=<strategic-merge-patch>

僅僅更新容器鏡像還可以使用以下命令:
# kubectl set image ds/<daemonset-name> <container-name>=<container-new-image>

下面對filebeat-ds的鏡像進行版本更新,如下:
[root@k8s-master mainfests]# kubectl set image daemonsets filebeat-ds filebeat=ikubernetes/filebeat:5.6.6-alpine
daemonset.extensions/filebeat-ds image updated

[root@k8s-master mainfests]# kubectl get pods -w              #觀察滾動更新狀態
NAME                     READY     STATUS        RESTARTS   AGE
filebeat-ds-rpp9p        1/1       Running       0          27m
filebeat-ds-vwx7d        0/1       Terminating   0          27m
pod-demo                 2/2       Running       6          5d
redis-5b5d6fbbbd-v82pw   1/1       Running       0          23m
filebeat-ds-vwx7d   0/1       Terminating   0         27m
filebeat-ds-vwx7d   0/1       Terminating   0         27m
filebeat-ds-s466l   0/1       Pending   0         0s
filebeat-ds-s466l   0/1       ContainerCreating   0         0s
filebeat-ds-s466l   1/1       Running   0         13s
filebeat-ds-rpp9p   1/1       Terminating   0         28m
filebeat-ds-rpp9p   0/1       Terminating   0         28m
filebeat-ds-rpp9p   0/1       Terminating   0         28m
filebeat-ds-rpp9p   0/1       Terminating   0         28m
filebeat-ds-hxgdx   0/1       Pending   0         0s
filebeat-ds-hxgdx   0/1       ContainerCreating   0         0s
filebeat-ds-hxgdx   1/1       Running   0         28s

[root@k8s-master mainfests]# kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
filebeat-ds-hxgdx        1/1       Running   0          2m
filebeat-ds-s466l        1/1       Running   0          2m
pod-demo                 2/2       Running   6          5d
redis-5b5d6fbbbd-v82pw   1/1       Running   0          25m

從上面的滾動更新,可以看到在更新過程中,是先終止舊的pod,再創建一個新的pod,逐步進行替換的,這就是DaemonSet的滾動更新策略!!!

五、StatefulSet
使用Kubernetes來調度無狀態的應用非常簡單,那Kubernetes如何來管理調度有狀態的應用呢?Kubernetes中提供了StatefulSet控制器來管理有狀態的應用。Setatefulset控制器是pod資源控制器的一種實現,用於部署和擴展有狀態應用的pod資源,確保他們的運行順序及每個pod資源的唯一性Deployment用於部署無狀態服務,StatefulSet用來部署有狀態服務。

Statefulset主要適用於具有以下特點的應用:
1.  具有固定的網絡標記(主機名);
2.  具有持久化存儲;
3.  需要按順序部署和擴展;
4.  需要按順序終止及刪除;
5.  需要按順序滾動更新;

穩定的主要是針對Pod發生re-schedule后仍然要保持之前的網絡標識和持久化存儲。這里所說的網絡標識包括hostname、集群內DNS中該Pod對應的A Record,並不能保證Pod re-schedule之后IP不變。要想保持Pod IP不變,我們可以借助穩定的Pod hostname定制IPAM獲取固定的Pod IP。借助StatefulSet的穩定的唯一的網絡標識特性,能比較輕松的實現Pod的固定IP需求,然后如果使用Deployment,那么將會復雜的多,需要考慮滾動更新的過程中的參數控制(maxSurge、maxUnavailable)、每個應用的IP池預留造成的IP浪費等等問題。

因此,可以再加一個StatefulSet的使用場景:實現固定的Pod IP方案, 可以優先考慮基於StatefulSet;

StatefulSet是Kubernetes提供的管理有狀態應用的負載管理控制器API。在Pods管理的基礎上,保證Pods的順序和一致性。與Deployment一樣,StatefulSet也是使用容器的Spec來創建Pod,與之不同StatefulSet創建的Pods在生命周期中會保持持久的標記(例如Pod Name)。

StatefulSet控制器中pod資源的標識符及存儲卷
由StatefulSet控制器創建的pod資源擁有固定、唯一的標識和專用的存儲卷,即使重新調度或終止后重建,其名稱也依然保持不變,且此前的存儲卷及數據不會丟失。它們的唯一標識符是通過唯一的索引號及相關的statefulset對象的名稱而生成,格式為“<statefulset name>-<ordinalindex>”。

StatefulSet 最佳實踐
--  StatefulSet對應Pod的存儲最好通過StorageClass來動態創建:每個Pod都會根據StatefulSet中定義的VolumeClaimTemplate來創建一個對應的PVC,然后PVS通過StorageClass自動創建對應的PV,並掛載給Pod。所以這種方式,需要你事先創建好對應的StorageClass。當然,你也可以通過預先由管理員手動創建好對應的PV,只要能保證自動創建的PVC能和這些PV匹配上。

--  為了數據安全,當刪除StatefulSet中Pods或者對StatefulSet進行縮容時,Kubernetes並不會自動刪除StatefulSet對應的PV,而且這些PV默認也不能被其他PVC Bound。當你確認數據無用之后再手動去刪除PV的時候,數據是否刪除取決於PV的ReclaimPolicy配置。Reclaim Policy支持以下三種:
    --  Retain,意味着需要你手動清理;
    --  Recycle,等同於rm -rf /thevolume/*
    --  Delete,默認值,依賴於后端的存儲系統自己實現。
       注意:
           --  目前只有NFS和HostPath支持Recycle;
           --  EBS,GCE PD, Azure Disk,Openstack Cinder支持Delete。
--   請小心刪除StatefulSet對應的PVC,首先確保Pods已經完全Terminate,然后確定不需要Volume中的數據后,再考慮刪除PV。因為刪除PVC可能觸發對應PV的自動刪除,並根據StorageClass中的recalimPolicy配置可能造成volume中的數據丟失。

--   因為部署的是有狀態應用,我們需要自己創建對應的Headless Service,注意Label要和StatefulSet中Pods的Label匹配。Kubernetes會為該Headless Service創建對應SRV Records,包含所有的后端Pods,KubeDNS會通過Round Robin算法進行選擇。

--   在Kubernetes 1.8+中,你必須保證StatefulSet的spec.selector能匹配.spec.template.metadata.labels,否則會導致StatefulSet創建失敗。在Kubernetes 1.8之前,StatefulSet的spec.selector如果沒指定則默認會等同於.spec.template.metadata.labels。

--   對StatefulSet進行縮容前,你需要確認對應的Pods都是Ready的,否則即使你觸發了縮容操作,Kubernetes也不會真的進行縮容操作。

如何理解穩定的網絡標識
StatefulSet中反復強調的“穩定的網絡標識”,主要指Pods的hostname以及對應的DNS Records。
HostName:StatefulSet的Pods的hostname按照這種格式生成:$(statefulset name)-$(ordinal), ordinal從0 ~ N-1(N為期望副本數)。
    --  StatefulSet Controller 在創建pods時,會給pod加上一個pod name label:statefulset.kubernetes.io/pod-name, 然后設置到Pod的pod name和hostname中。
    --  pod name label用處是什么?我們可以創建獨立的Service匹配到這個指定的pod,然后方便單獨對這個pod進行debug等處理。

DNS Records:
    --  Headless Service的DNS解析:$(service name).$(namespace).svc.cluster.local 通過DNS RR解析到后端其中一個Pod。SRV Records只包含對應的Running and Ready的Pods,不Ready的Pods不會在對應的SRV Records中。
    -- Pod的DNS解析:$(hostname).$(service name).$(namespace).svc.cluster.local解析到對應hostname的Pod。

如何理解穩定的持久化存儲
--  每個Pod對應一個PVC,PVC的名稱是這樣組成的:$(volumeClaimTemplates.name)-$(pod's hostname),跟對應的Pod是一一對應的。
--  當Pod發生re-schedule(其實是recreate)后,它所對應的PVC所Bound的PV仍然會自動的掛載到新的Pod中。
--  Kubernetes會按照VolumeClaimTemplate創建N(N為期望副本數)個PVC,由PVCs根據指定的StorageClass自動創建PVs。
--  當通過級聯刪除StatefulSet時並不會自動刪除對應的PVCs,所以PVC需要手動刪除。
--  當通過級聯刪除StatefulSet或者直接刪除對應Pods時,對應的PVs並不會自動刪除。需要你手動的去刪除PV。

部署和伸縮時與Deployment的區別
--  當部署有N個副本的StatefulSet應用時,嚴格按照index從0到N-1的遞增順序創建,下一個Pod創建必須是前一個Pod Ready為前提。
--  當刪除有N個副本的StatefulSet應用時,嚴格按照index從N-1到0的遞減順序刪除,下一個Pod刪除必須是前一個Pod shutdown並完全刪除為前提。
--  當擴容StatefulSet應用時,每新增一個Pod必須是前一個Pod Ready為前提。
--  當縮容StatefulSet應用時,沒刪除一個Pod必須是前一個Pod shutdown並成功刪除為前提。
--  注意StatefulSet的pod.Spec.TerminationGracePeriodSeconds不要設置為0。

Node網絡異常等情況下該如何處理
--  正常情況下,StatefulSet Controller會保證集群內同一namespace下不會出現多個相同network identity的StatefulSet Pods。
--  如果集群內出現以上情況,那么有可能導致該有狀態應用不能正常工作、甚至出現數據丟失等致命問題。

那么什么情況下會導致出現同一namespace下會出現多個相同network identity的StatefulSet Pods呢?考慮下Node出現網絡Unreachable的情況:

--  如果使用Kubernetes 1.5之前的版本,當Node Condition是NetworkUnavailable時,node controller會強制從apiserver中刪除這個Node上的這些pods對象,這時StatefulSet Controller就會自動在其他Ready Nodes上recreate同identity的Pods。這樣做其實風險是很大的,可能會導致有一段時間有多個相同network identity的StatefulSet Pods,可能會導致該有狀態應用不能正常工作。所以盡量不要在Kubernetes 1.5之前的版本中使用StatefulSet,或者你明確知道這個風險並且無視它。

--  如果使用Kubernetes 1.5+的版本,當Node Condition是NetworkUnavailable時,node controller不會強制從apiserver中刪除這個Node上的這些pods對象,這些pods的state在apiserver中被標記為Terminating或者Unknown,因此StatefulSet Controller並不會在其他Node上再recreate同identity的Pods。當你確定了這個Node上的StatefulSet Pods shutdown或者無法和該StatefulSet的其他Pods網絡不同時,接下來就需要強制刪除apiserver中這些unreachable pods object,然后StatefulSet Controller就能在其他Ready Nodes上recreate同identity的Pods,使得StatefulSet繼續健康工作。

那么在Kubernetes 1.5+中,如何強制從apiserver中刪除該StatefulSet pods呢?有如下三種方法:

--  如果Node永久的無法連接網絡或者關機了,意味着能確定這個Node上的Pods無法與其他Pods通信了,不會對StatefulSet應用的可用性造成影響,那么建議手動從apiserver中刪除該NetworkUnavailable的Node,Kubernetes會自動從apiserver中刪除它上面的Pods object。
--  如果Node是因為集群網絡腦裂導致的,則建議去檢查網絡問題並成功恢復,因為Pods state已經是Terminating或者Unkown,所以kubelet從apiserver中獲取到這個信息后就會自動刪除這些Pods。
--  其他情況才考慮直接手動從apiserver中刪除這些Pods,因為這時你無法確定對應的Pods是否已經shutdown或者對StatefulSet應用無影響,強制刪除后就可能導致出現同一namespace下有多個相同network identity的StatefulSet Pods,所以盡量不要使用這種方法。
#  kubectl delete pods <pod> --grace-period=0 --force

當前Node Condition有以下6種:OutOfDisk、Ready、MemoryPressure、DiskPressure、NetworkUnavailable、ConfigOK

StatefulSet的Pod管理策略
Kubernetes 1.7+,StatefulSet開始支持Pod Management Policy配置,提供以下兩種配置:
--  OrderedReady,StatefulSet的Pod默認管理策略,就是逐個的、順序的進行部署、刪除、伸縮,也是默認的策略。
--  Parallel,支持並行創建或刪除同一個StatefulSet下面的所有Pod,並不會逐個的、順序的等待前一個操作確保成功后才進行下一個Pod的處理。其實用這種策略的場景非常少。

StatefulSet的更新策略
StatefulSet的更新策略(由.spec.updateStrategy.type指定)支持以下兩種:
--  OnDelete, 含義同Deployment的OnDelete策略,大家應該很熟悉了,不多介紹。
--  RollingUpdate,滾動更新過程也跟Deployment大致相同,區別在於:
       --  相當於Deployment的maxSurge=0,maxUnavailable=1(其實StatefulSet是不存在這兩個配置的)
       --  滾動更新的過程是有序的(逆序),index從N-1到0逐個依次進行,並且下一個Pod創建必須是前一個Pod Ready為前提,下一個Pod刪除必須是前一個Pod shutdown並完全刪除為前提。
       --  支持部分實例滾動更新,部分不更新,通過.spec.updateStrategy.rollingUpdate.partition來指定一個index分界點。
               --  所有ordinal大於等於partition指定的值的Pods將會進行滾動更新。
               --  所有ordinal小於partition指定的值得Pods將保持不變。即使這些Pods被recreate,也會按照原來的pod template創建,並不會更新到最新的版本。
               --  特殊地,如果partition的值大於StatefulSet的期望副本數N,那么將不會觸發任何Pods的滾動更新。

1. StatefulSet 創建
一個完整的statefulset控制器是由一個Headless Service、statefulset、volumeClaimTemplate三個組件組成。(下面示例沒有使用動態供給的PV,其實,為了方便pvc的創建,可以使用glusterfs提供動態的PV供給)。

[root@devops-101 ~]# vim ss-nginx.yml 
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
     matchLabels:
       app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: docker.io/nginx
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: ["ReadWriteOnce"]
      volumeMode: Filesystem
      resources:
        requests:
          storage: 50Mi
      storageClassName: local-storage

需要注意:
StatefulSet創建順序是從0到N-1,終止順序則是相反。如果需要對StatefulSet擴容,則之前的N個Pod必須已經存在。如果要終止一個Pod,則它的后序Pod必須全部終止。
在Kubernetes 1.7版本后,放松了順序的保證策略,對應的參數為 .spec.podManagementPolicy。

默認情況下,statefulset控制器以串行的方式運行創建各pod副本,如果想要以並行的方式創建和刪除pod資源,則可以設定.spec.podManagementPolicy字段值為"Parallel",
默認值為"OrderadReady"。

執行創建命令,並且觀察對象是否正常創建。
[root@devops-101 ~]# kubectl create -f ss-nginx.yml 
service "nginx" created
statefulset "web" created
[root@devops-101 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   9d
nginx        ClusterIP   None         <none>        80/TCP    1d
[root@devops-101 ~]# kubectl get statefulset 
NAME      DESIRED   CURRENT   AGE
web       2         2         1d

看一下Pod是否是有順序的
[root@devops-101 ~]# kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          11m
web-1     1/1       Running   0          11m

根據Pod的順序,每個Pod擁有對應的主機名,在Pod中執行hostname命令確認下。
[root@devops-101 ~]# for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1

使用帶有nslookup命令的busybox鏡像啟動一個Pod,檢查集群內的DNS地址設置。

2.  StatefulSet 擴縮容
Statefulset資源的擴縮容與deployment資源相似,均可通過“kubectl scale”或“kubectl patch”或“kubectl edit”命令直接修改其副本數。

將Pod實例擴充到5個。
[root@devops-101 ~]# kubectl scale sts web --replicas=5
statefulset.apps/web scaled

[root@devops-101 ~]# kubectl get pods -w
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          5m
web-1     1/1       Running   0          5m
web-2     0/1       Pending   0          1s
web-2     0/1       Pending   0          1s

因為這里的集群PV不是動態供給的,所以擴容停留在等待PV的階段,可以按照后面的辦法手工創建相應的PV。

將實例減少到2個。
[root@devops-101 ~]# kubectl get pod
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          24m
web-1     1/1       Running   0          23m
web-2     1/1       Running   0          18m
web-3     1/1       Running   0          10m
web-4     1/1       Running   0          6m

[root@devops-101 ~]# kubectl patch sts web -p '{"spec":{"replicas":2}}'
statefulset.apps/web patched

3.  StatefulSet更新策略(資源升級)

1)滾動更新
滾動更新是以逆序的方式從其最大索引編號的pod資源逐一進行。即默認的更新策略是以相反的順序依次更新Pod

升級nginx版本
[root@devops-101 ~]# kubectl set image statefulset web nginx=docker.io/nginx:latest

觀察升級過程
[root@devops-101 ~]# kubectl get pods -l app=nginx -w

2)暫存更新
當用戶需要設定一個更新操作,但又不希望他立即執行時,可將跟新操作予以“暫存”,待條件滿足后再手動觸發更新。Statefulset資源的分區跟新機制能夠實現此項功能。

首先將滾動更新分區制設置大於pod副本數量
[root@devops-101 ~]# kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":3}}}}'

更新pod鏡像
[root@devops-101 ~]# kubectl set image statefulset web nginx=docker.io/nginx:latest
 
檢查各pod中鏡像版本並未發生改變
[root@devops-101 ~]# kubectl get pods -l app=nginx -o custom-columns=NAME:metadata.name,IMAGE:spec.containers[0].image

3)金絲雀部署
將處於暫存狀態的更新操作的partition定位於pod資源的最大索引號,即只放出一只金絲雀,由其測試第一輪的跟新操作,在確認無誤后通過修改partition屬性的值跟新其他的pod對象。

4)並行操作
Statefulset控制器管理的pod資源的策略除了默認的OrderedReady(順序創建及逆序刪除)外,還支持並行的創建及刪除操作,即同事創建所有的pod資源及同事刪除所有的pod資源,完成該操作需要將spec.podManageMentPolicy字段的值設置為Parallel。

4.  StatefulSet 刪除
StatefulSet支持級連刪除和非級連刪除,在非級連刪除模式下,僅刪除StatefulSet不刪除Pod,級連刪除則全部刪除。非級連刪除StatefulSet后,如果刪除Pod,就不會重新拉起原來的Pod,而是新建一個Pod。但是如果重新創建StatefulSet,則會對現有的Pod按照規則進行重新整理。一些分布式系統,並不希望按照順序來管理啟停Pod,因此在kubernetes 1.7版本之后,提供了.spec.podManagementPolicy這個參數,默認為OrderedReady,可以設置為Parallel這樣Pod的創建就不必等待,而是會同時創建、同時刪除。

5. 一個坑點
官方的文檔沒有創建PVC對應的PV,按照官方文檔操作創建后會遇到下面的錯誤:"pod has unbound PersistentVolumeClaims"!!
這就需要提前創建PV及PVC!官方文檔里解釋了,因為它所用的集群配置為動態提供PV,所以不用手工創建。PV的創建腳本如下:

kind: List
apiVersion: v1
items:
- apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: es-storage-pv-01
  spec:
    capacity:
      storage: 100Mi
    volumeMode: Filesystem
    accessModes: ["ReadWriteOnce"]
    persistentVolumeReclaimPolicy: Delete
    storageClassName: local-storage
    local:
      path: /home/es
    nodeAffinity:
      required:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - devops-102
            - devops-103
- apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: es-storage-pv-02
  spec:
    capacity:
      storage: 100Mi
    volumeMode: Filesystem
    accessModes: ["ReadWriteOnce"]
    persistentVolumeReclaimPolicy: Delete
    storageClassName: local-storage
    local:
      path: /home/es01
    nodeAffinity:
      required:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - devops-102
            - devops-103

Replication Controller,ReplicaSet,DaemonSet 都是持續運行pod,也就是pod執行結束或者手動刪除pod,這些控制器都會重啟pod,有些時候需要讓pod運行完成退出后不在重啟,並且保證pod如果在運行時異常退出了可以重啟的情況。這就需要用到job了。job管理的pod,正常完成退出后不重啟,當節點故障時,會按照replicaSet的pod方式,重新安排到一個新節點。也可以配置job,如果進程異常退出返回錯誤碼時,重啟容器。

六、Job
kubernetes從1.2版本開始支持批處理類型的應用,可以通過kubernetes Job資源對象來定義並啟動一個批處理任務。批處理任務通常並行(或串行)啟動多個計算進程去處理一批工作項(work item),處理完后,整個批處理任務結束。

Job是一次性任務,萬一運行卡住了,永遠不知道它的狀態,所以你可以限制它運行的時長。超過時長標記為失敗,這就需要用到pod中activeDeadlineSeconds 屬性來定義運行時長。同時可以定義job 中spec.backoffLimit配置job被標記為失敗之前可以重試的次數,默認為6!

運行一次性任務,容器中進程在正常結束后不會再重啟

單工作隊列,串行方式執行,某個時刻僅存在一個Pod資源對象。
多工作隊列,多隊列並行運行。

apiVersion: batch/v1
kind: Job
metadata:
  name: job-example
spec:
  template:
    spec:
      containers:
      - name: myjob
        image: alpine
        command: ["/bin/sh","-c","sleep 120"]
      # template下默認restartPolicy為Always 對Job不適用
      # 必須顯式指定為Never 或 OnFailure
      restartPolicy: Never

# kubectl get jobs.batch

Pod資源能以Job控制器名稱為標簽進行匹配
# kubectl get jobs -l job-name=job-example

並行式 Job
spec.parallelism 並行度屬性;spec.completion 總任務數
串行運行5次任務:spec.parallelism=1 spec.completion=5
並行2隊列5次任務:spec.parallelism=2 spec.completion=5

apiVersion: batch/v1
kind: Job
metadata:
  name: job-multi
spec:
  completions: 5
  parallelism: 1
  template:
    spec:
      containers:
      - name: myjob
        image: alpine
        command: ["/bin/sh","-c","sleep 20"]
      restartPolicy: OnFailure

# kubectl get pods -l job-name=job-multi -w

Job 擴容
spec.parallelism 並行度,即同時運行的Pod對象數,支持運行時調整,實現擴容和縮容
命令為: kubectl scale --replicas
# kubectl scale jobs job-multi --replicas=2 # 注意該命令即將廢除

刪除 Job
spec.activeDeadlineSeconds 指定最大活動時間,超出將被終止
spec.backoffLimit 默認6,將作業標記為失敗前的重試次數

Job創建后,立即會運行,但有些時候我們希望定時運行Job,這就需要用到kubernetes的CronJob資源。

七、CronJob
指定運行時間點及是否重復運行
指定時間點時,?* 都表示任何可用的有效值

kubectl explain cronjob.spec
jobTemplate Job    #控制器模板,必須
schedule obj         #運行時間點,必須
concurrencyPolicy string    #並發執行策略,值為 Allow、Forbid、Replace 定義前一次作業未執行完又遇到后一次作業的情形
failedJobHistoryLimit int     #為失敗任務保留的歷史記錄數,默認1
successfulJobsHistoryLimit int   #為成功任務保留的歷史記錄數,默認3
startingDeadlineSeconds int      #啟動作業錯誤的超時時長
suspend bool         #是否掛起后續的任務執行,默認false,對運行中作業沒有影響

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cronjob-example
  labels:
    app: mycronjob
spec:
  schedule: "*/2 * * * *"
  jobTemplate:
    metadata:
      labels:
        app: mycronjob-jobs
    spec:
      parallelism: 2
      template:
        spec:
          containers:
          - name: myjob
            image: alpine
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes clust

# kubectl get cronjobs.batch cronjob-example

CronJob 控制機制
# kubectl get jobs -l app=mycronjob-jobs #默認顯示3條歷史記錄

八、PDB Pod中斷預算
PodDisruptionBudget(簡稱為PDB)是Kubernetes用來限制可自願中斷的最大Pod副本數或確保最少可用Pod副本數,確保服務的高可用性;其中:
非自願中斷:由不可控外界因素導致的Pod中斷退出,如硬件或系統內核故障、網絡故障、節點資源不足導致的Pod被驅逐等;
自願中斷: 由用戶特地執行的管理操作導致的Pod中斷退出,如排空節點、人為刪除Pod對象、由更新觸發的Pod重建等;

每個應用程序都可創建一個對應的PDB對象以限制自願中斷時最大可以中斷的副本數或最少應該保持可用的副本數,從而保證自身高可用。
kubectl explain poddisruptionbudget.spec
selector obj       #標簽選擇器,一般與相關的Pod控制器使用同一個選擇器;
minAvailable string       #最少保證可用Pod對象數量或比例,要限止發生自願中斷,可設置為100%;
maxUnavailable string       #最多不可用狀態的Pod對象數量或比例,0表示不允許Pod對象進行自願中斷,此字段與minAvailable互斥;

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: myapp-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: myapp
kubectl get pods -l app=mya

# kubectl get pods -l app=myapp
# kubectl get pdb
# kubectl describe pdb myapp-pdb


免責聲明!

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



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