Pod(容器組)是 Kubernetes 中最小的調度單元,能夠經過 yaml 定義文件直接建立一個 Pod。但 Pod 自己並不具有自我恢復(self-healing)功能。若是一個 Pod 所在的節點出現故障,或者調度程序自身出現問題,以及節點資源不夠或節點進入維護而驅逐 Pod 時,Pod 將被刪除,且不能自我恢復。node
所以,Kubernetes 中咱們通常不直接建立 Pod, 而是經過 Controller(控制器)來管理 Pod。linux
Controller 能爲 Pod 提供以下特性:nginx
Kubernetes 中支持的控制器包括:shell
Kubernetes 中,雖然通常使用 Deployment 來管理 Pod, 但 Deployment 中也是經過 ReplicaSet 來維護 Pod 的副本集合的,所以此處也對 ReplicaSet 進行簡單介紹。api
在 ReplicaSet 的定義中,包含三部分:app
matchLabels
來與 Pod 的 label 匹配。ReplicaSet 的示例定義文檔以下所示,負載均衡
apiVersion: apps/v1 # api版本 kind: ReplicaSet # 資源類型 metadata: # 元數據定義 name: nginx-ds # ReplicaSet 名稱 spec: replicas: 2 # Pod 副本數量,默認1 selector: # 標籤選擇器 matchLabels: app: nginx template: # Pod 定義模板 metadata: # Pod 元數據定義 labels: app: nginx # Pod 標籤 spec: containers: # 容器定義 - name: nginx image: nginx
ReplicaSet 經過建立、刪除 Pod 容器組來確保符合 selector 選擇器的 Pod 數量等於 replicas 指定的數量。 ReplicaSet 建立的 Pod 中,都有一個字段 metadata.ownerReferences
用於標識該 Pod 從屬於哪個 ReplicaSet。可經過 kubectl get pod pod-name -o yaml
來查看 Pod 的 ownerReference。運維
ReplicaSet 經過 selector 字段的定義,識別哪些 Pod 應該由其管理, 不論該 Pod 是否由該 ReplicaSet 建立,即只要 selector 匹配, 經過外部定義建立的 Pod 也會被該 ReplicaSet 管理。所以須要注意 .spec.selector.matchLabels
與 .spec.template.metadata.labels
的定義一致, 且避免與其餘控制器的 selector 重合,形成混亂。ide
ReplicaSet 不支持滾動更新,因此對於無狀態應用,通常使用 Deployment來部署, 而不直接使用 ReplicaSet。ReplicaSet 主要是被用做 Deployment 中負責 Pod 建立、刪除、更新的一種手段。ui
Deployment 對象包含 ReplicaSet 做爲從屬對象,而且可經過聲明式、滾動更新的方式來更新 ReplicaSet 及其 Pod。ReplicaSet 如今主要是被用做 Deployment 中負責 Pod 建立、刪除、更新的一種手段。使用 Deployment 時,無需關心由 Deployment 建立的 ReplicaSet,Deployment 將處理全部與之相關的細節。同時,Deployment 還能以「聲明式」的方式管理 Pod 和 ReplicaSet (其本質是將一些特定場景的一系列運維步驟固化下來,以便快速準確無誤的執行),並提供版本(revision)回退功能。
Deployment 定義示例,
apiVersion: apps/v1 kind: Deployment # 對象類型,固定爲 Deployment metadata: name: nginx-deploy # Deployment 名稱 namespace: default # 命名空間,默認爲 default labels: app: nginx # 標籤 spec: replicas: 4 # Pod 副本數,默認1 strategy: rollingUpdate: # 升級策略爲滾動升級,因爲replicas爲4,則整個升級過程pod個數在3-5個之間 maxSurge: 1 # 滾動升級時超過 replicas 的最大 pod 數,也能夠爲百分比(replicas的百分比),默認爲1 maxUnavailable: 1 # 滾動升級時不可用的最大 pod 數,也可爲百分比(replicas的百分比),默認爲1 selector: # 標籤選擇器,經過標籤選擇該 Deployment 管理的 Pod matchLabels: app: nginx template: # Pod 定義模板 metadata: labels: app: nginx # Pod 標籤 spec: # 定義容器模板,能夠包含多個容器 containers: - name: nginx image: nginx:latest ports: - containerPort: 80
可經過 kubectl explain xxx
來查看支持哪些配置選項,
# 查看 deployment 配置項 [root@kmaster ~]# kubectl explain deployment ... # 查看 deployment.spec 模塊的配置項 [root@kmaster ~]# kubectl explain deployment.spec KIND: Deployment VERSION: apps/v1 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. 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. Defaults to 600s. 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. Defaults to 10. selector <Object> -required- Label selector for pods. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment. It must match the pod template's labels. strategy <Object> The deployment strategy to use to replace existing pods with new ones. template <Object> -required-
其它配置項說明:
.spec.minReadySeconds
:用來控制應用升級的速度。升級過程當中,新建立的 Pod 一旦成功響應了就緒探測即被認爲是可用狀態,而後進行下一輪的替換。 .spec.minReadySeconds
定義了在新的 Pod 對象建立後至少須要等待多長的時間才能會被認爲其就緒,在該段時間內,更新操做會被阻塞。.spec.progressDeadlineSeconds
:用來指定在系統報告 Deployment 失敗 —— 表現爲狀態中的 type=Progressing、Status=False、 Reason=ProgressDeadlineExceeded
前能夠等待的 Deployment 進行的秒數。Deployment controller 會繼續重試該 Deployment。若是設置該參數,該值必須大於 .spec.minReadySeconds
。.spec.revisionHistoryLimit
:用來指定能夠保留的舊的 ReplicaSet 或 revision(版本) 的數量。默認全部舊的 Replicaset 都會被保留。若是刪除了一箇舊的 RepelicaSet,則 Deployment 將沒法再回退到那個 revison。若是將該值設置爲0,全部具備0個 Pod 副本的 ReplicaSet 都會被刪除,這時候 Deployment 將沒法回退,由於 revision history 都被清理掉了。[root@kmaster test]# kubectl apply -f nginx-deploy.yaml --record
--record
會將這次命令寫入 Deployment 的 kubernetes.io/change-cause 註解中。可在後面查看某一個 Deployment 版本變化的緣由。
建立 Deployment 後,Deployment 控制器將馬上建立一個 ReplicaSet,並由 ReplicaSet 建立所須要的 Pod。
# 查看 Deployment [root@kmaster test]# kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE nginx-deploy 0/2 2 0 64s # 查看 ReplicaSet [root@kmaster test]# kubectl get rs NAME DESIRED CURRENT READY AGE nginx-deploy-59c9f8dff 2 2 1 2m16s # 查看 Pod,顯示調度的節點,及標籤 [root@kmaster test]# kubectl get pod -o wide --show-labels NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS nginx-deploy-59c9f8dff-47bgd 1/1 Running 0 5m14s 10.244.1.91 knode2 <none> <none> app=nginx,pod-template-hash=59c9f8dff nginx-deploy-59c9f8dff-q4zb8 1/1 Running 0 5m14s 10.244.3.47 knode3 <none> <none> app=nginx,pod-template-hash=59c9f8dff
pod-template-hash
標籤是 Deployment 建立 ReplicaSet 時添加到 ReplicaSet 上的,ReplicaSet 進而將此標籤添加到 Pod 上。這個標籤用於區分 Deployment 中哪一個 ReplicaSet 建立了哪些 Pod。該標籤的值是 .spec.template
的 hash 值,不要去修改這個標籤。由上可看出 ReplicaSet、 Pod 的命名分別遵循 <Deployment-name>-<Pod-template-hash>
、<Deployment-name>-<Pod-template-hash>-xxx
的格式。
當且僅當 Deployment 的 Pod template(.spec.template
)字段中的內容發生變動時(例如標籤或容器的鏡像被改變),Deployment 的發佈更新(rollout)纔會被觸發。Deployment 中其餘字段的變化(例如修改 .spec.replicas
字段)將不會觸發 Deployment 的發佈更新。
更新 Deployment 中 Pod 的定義(例如,發佈新版本的容器鏡像)。此時 Deployment 控制器將爲該 Deployment 建立一個新的 ReplicaSet,而且逐步在新的 ReplicaSet 中建立 Pod,在舊的 ReplicaSet 中刪除 Pod,以達到滾動更新的效果。
好比咱們將上面 Deployment 的容器鏡像進行修改,
# 方式一:直接使用 kubectl 命令設置修改 [root@kmaster ~]# kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record deployment.apps/nginx-deploy image updated # 方式二:使用 kubectl edit 編輯yaml修改 [root@kmaster ~]# kubectl edit deploy nginx-deploy
查看發佈更新(rollout)的狀態
[root@kmaster ~]# kubectl rollout status deploy nginx-deploy Waiting for deployment "nginx-deploy" rollout to finish: 2 out of 4 new replicas have been updated...
查看 ReplicaSet,
[root@kmaster ~]# kubectl get rs NAME DESIRED CURRENT READY AGE nginx-deploy-59c9f8dff 1 1 1 3d6h nginx-deploy-d47dbbb7c 4 4 2 3m41s
咱們能夠看到 Deployment 的更新是經過建立一個新的4個副本的 ReplicaSet,並同時將舊的 ReplicaSet 的副本數縮容到0個副原本達成的。
由於前面咱們將 maxSurge, 與 maxUnavailable 都設置爲了1, 所以在更新的過程當中,任什麼時候刻兩個 ReplicaSet 的 Pod 數至多爲5個(4 replicas +1 maxSurge),且可用的 Pod 數至少爲3個(4 replicas - 1 maxUnavailable)。
使用 kubectl describe
命令查看 Deployment 的事件部分,以下所示
[root@kmaster ~]# kubectl describe deploy nginx-deploy ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 12m deployment-controller Scaled up replica set nginx-deploy-d47dbbb7c to 1 Normal ScalingReplicaSet 12m deployment-controller Scaled down replica set nginx-deploy-59c9f8dff to 3 Normal ScalingReplicaSet 12m deployment-controller Scaled up replica set nginx-deploy-d47dbbb7c to 2 Normal ScalingReplicaSet 10m deployment-controller Scaled down replica set nginx-deploy-59c9f8dff to 2 Normal ScalingReplicaSet 10m deployment-controller Scaled up replica set nginx-deploy-d47dbbb7c to 3 Normal ScalingReplicaSet 8m56s deployment-controller Scaled down replica set nginx-deploy-59c9f8dff to 1 Normal ScalingReplicaSet 8m56s deployment-controller Scaled up replica set nginx-deploy-d47dbbb7c to 4 Normal ScalingReplicaSet 5m55s deployment-controller Scaled down replica set nginx-deploy-59c9f8dff to 0
當更新了 Deployment 的 Pod Template 時,Deployment Controller 會建立一個新的 ReplicaSet (nginx-deploy-d47dbbb7c) ,並將其 scale up 到 1 個副本,同時將舊的 ReplicaSet(nginx-deploy-59c9f8dff) scale down 到3個副本。接下來 Deployment Controller 繼續 scale up 新的 ReplicaSet 並 scale down 舊的 ReplicaSet,直到新的 ReplicaSet 擁有 replicas 個數的 Pod, 舊的 ReplicaSet Pod 數縮放到0。這個過程稱爲 rollout(發佈更新)。
經過 .spec.strategy
字段,能夠指定更新策略,除了上述使用的 RollingUpdate(滾動更新),另外一個可取的值爲 Recreate(從新建立)。選擇從新建立,Deployment 將先刪除原有 ReplicaSet 中的全部 Pod,而後再建立新的 ReplicaSet 和新的 Pod,更新過程當中將出現一段應用程序不可用的狀況。所以,線上環境通常使用 RollingUpdate。
默認狀況下,kubernetes 將保存 Deployment 的全部更新(rollout)歷史。能夠經過設定 revision history limit(.spec.revisionHistoryLimit
配置項)來指定保存的歷史版本數量。
當且僅當 Deployment 的 .spec.template
字段被修改時(例如修改容器的鏡像),kubernetes 才爲其建立一個 Deployment revision(版本)。Deployment 的其餘更新(例如:修改 .spec.replicas
字段)將不會建立新的 Deployment revision(版本)。
查看 Deployment 的 revision,
[root@kmaster ~]# kubectl rollout history deploy nginx-deploy deployment.apps/nginx-deploy REVISION CHANGE-CAUSE 1 kubectl apply --filename=nginx-deploy.yaml --record=true 2 kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record=true
若是前面更新 Deployment 時沒有添加 --record=true
,則此處 CHANGE-CAUSE 將爲空。
咱們經過將鏡像修改成一個不存在的版原本模擬一次失敗的更新,並回滾到前一個版本的場景,
# 1. 修改鏡像版本到一個不存在的值 [root@kmaster ~]# kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record deployment.apps/nginx-deploy image updated # 2. 查看 ReplicaSet [root@kmaster ~]# kubectl get rs NAME DESIRED CURRENT READY AGE nginx-deploy-58f69cfc57 2 2 0 2m7s nginx-deploy-59c9f8dff 0 0 0 3d7h nginx-deploy-d47dbbb7c 3 3 3 81m # 3. 查看 Pod 狀態 [root@kmaster ~]# kubect get pod NAME READY STATUS RESTARTS AGE nginx-deploy-58f69cfc57-5968g 0/1 ContainerCreating 0 42s nginx-deploy-58f69cfc57-tk7c5 0/1 ErrImagePull 0 42s nginx-deploy-d47dbbb7c-2chgx 1/1 Running 0 77m nginx-deploy-d47dbbb7c-8fcb9 1/1 Running 0 80m nginx-deploy-d47dbbb7c-gnwjj 1/1 Running 0 78m # 4. 查看 Deployment 詳情 [root@kmaster ~]# kubectl describe deploy nginx-deploy ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 3m57s deployment-controller Scaled up replica set nginx-deploy-58f69cfc57 to 1 Normal ScalingReplicaSet 3m57s deployment-controller Scaled down replica set nginx-deploy-d47dbbb7c to 3 Normal ScalingReplicaSet 3m57s deployment-controller Scaled up replica set nginx-deploy-58f69cfc57 to 2 # 5. 查看 Deployment 的歷史版本 [root@kmaster ~]# kubectl rollout history deploy nginx-deploy deployment.apps/nginx-deploy REVISION CHANGE-CAUSE 1 kubectl apply --filename=nginx-deploy.yaml --record=true 2 kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record=true 3 kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record=true # 6. 查看某個版本的詳情 [root@kmaster ~]# kubectl rollout history deploy nginx-deploy --revision=3 deployment.apps/nginx-deploy with revision #3 Pod Template: Labels: app=nginx pod-template-hash=58f69cfc57 Annotations: kubernetes.io/change-cause: kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record=true Containers: nginx: Image: nginx:1.161 Port: 80/TCP Host Port: 0/TCP Environment: <none> Mounts: <none> Volumes: <none> # 7. 回滾到前一個版本 [root@kmaster ~]# kubectl rollout undo deploy nginx-deploy deployment.apps/nginx-deploy rolled back # 8. 回滾到指定的版本 [root@kmaster ~]# kubectl rollout undo deploy nginx-deploy --to-revision=1 deployment.apps/nginx-deploy rolled back # 9. 查看歷史版本信息 [root@kmaster ~]# kubectl rollout history deploy nginx-deploy deployment.apps/nginx-deploy REVISION CHANGE-CAUSE 3 kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record=true 4 kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record=true 5 kubectl apply --filename=nginx-deploy.yaml --record=true
經過 kubectl rollout undo
命令可回滾到上一個版本或指定的版本,上述示例也可看出,回滾到歷史版本,會將歷史版本的序號設置爲最新序號。如前所述,咱們能夠經過設置 Deployment 的 .spec.revisionHistoryLimit
來指定保留多少箇舊的 ReplicaSet(或 revision),超出該數字的將在後臺進行垃圾回收。若是該字段被設爲 0,Kubernetes 將清理掉該 Deployment 的全部歷史版本(revision),此時,將沒法對該 Deployment 執行回滾操做了。
能夠經過 kubectl scale
命令或 kubectl edit
修改定義的方式來對 Deployment 進行伸縮,增長或減小 Pod 的副本數,
# 將 Pod 數縮放到2個 [root@kmaster ~]# kubectl scale deploy nginx-deploy --replicas=2 deployment.apps/nginx-deploy scaled # 查看 Pod [root@kmaster ~]# kubectl get pod NAME READY STATUS RESTARTS AGE nginx-deploy-59c9f8dff-7bpjp 1/1 Running 0 9m48s nginx-deploy-59c9f8dff-tpxzf 0/1 Terminating 0 8m57s nginx-deploy-59c9f8dff-v8fgz 0/1 Terminating 0 10m nginx-deploy-59c9f8dff-w8s9z 1/1 Running 0 10m # 查看 ReplicaSet,DESIRED 變爲2了 [root@kmaster ~]# kubectl get rs NAME DESIRED CURRENT READY AGE nginx-deploy-58f69cfc57 0 0 0 22m nginx-deploy-59c9f8dff 2 2 2 3d8h nginx-deploy-d47dbbb7c 0 0 0 102m
若是集羣啓用了自動伸縮(HPA —— Horizontal Pod Autoscaling),則能夠基於 CPU、 內存的使用率在一個最大和最小的區間對 Deployment 實現自動伸縮,
# 建立一個 HPA [root@kmaster ~]# kubectl autoscale deploy nginx-deploy --min=2 --max=4 --cpu-percent=80 horizontalpodautoscaler.autoscaling/nginx-deploy autoscaled # 查看 HPA [root@kmaster ~]# kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE nginx-deploy Deployment/nginx-deploy <unknown>/80% 2 4 2 16s # 刪除 HPA [root@kmaster ~]# kubectl delete hpa nginx-deploy horizontalpodautoscaler.autoscaling "nginx-deploy" deleted
咱們能夠將一個 Deployment 暫停(pause),而後在它上面作一個或多個更新,此時 Deployment 並不會觸發更新,只有再恢復(resume)該 Deployment,纔會執行該時間段內的全部更新。這種作法能夠在暫停和恢復中間對 Deployment 作屢次更新,而不會觸發沒必要要的滾動更新。
# 1. 暫停 Deployment [root@kmaster ~]# kubectl rollout pause deploy nginx-deploy deployment.apps/nginx-deploy paused # 2. 更新容器鏡像 [root@kmaster ~]# kubectl set image deploy nginx-deploy nginx=nginx:1.9.1 --record deployment.apps/nginx-deploy image updated # 3. 查看版本歷史, 此時並無觸發更新 [root@kmaster ~]# kubectl rollout history deploy nginx-deploy deployment.apps/nginx-deploy REVISION CHANGE-CAUSE 3 kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record=true 4 kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record=true 5 kubectl apply --filename=nginx-deploy.yaml --record=true # 4. 更新 Resource 限制,一樣並不會觸發更新 [root@kmaster ~]# kubectl set resources deploy nginx-deploy -c=nginx --limits=memory=512Mi,cpu=500m deployment.apps/nginx-deploy resource requirements updated # 5. 查看修改,Pod 定義已被更新 [root@kmaster ~]# kubectl describe deploy nginx-deploy Pod Template: Labels: app=nginx Containers: nginx: Image: nginx:1.9.1 Port: 80/TCP Host Port: 0/TCP Limits: cpu: 500m memory: 512Mi # 6. 恢復 Deployment [root@kmaster ~]# kubectl rollout resume deploy nginx-deploy deployment.apps/nginx-deploy resumed # 7. 查看版本歷史,可見兩次修改只作了一次 rollout [root@kmaster ~]# kubectl rollout history deploy nginx-deploy deployment.apps/nginx-deploy REVISION CHANGE-CAUSE 3 kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record=true 4 kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record=true 5 kubectl apply --filename=nginx-deploy.yaml --record=true 6 kubectl set image deploy nginx-deploy nginx=nginx:1.9.1 --record=true
在更新容器鏡像時,由於 Deployment 處於暫停狀態,因此並不會生成新的版本(Revision),當 Deployment 恢復時,纔將這段時間的更新生效,執行滾動更新,生成新的版本。在暫停中的 Deployment 上作的更新, 由於沒有生成版本,所以也不能回滾(rollback)。也不能對處於暫停狀態的 Deployment 執行回滾操做,只有在恢復(Resume)以後才能執行回滾操做。
金絲雀發佈也叫灰度發佈。當咱們須要發佈新版本時,能夠針對新版本新建一個 Deployment,與舊版本的 Deployment 同時掛在一個 Service 下(經過 label match), 經過 Service 的負載均衡將用戶請求流量分發到新版 Deployment 的 Pod 上,觀察新版運行狀況,若是沒有問題再將舊版 Deployment 的版本更新到新版完成滾動更新,最後刪除新建的 Deployment。很明顯這種金絲雀發佈具備必定的侷限性,沒法根據用戶或地域來分流,若是要更充分地實現金絲雀發佈,則可能須要引入 Istio 等。
金絲雀發佈名稱的由來: 之前,曠工在下礦洞時面臨的一個重要危險是礦井中的毒氣,他們想到一個辦法來辨別礦井中是否有毒氣,礦工們隨身攜帶一隻金絲雀下礦井,金絲雀對毒氣的抵抗能力比人類要弱,在毒氣環境下會先掛掉從而起到預警的做用。它背後的原理是:用較小的代價試錯,即便出現了嚴重的錯誤(出現了毒氣),系統整體的損失也是可承受的或者是很是小的(失去了一隻金絲雀)。
Kubernetes 中最小的調度單元是 Pod, 負載建立 Pod 並控制其按必定的副本數運行的是 ReplicaSet, 而 Deployment 能夠以「聲明式」的方式來管理 Pod 和 ReplicaSet,並提供滾動更新與版本(revision)回退功能。因此,通常使用 Deployment 來部署應用, 而不直接操做 ReplicaSet 或 Pod。
[轉載請註明出處]
做者:雨歌
歡迎關注做者公衆號:半路雨歌,查看更多技術乾貨文章