# Deployment

{% hint style="info" %}
Deployment-->Replica Set-->Pods
{% endhint %}

## 1 创建

一个 Deployment 拥有多个 Replica Set，而一个 Replica Set 拥有一个或多个 Pod。一个 Deployment 控制多个 rs 主要是为了支持回滚机制，每当 Deployment 操作时，Kubernetes 会重新生成一个 Replica Set 并保留，以后有需要的话就可以回滚至之前的状态。 下面创建一个 Deployment，它创建了一个 Replica Set 来启动 3 个 nginx pod，yaml 文件如下：

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

将上面内容保存为：nginx-deployment.yaml，执行命令：

```shell
$ kubectl create -f nginx-deployment.yaml
deployment "nginx-deploy" created
```

然后执行一下命令查看刚刚创建的 Deployment:

```shell
$ kubectl get deployments
NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deploy   3         0         0            0           1s
```

隔一会再次执行上面命令：

```shell
$ kubectl get deployments
NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deploy   3         3         3            3           4m
```

我们可以看到 Deployment 已经创建了 1 个 Replica Set 了，执行下面的命令查看 rs 和 pod:

```shell
$ kubectl get rs
NAME                     DESIRED   CURRENT   READY     AGE
nginx-deploy-431080787   3         3         3         6m
```

```shell
$ kubectl get pod --show-labels
NAME                           READY     STATUS    RESTARTS   AGE       LABELS
nginx-deploy-431080787-53z8q   1/1       Running   0          7m        app=nginx,pod-template-hash=431080787
nginx-deploy-431080787-bhhq0   1/1       Running   0          7m        app=nginx,pod-template-hash=431080787
nginx-deploy-431080787-sr44p   1/1       Running   0          7m        app=nginx,pod-template-hash=431080787
```

上面的 Deployment 的 yaml 文件中的`replicas:3`将会保证我们始终有 3 个 POD 在运行。

## 2 滚动升级 <a href="#gun-dong-sheng-ji" id="gun-dong-sheng-ji"></a>

现在我们将刚刚保存的 yaml 文件中的 nginx 镜像修改为`nginx:1.13.3`，然后在 spec 下面添加滚动升级策略：

```yaml
minReadySeconds: 5
strategy:
  # indicate which strategy we want for rolling update
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 1
```

* minReadySeconds:
  * Kubernetes 在等待设置的时间后才进行升级
  * 如果没有设置该值，Kubernetes 会假设该容器启动起来后就提供服务了
  * 如果没有设置该值，在某些极端情况下可能会造成服务不正常运行
* maxSurge:
  * 升级过程中最多可以比原先设置多出的 POD 数量
  * 例如：maxSurage=1，replicas=5,则表示 Kubernetes 会先启动 1 一个新的 Pod 后才删掉一个旧的 POD，整个升级过程中最多会有 5+1 个 POD。
* maxUnavaible:
  * 升级过程中最多有多少个 POD 处于无法提供服务的状态
  * 当`maxSurge`不为 0 时，该值也不能为 0
  * 例如：maxUnavaible=1，则表示 Kubernetes 整个升级过程中最多会有 1 个 POD 处于无法服务的状态。

然后执行命令：

```shell
$ kubectl apply -f nginx-deployment.yaml
deployment "nginx-deploy" configured
```

然后我们可以使用`rollout`命令：

*   查看状态：

    ```shell
    $ kubectl rollout status deployment/nginx-deploy
    Waiting for rollout to finish: 1 out of 3 new replicas have been updated..
    deployment "nginx-deploy" successfully rolled out
    ```
*   暂停升级

    ```shell
    $ kubectl rollout pause deployment <deployment>
    ```
*   继续升级

    ```shell
    $ kubectl rollout resume deployment <deployment>
    ```

升级结束后，继续查看 rs 的状态：

```shell
$ kubectl get rs
NAME                      DESIRED   CURRENT   READY     AGE
nginx-deploy-2078889897   0         0         0         47m
nginx-deploy-3297445372   3         3         3         42m
nginx-deploy-431080787    0         0         0         1h
```

根据 AGE 我们可以看到离我们最近的当前状态是：3，和我们的 yaml 文件是一致的，证明升级成功了。用`describe`命令可以查看升级的全部信息：

