# 无状态负载（Deployment）
#### 无状态负载（Deployment）
<video controls="controls" preload="none" id="object4562957154113" class="idp-external-video" src="https://res-video.hc-cdn.com/cloudbu-site/china/zh-cn/video/cce/deployment.mp4" title="Deployment介绍" poster="https://support.huaweicloud.com/basics-cce/zh-cn_image_0000002324264158.jpg" height="340.0000" width="600.0000"></video>
Pod是Kubernetes创建或部署的最小单位，但是Pod是被设计为相对短暂的一次性实体，Pod可以被驱逐（当节点资源不足时）、随着集群的节点崩溃而消失。Kubernetes提供了Controller（控制器）来管理Pod，Controller可以创建和管理多个Pod，提供副本管理、滚动升级和自愈能力，其中最为常用的就是Deployment。
图1Deployment   
![](https://support.huaweicloud.com/basics-cce/zh-cn_image_0258095884.png "点击放大")
一个Deployment可以包含一个或多个Pod副本，每个Pod副本的角色相同，所以系统会自动为Deployment的多个Pod副本分发请求。
Deployment集成了上线部署、滚动升级、创建副本、恢复上线的功能，在某种程度上，Deployment实现无人值守的上线，大大降低了上线过程的复杂性和操作风险。
#### 创建Deployment
以下示例为创建一个名为nginx的Deployment负载，使用nginx:latest镜像创建两个Pod，每个Pod占用100m CPU、200Mi内存。
```
apiVersion: apps/v1      # 注意这里与Pod的区别，Deployment是apps/v1而不是v1
kind: Deployment         # 资源类型为Deployment
metadata:
  name: nginx            # Deployment的名称
spec:
  replicas: 2            # Pod的数量，Deployment会确保一直有2个Pod运行         
  selector:              # Label Selector
    matchLabels:
      app: nginx
  template:              # Pod的定义，用于创建Pod，也称为Pod template
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:latest
        name: container-0
        resources:
          limits:
            cpu: 100m
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
      imagePullSecrets:
      - name: default-secret
```
从这个定义中可以看到Deployment的名称为nginx，spec.replicas定义了Pod的数量，即这个Deployment控制2个Pod；spec.selector是Label Selector（标签选择器），表示这个Deployment会选择Label为app=nginx的Pod；spec.template是Pod的定义，内容与[Pod](https://support.huaweicloud.com/basics-cce/kubernetes_0006.html)中的定义完全一致。
将上面Deployment的定义保存到deployment.yaml文件中，使用kubectl创建这个Deployment。
使用kubectl get查看Deployment和Pod，可以看到**READY** 值为2/2，前一个2表示当前有2个Pod运行，后一个2表示期望有2个Pod，**AVAILABLE**为2表示有2个Pod是可用的。
```
$ kubectl create -f deployment.yaml
deployment.apps/nginx created
$ kubectl get deploy
NAME           READY     UP-TO-DATE   AVAILABLE   AGE
nginx          2/2       2            2           4m5s
```
#### Deployment如何控制Pod
继续查询Pod，如下所示。
```
$ kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
nginx-7f98958cdf-tdmqk   1/1       Running   0          13s
nginx-7f98958cdf-txckx   1/1       Running   0          13s
```
如果删掉一个Pod，您会发现立马会有一个新的Pod被创建出来，如下所示，这就是前面所说的Deployment会确保有2个Pod在运行，如果删掉一个，Deployment会重新创建一个，如果某个Pod故障或有其他问题，Deployment会自动拉起这个Pod。
```
$ kubectl delete pod nginx-7f98958cdf-txckx
$ kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
nginx-7f98958cdf-tdmqk   1/1       Running   0          21s
nginx-7f98958cdf-tesqr   1/1       Running   0          1s
```
看到有如下两个名为nginx-7f98958cdf-tdmqk和nginx-7f98958cdf-tesqr的Pod， 其中nginx是直接使用Deployment的名称，-7f98958cdf-tdmqk和-7f98958cdf-tesqr是kubernetes随机生成的后缀。
您也许会发现这两个后缀中前面一部分是相同的，都是7f98958cdf，这是因为Deployment不是直接控制Pod的，Deployment是通过一种名为ReplicaSet的控制器控制Pod，通过如下命令可以查询ReplicaSet，其中rs是ReplicaSet的缩写。
```
$ kubectl get rs
NAME               DESIRED   CURRENT   READY     AGE
nginx-7f98958cdf   2         2         2         1m
```
这个ReplicaSet的名称为nginx-7f98958cdf，后缀-7f98958cdf也是随机生成的。
Deployment控制Pod的方式如[图2]所示，Deployment控制ReplicaSet，ReplicaSet控制Pod。
图2Deployment通过ReplicaSet控制Pod   
![](https://support.huaweicloud.com/basics-cce/zh-cn_image_0258097483.png "点击放大")
如果使用kubectl describe命令查看Deployment的详情，您就可以看到ReplicaSet，如下所示，可以看到有一行NewReplicaSet: nginx-7f98958cdf (2/2 replicas created)，而且Events里面事件确是把ReplicaSet的实例扩容到2个。在实际使用中您也许不会直接操作ReplicaSet，但了解Deployment通过控制ReplicaSet来控制Pod会有助于您定位问题。
```
$ kubectl describe deploy nginx
Name:                   nginx
Namespace:              default
CreationTimestamp:      Sun, 16 Dec 2018 19:21:58 +0800
Labels:                 app=nginx
...
NewReplicaSet:   nginx-7f98958cdf (2/2 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  5m    deployment-controller  Scaled up replica set nginx-7f98958cdf to 2
```
#### 升级
在实际应用中，升级是一个常见的场景，Deployment能够很方便地支撑应用升级。
Deployment可以设置不同的升级策略，有如下两种。
- RollingUpdate：滚动升级，即逐步创建新Pod再删除旧Pod，为默认策略。
- Recreate：替换升级，即先把当前Pod删掉再重新创建Pod。
Deployment的升级可以是声明式的，也就是说只需要修改Deployment的YAML定义即可，比如使用kubectl edit命令将上面Deployment中的镜像修改为nginx:alpine。修改完成后再查询ReplicaSet和Pod，发现创建了一个新的ReplicaSet，Pod也重新创建了。
```
$ kubectl edit deploy nginx
$ kubectl get rs
NAME               DESIRED   CURRENT   READY     AGE
nginx-6f9f58dffd   2         2         2         1m
nginx-7f98958cdf   0         0         0         48m
$ kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
nginx-6f9f58dffd-tdmqk   1/1       Running   0          1m
nginx-6f9f58dffd-tesqr   1/1       Running   0          1m
```
Deployment可以通过maxSurge和maxUnavailable两个参数控制升级过程中同时重新创建Pod的比例，这在很多时候是非常有用，配置如下所示。
```
spec:
  strategy:
    rollingUpdate:
      maxSurge: 0.25
      maxUnavailable: 0.25
    type: RollingUpdate
```
- maxSurge：表示在滚动更新过程中，允许超出期望副本数的最大实例数或比例，即决定可以同时创建多少个新Pod替换旧Pod，默认值为25%。实际升级过程中，比例会换算为绝对数，并**向上取整** 。
  例如spec.replicas为2，默认状态下最多同时创建2\*0.25=1个（向上取整）Pod，即系统中最多同时存在3个Pod。
  
- maxUnavailable：表示在滚动更新过程中，允许处于不可用状态的最大实例数量或比例，即实际运行Pod数可低于期望副本数的最大限制，默认为25%。实际升级过程中，比例会换算为绝对数，并**向下取整** 。
  例如spec.replicas为2，默认状态下最多有2\*0.25=0个（向下取整）Pod失效，即实际运行Pod数不可低于期望副本数，系统中最少有2个Pod处于运行状态。换言之，在升级过程中，一直会有2个Pod处于运行状态，每次新建一个Pod，等这个Pod创建成功后再删掉一个旧Pod，直至Pod全部为新Pod。
  
 
#### 回滚
回滚也称为回退，即当发现升级出现问题时，让应用回到老的版本。Deployment可以非常方便地回滚到老版本。
例如上面升级的新版镜像有问题，可以执行kubectl rollout undo命令进行回滚。
```
$ kubectl rollout undo deployment nginx
deployment.apps/nginx rolled back
```
Deployment之所以能如此容易地做到回滚，是因为Deployment是通过ReplicaSet控制Pod的，升级后之前ReplicaSet都一直存在，Deployment回滚做的就是使用之前的ReplicaSet再次把Pod创建出来。Deployment中保存ReplicaSet的数量可以使用revisionHistoryLimit参数限制，默认值为10。
