# Deployment
## 簡述
Deployment 為 Pod 和 ReplicaSet 提供了一個聲明式定義(declarative)方法,用來替代以前的ReplicationController 來方便的管理應用。典型的應用場景包括:
- 定義Deployment來創建Pod和ReplicaSet
- 滾動升級和回滾應用
- 擴容和縮容
- 暫停和繼續Deployment
比如一個簡單的nginx應用可以定義為
```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
```
擴容:
```
kubectl scale deployment nginx-deployment --replicas 10
```
如果集群支持 horizontal pod autoscaling 的話,還可以為Deployment設置自動擴展:
```
kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80
```
更新鏡像也比較簡單:
```
kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
```
回滾:
```
kubectl rollout undo deployment/nginx-deployment
```
## Deployment 結構示意圖

## Deployment 概念詳細解析
本文翻譯自kubernetes官方文檔:https://kubernetes.io/docs/concepts/workloads/controllers/deployment
根據2017年5月10日的Commit 8481c02 翻譯。
## Deployment 是什么?
Deployment為Pod和Replica Set(下一代Replication Controller)提供聲明式更新。
您只需要在 Deployment 中描述您想要的目標狀態是什么,Deployment controller 就會幫您將 Pod 和ReplicaSet 的實際狀態改變到您的目標狀態。您可以定義一個全新的 Deployment 來創建 ReplicaSet 或者刪除已有的 Deployment 并創建一個新的來替換。
**注意**:您不該手動管理由 Deployment 創建的 ReplicaSet,否則您就篡越了 Deployment controller 的職責!下文羅列了 Deployment 對象中已經覆蓋了所有的用例。如果未有覆蓋您所有需要的用例,請直接在 Kubernetes 的代碼庫中提 issue。
典型的用例如下:
- 使用Deployment來創建ReplicaSet。ReplicaSet在后臺創建pod。檢查啟動狀態,看它是成功還是失敗。
- 然后,通過更新Deployment的PodTemplateSpec字段來聲明Pod的新狀態。這會創建一個新的ReplicaSet,Deployment會按照控制的速率將pod從舊的ReplicaSet移動到新的ReplicaSet中。
- 如果當前狀態不穩定,回滾到之前的Deployment revision。每次回滾都會更新Deployment的revision。
- 擴容Deployment以滿足更高的負載。
- 暫停Deployment來應用PodTemplateSpec的多個修復,然后恢復上線。
- 根據Deployment 的狀態判斷上線是否hang住了。
- 清除舊的不必要的 ReplicaSet。
## 創建 Deployment
下面是一個 Deployment 示例,它創建了一個 ReplicaSet 來啟動3個 nginx pod。
下載示例文件并執行命令:
```bash
$ kubectl create -f https://kubernetes.io/docs/user-guide/nginx-deployment.yaml --record
deployment "nginx-deployment" created
```
將kubectl的 `--record` 的 flag 設置為 `true`可以在 annotation 中記錄當前命令創建或者升級了該資源。這在未來會很有用,例如,查看在每個 Deployment revision 中執行了哪些命令。
然后立即執行 `get` 將獲得如下結果:
```bash
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 0 0 0 1s
```
輸出結果表明我們希望的repalica數是3(根據deployment中的`.spec.replicas`配置)當前replica數( `.status.replicas`)是0, 最新的replica數(`.status.updatedReplicas`)是0,可用的replica數(`.status.availableReplicas`)是0。
過幾秒后再執行`get`命令,將獲得如下輸出:
```bash
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 18s
```
我們可以看到Deployment已經創建了3個 replica,所有的 replica 都已經是最新的了(包含最新的pod template),可用的(根據Deployment中的`.spec.minReadySeconds`聲明,處于已就緒狀態的pod的最少個數)。執行`kubectl get rs`和`kubectl get pods`會顯示Replica Set(RS)和Pod已創建。
```bash
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-2035384211 3 3 0 18s
```
您可能會注意到 ReplicaSet 的名字總是`<Deployment的名字>-<pod template的hash值>`。
```bash
$ kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-deployment-2035384211-7ci7o 1/1 Running 0 18s app=nginx,pod-template-hash=2035384211
nginx-deployment-2035384211-kzszj 1/1 Running 0 18s app=nginx,pod-template-hash=2035384211
nginx-deployment-2035384211-qqcnn 1/1 Running 0 18s app=nginx,pod-template-hash=2035384211
```
剛創建的Replica Set將保證總是有3個 nginx 的 pod 存在。
**注意:** 您必須在 Deployment 中的 selector 指定正確的 pod template label(在該示例中是 `app = nginx`),不要跟其他的 controller 的 selector 中指定的 pod template label 搞混了(包括 Deployment、Replica Set、Replication Controller 等)。**Kubernetes 本身并不會阻止您任意指定 pod template label **,但是如果您真的這么做了,這些 controller 之間會相互打架,并可能導致不正確的行為。
### Pod-template-hash label
**注意**:這個 label 不是用戶指定的!
注意上面示例輸出中的 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 上。
## 更新Deployment
**注意:** Deployment 的 rollout 當且僅當 Deployment 的 pod template(例如`.spec.template`)中的label更新或者鏡像更改時被觸發。其他更新,例如擴容Deployment不會觸發 rollout。
假如我們現在想要讓 nginx pod 使用`nginx:1.9.1`的鏡像來代替原來的`nginx:1.7.9`的鏡像。
```bash
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
deployment "nginx-deployment" image updated
```
我們可以使用`edit`命令來編輯 Deployment,修改 `.spec.template.spec.containers[0].image` ,將`nginx:1.7.9` 改寫成 `nginx:1.9.1`。
```bash
$ kubectl edit deployment/nginx-deployment
deployment "nginx-deployment" edited
```
查看 rollout 的狀態,只要執行:
```bash
$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment "nginx-deployment" successfully rolled out
```
Rollout 成功后,`get` Deployment:
```bash
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 36s
```
UP-TO-DATE 的 replica 的數目已經達到了配置中要求的數目。
CURRENT 的 replica 數表示 Deployment 管理的 replica 數量,AVAILABLE 的 replica 數是當前可用的replica數量。
我們通過執行`kubectl get rs`可以看到 Deployment 更新了Pod,通過創建一個新的 ReplicaSet 并擴容了3個 replica,同時將原來的 ReplicaSet 縮容到了0個 replica。
```bash
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-1564180365 3 3 0 6s
nginx-deployment-2035384211 0 0 0 36s
```
執行 `get pods`只會看到當前的新的 pod:
```bash
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-1564180365-khku8 1/1 Running 0 14s
nginx-deployment-1564180365-nacti 1/1 Running 0 14s
nginx-deployment-1564180365-z9gth 1/1 Running 0 14s
```
下次更新這些 pod 的時候,只需要更新 Deployment 中的 pod 的 template 即可。
Deployment 可以保證在升級時只有一定數量的 Pod 是 down 的。默認的,它會確保至少有比期望的Pod數量少一個是up狀態(最多一個不可用)。
Deployment 同時也可以確保只創建出超過期望數量的一定數量的 Pod。默認的,它會確保最多比期望的Pod數量多一個的 Pod 是 up 的(最多1個 surge )。
**在未來的 Kuberentes 版本中,將從1-1變成25%-25%。**
例如,如果您自己看下上面的 Deployment,您會發現,開始創建一個新的 Pod,然后刪除一些舊的 Pod 再創建一個新的。當新的Pod創建出來之前不會殺掉舊的Pod。這樣能夠確保可用的 Pod 數量至少有2個,Pod的總數最多4個。
```bash
$ kubectl describe deployments
Name: nginx-deployment
Namespace: default
CreationTimestamp: Tue, 15 Mar 2016 12:01:06 -0700
Labels: app=nginx
Selector: app=nginx
Replicas: 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-1564180365 (3/3 replicas created)
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
36s 36s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-2035384211 to 3
23s 23s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 1
23s 23s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 2
23s 23s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 2
21s 21s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 0
21s 21s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 3
```
我們可以看到當我們剛開始創建這個 Deployment 的時候,創建了一個 ReplicaSet(nginx-deployment-2035384211),并直接擴容到了3個 replica。
當我們更新這個 Deployment 的時候,它會創建一個新的 ReplicaSet(nginx-deployment-1564180365),將它擴容到1個replica,然后縮容原先的 ReplicaSet 到2個 replica,此時滿足至少2個 Pod 是可用狀態,同一時刻最多有4個 Pod 處于創建的狀態。
接著繼續使用相同的 rolling update 策略擴容新的 ReplicaSet 和縮容舊的 ReplicaSet。最終,將會在新的 ReplicaSet 中有3個可用的 replica,舊的 ReplicaSet 的 replica 數目變成0。
### Rollover(多個rollout并行)
每當 Deployment controller 觀測到有新的 deployment 被創建時,如果沒有已存在的 ReplicaSet 來創建期望個數的 Pod 的話,就會創建出一個新的 ReplicaSet 來做這件事。已存在的 ReplicaSet 控制 label 與`.spec.selector`匹配但是 template 跟`.spec.template`不匹配的 Pod 縮容。最終,新的 ReplicaSet 將會擴容出`.spec.replicas`指定數目的 Pod,舊的 ReplicaSet 會縮容到0。
如果您更新了一個的已存在并正在進行中的 Deployment,每次更新 Deployment都會創建一個新的 ReplicaSet并擴容它,同時回滾之前擴容的 ReplicaSet ——將它添加到舊的 ReplicaSet 列表中,開始縮容。
例如,假如您創建了一個有5個`niginx:1.7.9` replica的 Deployment,但是當還只有3個`nginx:1.7.9`的 replica 創建出來的時候您就開始更新含有5個`nginx:1.9.1` replica 的 Deployment。在這種情況下,Deployment 會立即殺掉已創建的3個`nginx:1.7.9`的 Pod,并開始創建`nginx:1.9.1`的 Pod。它不會等到所有的5個`nginx:1.7.9`的 Pod 都創建完成后才開始改變航道。
### Label selector 更新
我們通常不鼓勵更新 label selector,我們建議事先規劃好您的 selector。
任何情況下,只要您想要執行 label selector 的更新,請一定要謹慎并確認您已經預料到所有可能因此導致的后果。
- 增添 selector 需要同時在 Deployment 的 spec 中更新新的 label,否則將返回校驗錯誤。此更改是不可覆蓋的,這意味著新的 selector 不會選擇使用舊 selector 創建的 ReplicaSet 和 Pod,從而導致所有舊版本的 ReplicaSet 都被丟棄,并創建新的 ReplicaSet。
- 更新 selector,即更改 selector key 的當前值,將導致跟增添 selector 同樣的后果。
- 刪除 selector,即刪除 Deployment selector 中的已有的 key,不需要對 Pod template label 做任何更改,現有的 ReplicaSet 也不會成為孤兒,但是請注意,刪除的 label 仍然存在于現有的 Pod 和 ReplicaSet 中。
## 回退Deployment
有時候您可能想回退一個 Deployment,例如,當 Deployment 不穩定時,比如一直 crash looping。
默認情況下,kubernetes 會在系統中保存前兩次的 Deployment 的 rollout 歷史記錄,以便您可以隨時回退(您可以修改`revision history limit`來更改保存的revision數)。
**注意:** 只要 Deployment 的 rollout 被觸發就會創建一個 revision。也就是說當且僅當 Deployment 的 Pod template(如`.spec.template`)被更改,例如更新template 中的 label 和容器鏡像時,就會創建出一個新的 revision。
其他的更新,比如擴容 Deployment 不會創建 revision——因此我們可以很方便的手動或者自動擴容。這意味著當您回退到歷史 revision 時,只有 Deployment 中的 Pod template 部分才會回退。
假設我們在更新 Deployment 的時候犯了一個拼寫錯誤,將鏡像的名字寫成了`nginx:1.91`,而正確的名字應該是`nginx:1.9.1`:
```bash
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.91
deployment "nginx-deployment" image updated
```
Rollout 將會卡住。
```bash
$ kubectl rollout status deployments nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
```
按住 Ctrl-C 停止上面的 rollout 狀態監控。
您會看到舊的 replica(nginx-deployment-1564180365 和 nginx-deployment-2035384211)和新的 replica (nginx-deployment-3066724191)數目都是2個。
```bash
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-1564180365 2 2 0 25s
nginx-deployment-2035384211 0 0 0 36s
nginx-deployment-3066724191 2 2 2 6s
```
看下創建 Pod,您會看到有兩個新的 ReplicaSet 創建的 Pod 處于 ImagePullBackOff 狀態,循環拉取鏡像。
```bash
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-1564180365-70iae 1/1 Running 0 25s
nginx-deployment-1564180365-jbqqo 1/1 Running 0 25s
nginx-deployment-3066724191-08mng 0/1 ImagePullBackOff 0 6s
nginx-deployment-3066724191-eocby 0/1 ImagePullBackOff 0 6s
```
注意,Deployment controller會自動停止壞的 rollout,并停止擴容新的 ReplicaSet。
```bash
$ kubectl describe deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Tue, 15 Mar 2016 14:48:04 -0700
Labels: app=nginx
Selector: app=nginx
Replicas: 2 updated | 3 total | 2 available | 2 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
OldReplicaSets: nginx-deployment-1564180365 (2/2 replicas created)
NewReplicaSet: nginx-deployment-3066724191 (2/2 replicas created)
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-2035384211 to 3
22s 22s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 1
22s 22s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 2
22s 22s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 2
21s 21s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 0
21s 21s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 3
13s 13s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-3066724191 to 1
13s 13s 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-1564180365 to 2
13s 13s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-3066724191 to 2
```
為了修復這個問題,我們需要回退到穩定的 Deployment revision。
### 檢查 Deployment 升級的歷史記錄
首先,檢查下 Deployment 的 revision:
```bash
$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment":
REVISION CHANGE-CAUSE
1 kubectl create -f https://kubernetes.io/docs/user-guide/nginx-deployment.yaml--record
2 kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
3 kubectl set image deployment/nginx-deployment nginx=nginx:1.91
```
因為我們創建 Deployment 的時候使用了`--record`參數可以記錄命令,我們可以很方便的查看每次 revision 的變化。
查看單個revision 的詳細信息:
```bash
$ kubectl rollout history deployment/nginx-deployment --revision=2
deployments "nginx-deployment" revision 2
Labels: app=nginx
pod-template-hash=1159050644
Annotations: kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
Containers:
nginx:
Image: nginx:1.9.1
Port: 80/TCP
QoS Tier:
cpu: BestEffort
memory: BestEffort
Environment Variables: <none>
No volumes.
```
### 回退到歷史版本
現在,我們可以決定回退當前的 rollout 到之前的版本:
```bash
$ kubectl rollout undo deployment/nginx-deployment
deployment "nginx-deployment" rolled back
```
也可以使用 `--revision`參數指定某個歷史版本:
```bash
$ kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment "nginx-deployment" rolled back
```
該 Deployment 現在已經回退到了先前的穩定版本。如您所見,Deployment controller產生了一個回退到revison 2的`DeploymentRollback`的 event。
```bash
$ kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 30m
$ kubectl describe deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Tue, 15 Mar 2016 14:48:04 -0700
Labels: app=nginx
Selector: app=nginx
Replicas: 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-1564180365 (3/3 replicas created)
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
30m 30m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-2035384211 to 3
29m 29m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 1
29m 29m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 2
29m 29m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 2
29m 29m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-2035384211 to 0
29m 29m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-3066724191 to 2
29m 29m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-3066724191 to 1
29m 29m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-1564180365 to 2
2m 2m 1 {deployment-controller } Normal ScalingReplicaSet Scaled down replica set nginx-deployment-3066724191 to 0
2m 2m 1 {deployment-controller } Normal DeploymentRollback Rolled back deployment "nginx-deployment" to revision 2
29m 2m 2 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-1564180365 to 3
```
### 清理 Policy
您可以通過設置`.spec.revisonHistoryLimit`項來指定 deployment 最多保留多少 revision 歷史記錄。默認的會保留所有的 revision;如果將該項設置為0,Deployment就不允許回退了。
## Deployment 擴容
您可以使用以下命令擴容 Deployment:
```bash
$ kubectl scale deployment nginx-deployment --replicas 10
deployment "nginx-deployment" scaled
```
假設您的集群中啟用了[horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough),您可以給 Deployment 設置一個 autoscaler,基于當前 Pod的 CPU 利用率選擇最少和最多的 Pod 數。
```bash
$ kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80
deployment "nginx-deployment" autoscaled
```
### 比例擴容
RollingUpdate Deployment 支持同時運行一個應用的多個版本。或者 autoscaler 擴 容 RollingUpdate Deployment 的時候,正在中途的 rollout(進行中或者已經暫停的),為了降低風險,Deployment controller 將會平衡已存在的活動中的 ReplicaSet(有 Pod 的 ReplicaSet)和新加入的 replica。這被稱為比例擴容。
例如,您正在運行中含有10個 replica 的 Deployment。maxSurge=3,maxUnavailable=2。
```bash
$ kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 10 10 10 10 50s
```
您更新了一個鏡像,而在集群內部無法解析。
```bash
$ kubectl set image deploy/nginx-deployment nginx=nginx:sometag
deployment "nginx-deployment" image updated
```
鏡像更新啟動了一個包含ReplicaSet nginx-deployment-1989198191的新的rollout,但是它被阻塞了,因為我們上面提到的maxUnavailable。
```bash
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-1989198191 5 5 0 9s
nginx-deployment-618515232 8 8 8 1m
```
然后發起了一個新的Deployment擴容請求。autoscaler將Deployment的repllica數目增加到了15個。Deployment controller需要判斷在哪里增加這5個新的replica。如果我們沒有誰用比例擴容,所有的5個replica都會加到一個新的ReplicaSet中。如果使用比例擴容,新添加的replica將傳播到所有的ReplicaSet中。大的部分加入replica數最多的ReplicaSet中,小的部分加入到replica數少的ReplciaSet中。0個replica的ReplicaSet不會被擴容。
在我們上面的例子中,3個replica將添加到舊的ReplicaSet中,2個replica將添加到新的ReplicaSet中。rollout進程最終會將所有的replica移動到新的ReplicaSet中,假設新的replica成為健康狀態。
```bash
$ kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 15 18 7 8 7m
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-1989198191 7 7 0 7m
nginx-deployment-618515232 11 11 11 7m
```
## 暫停和恢復Deployment
您可以在發出一次或多次更新前暫停一個 Deployment,然后再恢復它。這樣您就能多次暫停和恢復 Deployment,在此期間進行一些修復工作,而不會發出不必要的 rollout。
例如使用剛剛創建 Deployment:
```bash
$ kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx 3 3 3 3 1m
[mkargaki@dhcp129-211 kubernetes]$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-2142116321 3 3 3 1m
```
使用以下命令暫停 Deployment:
```bash
$ kubectl rollout pause deployment/nginx-deployment
deployment "nginx-deployment" paused
```
然后更新 Deplyment中的鏡像:
```bash
$ kubectl set image deploy/nginx nginx=nginx:1.9.1
deployment "nginx-deployment" image updated
```
注意新的 rollout 啟動了:
```bash
$ kubectl rollout history deploy/nginx
deployments "nginx"
REVISION CHANGE-CAUSE
1 <none>
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-2142116321 3 3 3 2m
```
您可以進行任意多次更新,例如更新使用的資源:
```bash
$ kubectl set resources deployment nginx -c=nginx --limits=cpu=200m,memory=512Mi
deployment "nginx" resource requirements updated
```
Deployment 暫停前的初始狀態將繼續它的功能,而不會對 Deployment 的更新產生任何影響,只要 Deployment是暫停的。
最后,恢復這個 Deployment,觀察完成更新的 ReplicaSet 已經創建出來了:
```bash
$ kubectl rollout resume deploy nginx
deployment "nginx" resumed
$ KUBECTL get rs -w
NAME DESIRED CURRENT READY AGE
nginx-2142116321 2 2 2 2m
nginx-3926361531 2 2 0 6s
nginx-3926361531 2 2 1 18s
nginx-2142116321 1 2 2 2m
nginx-2142116321 1 2 2 2m
nginx-3926361531 3 2 1 18s
nginx-3926361531 3 2 1 18s
nginx-2142116321 1 1 1 2m
nginx-3926361531 3 3 1 18s
nginx-3926361531 3 3 2 19s
nginx-2142116321 0 1 1 2m
nginx-2142116321 0 1 1 2m
nginx-2142116321 0 0 0 2m
nginx-3926361531 3 3 3 20s
^C
$ KUBECTL get rs
NAME DESIRED CURRENT READY AGE
nginx-2142116321 0 0 0 2m
nginx-3926361531 3 3 3 28s
```
**注意:** 在恢復 Deployment 之前您無法回退一個已經暫停的 Deployment。
## Deployment 狀態
Deployment 在生命周期中有多種狀態。在創建一個新的 ReplicaSet 的時候它可以是 [progressing](https://kubernetes.io/docs/concepts/workloads/controllers/deployment#progressing-deployment) 狀態, [complete](https://kubernetes.io/docs/concepts/workloads/controllers/deployment#complete-deployment) 狀態,或者 [fail to progress ](https://kubernetes.io/docs/concepts/workloads/controllers/deployment#failed-deployment)狀態。
### 進行中的 Deployment
Kubernetes 將執行過下列任務之一的 Deployment 標記為 *progressing* 狀態:
- Deployment 正在創建新的ReplicaSet過程中。
- Deployment 正在擴容一個已有的 ReplicaSet。
- Deployment 正在縮容一個已有的 ReplicaSet。
- 有新的可用的 pod 出現。
您可以使用`kubectl rollout status`命令監控 Deployment 的進度。
### 完成的 Deployment
Kubernetes 將包括以下特性的 Deployment 標記為 *complete* 狀態:
- Deployment 最小可用。最小可用意味著 Deployment 的可用 replica 個數等于或者超過 Deployment 策略中的期望個數。
- 所有與該 Deployment 相關的replica都被更新到了您指定版本,也就說更新完成。
- 該 Deployment 中沒有舊的 Pod 存在。
您可以用`kubectl rollout status`命令查看 Deployment 是否完成。如果 rollout 成功完成,`kubectl rollout status`將返回一個0值的 Exit Code。
```bash
$ kubectl rollout status deploy/nginx
Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx" successfully rolled out
$ echo $?
0
```
### 失敗的 Deployment
您的 Deployment 在嘗試部署新的 ReplicaSet 的時候可能卡住,永遠也不會完成。這可能是因為以下幾個因素引起的:
- 無效的引用
- 不可讀的 probe failure
- 鏡像拉取錯誤
- 權限不夠
- 范圍限制
- 程序運行時配置錯誤
探測這種情況的一種方式是,在您的 Deployment spec 中指定[`spec.progressDeadlineSeconds`](https://kubernetes.io/docs/concepts/workloads/controllers/deployment#progress-deadline-seconds)。`spec.progressDeadlineSeconds` 表示 Deployment controller 等待多少秒才能確定(通過 Deployment status)Deployment進程是卡住的。
下面的`kubectl`命令設置`progressDeadlineSeconds` 使 controller 在 Deployment 在進度卡住10分鐘后報告:
```bash
$ kubectl patch deployment/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'
"nginx-deployment" patched
```
當超過截止時間后,Deployment controller 會在 Deployment 的 `status.conditions`中增加一條DeploymentCondition,它包括如下屬性:
- Type=Progressing
- Status=False
- Reason=ProgressDeadlineExceeded
**注意:** kubernetes除了報告`Reason=ProgressDeadlineExceeded`狀態信息外不會對卡住的 Deployment 做任何操作。更高層次的協調器可以利用它并采取相應行動,例如,回滾 Deployment 到之前的版本。
**注意:** 如果您暫停了一個 Deployment,在暫停的這段時間內kubernetnes不會檢查您指定的 deadline。您可以在 Deployment 的 rollout 途中安全的暫停它,然后再恢復它,這不會觸發超過deadline的狀態。
您可能在使用 Deployment 的時候遇到一些短暫的錯誤,這些可能是由于您設置了太短的 timeout,也有可能是因為各種其他錯誤導致的短暫錯誤。例如,假設您使用了無效的引用。當您 Describe Deployment 的時候可能會注意到如下信息:
```bash
$ kubectl describe deployment nginx-deployment
<...>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True ReplicaSetUpdated
ReplicaFailure True FailedCreate
<...>
```
執行 `kubectl get deployment nginx-deployment -o yaml`,Deployement 的狀態可能看起來像這個樣子:
```yaml
status:
availableReplicas: 2
conditions:
- lastTransitionTime: 2016-10-04T12:25:39Z
lastUpdateTime: 2016-10-04T12:25:39Z
message: Replica set "nginx-deployment-4262182780" is progressing.
reason: ReplicaSetUpdated
status: "True"
type: Progressing
- lastTransitionTime: 2016-10-04T12:25:42Z
lastUpdateTime: 2016-10-04T12:25:42Z
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: "True"
type: Available
- lastTransitionTime: 2016-10-04T12:25:39Z
lastUpdateTime: 2016-10-04T12:25:39Z
message: 'Error creating: pods "nginx-deployment-4262182780-" is forbidden: exceeded quota:
object-counts, requested: pods=1, used: pods=3, limited: pods=2'
reason: FailedCreate
status: "True"
type: ReplicaFailure
observedGeneration: 3
replicas: 2
unavailableReplicas: 2
```
最終,一旦超過 Deployment 進程的 deadline,kubernetes 會更新狀態和導致 Progressing 狀態的原因:
```bash
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing False ProgressDeadlineExceeded
ReplicaFailure True FailedCreate
```
您可以通過縮容 Deployment的方式解決配額不足的問題,或者增加您的 namespace 的配額。如果您滿足了配額條件后,Deployment controller 就會完成您的 Deployment rollout,您將看到 Deployment 的狀態更新為成功狀態(`Status=True`并且`Reason=NewReplicaSetAvailable`)。
```bash
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
```
`Type=Available`、 `Status=True` 意味著您的Deployment有最小可用性。 最小可用性是在Deployment策略中指定的參數。`Type=Progressing` 、 `Status=True`意味著您的Deployment 或者在部署過程中,或者已經成功部署,達到了期望的最少的可用replica數量(查看特定狀態的Reason——在我們的例子中`Reason=NewReplicaSetAvailable` 意味著Deployment已經完成)。
您可以使用`kubectl rollout status`命令查看Deployment進程是否失敗。當Deployment過程超過了deadline,`kubectl rollout status`將返回非0的exit code。
```bash
$ kubectl rollout status deploy/nginx
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline
$ echo $?
1
```
### 操作失敗的 Deployment
所有對完成的 Deployment 的操作都適用于失敗的 Deployment。您可以對它擴/縮容,回退到歷史版本,您甚至可以多次暫停它來應用 Deployment pod template。
## 清理Policy
您可以設置 Deployment 中的 `.spec.revisionHistoryLimit` 項來指定保留多少舊的 ReplicaSet。 余下的將在后臺被當作垃圾收集。默認的,所有的 revision 歷史就都會被保留。在未來的版本中,將會更改為2。
**注意:** 將該值設置為0,將導致所有的 Deployment 歷史記錄都會被清除,該 Deployment 就無法再回退了。
## 用例
### 金絲雀 Deployment
如果您想要使用 Deployment 對部分用戶或服務器發布 release,您可以創建多個 Deployment,每個 Deployment 對應一個 release,參照 [managing resources](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments) 中對金絲雀模式的描述。
## 編寫 Deployment Spec
在所有的 Kubernetes 配置中,Deployment 也需要`apiVersion`,`kind`和`metadata`這些配置項。配置文件的通用使用說明查看 [部署應用](https://kubernetes.io/docs/tasks/run-application/run-stateless-application-deployment/),配置容器,和 [使用 kubectl 管理資源 ](https://kubernetes.io/docs/tutorials/object-management-kubectl/object-management/) 文檔。
### Pod Template
`.spec.template` 是 `.spec`中唯一要求的字段。
`.spec.template` 是 [pod template](https://kubernetes.io/docs/user-guide/replication-controller/#pod-template). 它跟 [Pod](https://kubernetes.io/docs/user-guide/pods)有一模一樣的schema,除了它是嵌套的并且不需要`apiVersion` 和 `kind`字段。
另外為了劃分Pod的范圍,Deployment中的pod template必須指定適當的label(不要跟其他controller重復了,參考[selector](https://kubernetes.io/docs/concepts/workloads/controllers/deployment#selector))和適當的重啟策略。
[`.spec.template.spec.restartPolicy`](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle) 可以設置為 `Always` , 如果不指定的話這就是默認配置。
### Replicas
`.spec.replicas` 是可以選字段,指定期望的pod數量,默認是1。
### Selector
`.spec.selector`是可選字段,用來指定 [label selector](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels) ,圈定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](https://kubernetes.io/docs/tasks/run-application/rolling-update-replication-controller) 的方式更新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](https://kubernetes.io/docs/concepts/workloads/controllers/deployment#failed-deployment) ——表現為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](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#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。
- 序言
- 云原生
- 云原生(Cloud Native)的定義
- CNCF - 云原生計算基金會簡介
- CNCF章程
- 云原生的設計哲學
- Play with Kubernetes
- 快速部署一個云原生本地實驗環境
- Kubernetes與云原生應用概覽
- 云原生應用之路——從Kubernetes到Cloud Native
- 云原生編程語言
- 云原生編程語言Ballerina
- 云原生編程語言Pulumi
- 云原生的未來
- Kubernetes架構
- 設計理念
- Etcd解析
- 開放接口
- CRI - Container Runtime Interface(容器運行時接口)
- CNI - Container Network Interface(容器網絡接口)
- CSI - Container Storage Interface(容器存儲接口)
- Kubernetes中的網絡
- Kubernetes中的網絡解析——以flannel為例
- Kubernetes中的網絡解析——以calico為例
- 具備API感知的網絡和安全性管理開源軟件Cilium
- Cilium架構設計與概念解析
- 資源對象與基本概念解析
- Pod狀態與生命周期管理
- Pod概覽
- Pod解析
- Init容器
- Pause容器
- Pod安全策略
- Pod的生命周期
- Pod Hook
- Pod Preset
- Pod中斷與PDB(Pod中斷預算)
- 集群資源管理
- Node
- Namespace
- Label
- Annotation
- Taint和Toleration(污點和容忍)
- 垃圾收集
- 控制器
- Deployment
- StatefulSet
- DaemonSet
- ReplicationController和ReplicaSet
- Job
- CronJob
- Horizontal Pod Autoscaling
- 自定義指標HPA
- 準入控制器(Admission Controller)
- 服務發現
- Service
- Ingress
- Traefik Ingress Controller
- 身份與權限控制
- ServiceAccount
- RBAC——基于角色的訪問控制
- NetworkPolicy
- 存儲
- Secret
- ConfigMap
- ConfigMap的熱更新
- Volume
- Persistent Volume(持久化卷)
- Storage Class
- 本地持久化存儲
- 集群擴展
- 使用自定義資源擴展API
- 使用CRD擴展Kubernetes API
- Aggregated API Server
- APIService
- Service Catalog
- 資源調度
- QoS(服務質量等級)
- 用戶指南
- 資源對象配置
- 配置Pod的liveness和readiness探針
- 配置Pod的Service Account
- Secret配置
- 管理namespace中的資源配額
- 命令使用
- Docker用戶過度到kubectl命令行指南
- kubectl命令概覽
- kubectl命令技巧大全
- 使用etcdctl訪問kubernetes數據
- 集群安全性管理
- 管理集群中的TLS
- kubelet的認證授權
- TLS bootstrap
- 創建用戶認證授權的kubeconfig文件
- IP偽裝代理
- 使用kubeconfig或token進行用戶身份認證
- Kubernetes中的用戶與身份認證授權
- Kubernetes集群安全性配置最佳實踐
- 訪問Kubernetes集群
- 訪問集群
- 使用kubeconfig文件配置跨集群認證
- 通過端口轉發訪問集群中的應用程序
- 使用service訪問群集中的應用程序
- 從外部訪問Kubernetes中的Pod
- Cabin - Kubernetes手機客戶端
- Kubernetic - Kubernetes桌面客戶端
- Kubernator - 更底層的Kubernetes UI
- 在Kubernetes中開發部署應用
- 適用于kubernetes的應用開發部署流程
- 遷移傳統應用到Kubernetes中——以Hadoop YARN為例
- 最佳實踐概覽
- 在CentOS上部署Kubernetes集群
- 創建TLS證書和秘鑰
- 創建kubeconfig文件
- 創建高可用etcd集群
- 安裝kubectl命令行工具
- 部署master節點
- 安裝flannel網絡插件
- 部署node節點
- 安裝kubedns插件
- 安裝dashboard插件
- 安裝heapster插件
- 安裝EFK插件
- 生產級的Kubernetes簡化管理工具kubeadm
- 使用kubeadm在Ubuntu Server 16.04上快速構建測試集群
- 服務發現與負載均衡
- 安裝Traefik ingress
- 分布式負載測試
- 網絡和集群性能測試
- 邊緣節點配置
- 安裝Nginx ingress
- 安裝配置DNS
- 安裝配置Kube-dns
- 安裝配置CoreDNS
- 運維管理
- Master節點高可用
- 服務滾動升級
- 應用日志收集
- 配置最佳實踐
- 集群及應用監控
- 數據持久化問題
- 管理容器的計算資源
- 集群聯邦
- 存儲管理
- GlusterFS
- 使用GlusterFS做持久化存儲
- 使用Heketi作為Kubernetes的持久存儲GlusterFS的external provisioner
- 在OpenShift中使用GlusterFS做持久化存儲
- GlusterD-2.0
- Ceph
- 用Helm托管安裝Ceph集群并提供后端存儲
- 使用Ceph做持久化存儲
- 使用rbd-provisioner提供rbd持久化存儲
- OpenEBS
- 使用OpenEBS做持久化存儲
- Rook
- NFS
- 利用NFS動態提供Kubernetes后端存儲卷
- 集群與應用監控
- Heapster
- 使用Heapster獲取集群和對象的metric數據
- Prometheus
- 使用Prometheus監控kubernetes集群
- Prometheus查詢語言PromQL使用說明
- 使用Vistio監控Istio服務網格中的流量
- 分布式跟蹤
- OpenTracing
- 服務編排管理
- 使用Helm管理Kubernetes應用
- 構建私有Chart倉庫
- 持續集成與發布
- 使用Jenkins進行持續集成與發布
- 使用Drone進行持續集成與發布
- 更新與升級
- 手動升級Kubernetes集群
- 升級dashboard
- 領域應用概覽
- 微服務架構
- 微服務中的服務發現
- 使用Java構建微服務并發布到Kubernetes平臺
- Spring Boot快速開始指南
- Service Mesh 服務網格
- 企業級服務網格架構
- Service Mesh基礎
- Service Mesh技術對比
- 采納和演進
- 定制和集成
- 總結
- Istio
- 安裝并試用Istio service mesh
- 配置請求的路由規則
- 安裝和拓展Istio service mesh
- 集成虛擬機
- Istio中sidecar的注入規范及示例
- 如何參與Istio社區及注意事項
- Istio教程
- Istio免費學習資源匯總
- 深入理解Istio Service Mesh中的Envoy Sidecar注入與流量劫持
- 深入理解Istio Service Mesh中的Envoy Sidecar代理的路由轉發
- Linkerd
- Linkerd 使用指南
- Conduit
- Condiut概覽
- 安裝Conduit
- Envoy
- Envoy的架構與基本術語
- Envoy作為前端代理
- Envoy mesh教程
- SOFAMesh
- SOFAMesh中的Dubbo on x-protocol
- SOFAMosn
- 使用 SOFAMosn 構建 SOFAMesh
- 大數據
- Spark standalone on Kubernetes
- 運行支持Kubernetes原生調度的Spark程序
- Serverless架構
- 理解Serverless
- FaaS-函數即服務
- OpenFaaS快速入門指南
- 邊緣計算
- 人工智能