```shell
Name:     nginx-deploy
Namespace:    default
CreationTimestamp:  Wed, 18 Oct 2017 16:58:52 +0800
Labels:     k8s-app=nginx-demo
Annotations:    deployment.kubernetes.io/revision=3
      kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"k8s-app":"nginx-demo"},"name":"nginx-deploy","namespace":"defa...
Selector:   app=nginx
Replicas:   3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:   RollingUpdate
MinReadySeconds:  0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels: app=nginx
  Containers:
   nginx:
    Image:    nginx:1.13.3
    Port:   80/TCP
    Environment:  <none>
    Mounts:   <none>
  Volumes:    <none>
Conditions:
  Type    Status  Reason
  ----    ------  ------
  Progressing   True  NewReplicaSetAvailable
  Available   True  MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet:  nginx-deploy-3297445372 (3/3 replicas created)
Events:
  FirstSeen LastSeen  Count From      SubObjectPath Type    Reason      Message
  --------- --------  ----- ----      ------------- --------  ------      -------
  50m   50m   1 deployment-controller     Normal    ScalingReplicaSet Scaled up replica set nginx-deploy-2078889897 to 1
  45m   45m   1 deployment-controller     Normal    ScalingReplicaSet Scaled down replica set nginx-deploy-2078889897 to 0
  45m   45m   1 deployment-controller     Normal    ScalingReplicaSet Scaled up replica set nginx-deploy-3297445372 to 1
  39m   39m   1 deployment-controller     Normal    ScalingReplicaSet Scaled down replica set nginx-deploy-431080787 to 2
  39m   39m   1 deployment-controller     Normal    ScalingReplicaSet Scaled up replica set nginx-deploy-3297445372 to 2
  38m   38m   1 deployment-controller     Normal    ScalingReplicaSet Scaled down replica set nginx-deploy-431080787 to 1
  38m   38m   1 deployment-controller     Normal    ScalingReplicaSet Scaled up replica set nginx-deploy-3297445372 to 3
  38m   38m   1 deployment-controller     Normal    ScalingReplicaSet Scaled down replica set nginx-deploy-431080787 to 0
```

## 3 回滚 Deployment <a href="#hui-gun-deployment" id="hui-gun-deployment"></a>

我们已经能够滚动平滑的升级我们的 Deployment 了，但是如果升级后的 POD 出了问题该怎么办？我们能够想到的最好最快的方式当然是回退到上一次能够提供正常工作的版本，Deployment 就为我们提供了回滚机制。

首先，查看 Deployment 的升级历史：

```shell
$ kubectl rollout history deployment nginx-deploy
deployments "nginx-deploy"
REVISION  CHANGE-CAUSE
1   <none>
2   <none>
3   kubectl apply --filename=Desktop/nginx-deployment.yaml --record=true
```

从上面的结果可以看出在执行`Deployment`升级的时候最好带上`record`参数，便于我们查看历史版本信息。

默认情况下，所有通过`kubectl xxxx --record`都会被`kubernetes`记录到`etcd`进行持久化，这无疑会占用资源，最重要的是，时间久了，当你`kubectl get rs`时，会有成百上千的垃圾`RS`返回给你，那时你可能就眼花缭乱了。

上生产时，我们最好通过设置 Deployment 的`.spec.revisionHistoryLimit`来限制最大保留的`revision number`，比如 15 个版本，回滚的时候一般只会回滚到最近的几个版本就足够了。其实`rollout history`中记录的`revision`都和`ReplicaSets`一一对应。如果手动`delete`某个 ReplicaSet，对应的`rollout history`就会被删除，也就是还说你无法回滚到这个`revison`了。

`rollout history`和`ReplicaSet`的对应关系，可以在`kubectl describe rs $RSNAME`返回的`revision`字段中得到，这里的`revision`就对应着`rollout history`返回的`revison`。

同样我们可以使用下面的命令查看单个`revison`的信息：

```shell
$ kubectl rollout history deployment nginx-deploy --revision=3
deployments "nginx-deploy" with revision #3
Pod Template:
  Labels: app=nginx
  pod-template-hash=3297445372
  Annotations:  kubernetes.io/change-cause=kubectl apply --filename=nginx-deployment.yaml --record=true
  Containers:
   nginx:
    Image:  nginx:1.13.3
    Port: 80/TCP
    Environment:  <none>
    Mounts: <none>
  Volumes:  <none>
```

假如现在要直接回退到当前版本的前一个版本：

```shell
$ kubectl rollout undo deployment nginx-deploy
deployment "nginx-deploy" rolled back
```

当然也可以用`revision`回退到指定的版本：

```shell
$ kubectl rollout undo deployment nginx-deploy --to-revision=2
deployment "nginx-deploy" rolled back
```

现在可以用命令查看 Deployment 现在的状态了。
