
# Deployment
在[Pod](https://support.huaweicloud.com/devg-cci/cci_05_0009.html)这个章节介绍了Pod，Pod是Kubernetes创建或部署的最小单位，但是Pod是被设计为相对短暂的一次性实体，Pod可以被驱逐（当节点资源不足时）、随着集群的节点fail而消失。同时kubernetes提供了Controller（控制器）来管理Pod，Controller可以创建和管理多个Pod，提供副本管理、滚动升级和自愈能力，其中最为常用的就是Deployment。
一个Deployment可以包含一个或多个Pod副本，每个Pod副本的角色相同，所以系统会自动为Deployment的多个Pod副本分发请求。
Deployment集成了上线部署、滚动升级、创建副本，恢复上线任务，在某种程度上，Deployment可以帮我们实现无人值守的上线，大大降低我们的上线过程的复杂沟通、操作风险。
#### 创建Deployment
以下示例为创建一个名为nginx的Deployment负载，使用nginx:latest镜像创建两个Pod，每个Pod占用500m core CPU、1G内存。
```
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:
      volumes:
        - name: cci-sfs-test                     # sfs卷的名称
          persistentVolumeClaim:
            claimName: cci-sfs-test
      containers:
      - image: nginx:latest
        name: container-0
        resources:
          limits:
            cpu: 500m
            memory: 1024Mi
          requests:
            cpu: 500m
            memory: 1024Mi
        volumeMounts:
          - name: cci-sfs-test
            mountPath: /tmp/sfs0/krlp2k8j        # sfs卷的容器内挂载路径  
      imagePullSecrets:           # 拉取镜像使用的证书，必须为imagepull-secret
      - name: imagepull-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/devg-cci/cci_05_0004.html)中的定义完全一致。
将上面Deployment的定义保存到deployment.yaml文件中，使用kubectl创建这个Deployment。
使用kubectl get查看Deployment和Pod，可以看到DESIRED值为2，这表示这个Deployment期望有2个Pod，CURRENT也为2，这表示当前有2个Pod，AVAILABLE为2表示有2个Pod是可用的。
```
$ kubectl create -f deployment.yaml -n $namespace_name 
$ kubectl get deployment -n $namespace_name
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx     2         2         2            2           8s
```
#### Deployment如何控制Pod
继续查询Pod，如下所示。
```
$ kubectl get pods -n $namespace_name
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 -n $namespace_name
$ kubectl get pods -n $namespace_name 
NAME                     READY     STATUS    RESTARTS   AGE
nginx-7f98958cdf-tdmqk   1/1       Running   0          21s
nginx-7f98958cdf-tesqr   1/1       Running   0          21s
```
看到有如下两个名为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 -n $namespace_name
NAME               DESIRED   CURRENT   READY     AGE
nginx-7f98958cdf   3         3         3         1m
```
这个ReplicaSet的名称为nginx-7f98958cdf，后缀-7f98958cdf也是随机生成的。
Deployment控制Pod的方式如[图1]所示，Deployment控制ReplicaSet，ReplicaSet控制Pod。
图1Deployment通过ReplicaSet控制Pod   
![](https://support.huaweicloud.com/devg-cci/zh-cn_image_0198422874.png)
如果使用kubectl describe命令查看Deployment的详情，您就可以看到ReplicaSet，如下所示，可以看到有一行 NewReplicaSet: nginx-7f98958cdf (2/2 replicas created)，而且 Events 里面事件确是把ReplicaSet的实例扩容到2个。在实际使用中您也许不会直接操作ReplicaSet，但了解Deployment通过控制ReplicaSet来控制Pod会有助于您定位问题。
```
$ kubectl describe deploy nginx -n $namespace_name
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 -n $namespace_name
$ kubectl get rs -n $namespace_name
NAME               DESIRED   CURRENT   READY     AGE
nginx-6f9f58dffd   2         2         2         1m
nginx-7f98958cdf   0         0         0         48m
$ kubectl get pods -n $namespace_name
NAME                     READY     STATUS    RESTARTS   AGE
nginx-6f9f58dffd-tdmqk   1/1       Running   0          21s
nginx-6f9f58dffd-tesqr   1/1       Running   0          21s
```
Deployment可以通过maxSurge 和 maxUnavailable两个参数控制升级过程中同时重新创建Pod的比例，这在很多时候是非常有用，配置如下所示。
```
spec:
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
    type: RollingUpdate
```
- maxSurge：与Deployment中spec.replicas相比，可以有多少个Pod存在，默认值是25%，比如spec.replicas为 4，那升级过程中就不能超过5个 Pod存在，即按1个的步伐升级，实际升级过程中会换算成数字，且换算会向上取整。这个值也可以直接设置成数字。
- maxUnavailable：与Deployment中spec.replicas相比，可以有多少个Pod失效，也就是删除的比例，默认值是25%，比如spec.replicas为 4，那升级过程中就至少有3个Pod存在，即删除Pod 的步伐是 1。同样这个值也可以设置成数字。
在前面的例子中，由于spec.replicas是2，如果maxSurge和maxUnavailable都为默认值25%，那实际升级过程中，maxSurge允许最多3个 Pod 存在（向上取整，2\*1.25=2.5，取整为3），而 maxUnavailable 则不允许有 Pod Unavailable（向上取整，2\*0.75=1.5，取整为2），也就是说在升级过程中，一直会有2个Pod处于运行状态，每次新建一个Pod，等这个Pod创建成功后再删掉一个旧Pod，直至Pod全部为新Pod。
#### 回滚
回滚也称为回退，即当发现升级出现问题时，让应用回到老的版本。Deployment可以非常方便地回滚到老版本。
例如上面升级的新版镜像有问题，可以执行kubectl rollout undo命令进行回滚。
```
$ kubectl rollout undo deployment nginx -n $namespace_name
deployment "nginx" rolled back
```
Deployment之所以能如此容易的做到回滚，是因为Deployment是通过ReplicaSet控制Pod的，升级后之前ReplicaSet都一直存在，Deployment回滚做的就是使用之前的ReplicaSet再次把Pod创建出来。Deployment中保存ReplicaSet的数量可以使用revisionHistoryLimit参数限制，默认值为10。
