持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情
Deployment
1) deployment的特点
Deployment 为 pod 和 rs 提供了一个声明式定义 (declarative) 方法,用来替代以前的 rc 来方便的管理应用。
这句话有几个要点:
- Deployment采用的是声明式定义的方式. 下面会说什么是声明式定义.
- Deployment不是直接创建pod的, 而是创建RS, 由RS来创建pod.
从图中我们可以看出. deployment创建了RS, RS创建了pod. 当资源清单中的内容发生变化的时候, 比如镜像的版本变了. 这时deployment会创建一个新的rs, 然后新的rs在创建新的pod. 这里创建pod有两种方式:
- 第一种: 销毁重建. 这种方式的含义是: 销毁一个原来rs下的pod, 重建一个新的rs下的pod; 然后再销毁一个原来的rs的pod, 在重建一个新的rs的pod,直到所有旧的pod都被销毁, 所有新的pod都被创建.
- 第二种: 滚动升级. 原来的rs是不会被删除的. 如果要回滚,直接回滚到原来的rs就可以了.
2) 典型的应用场景包括;
- 定义 Deployment 来创建 Pod 和 ReplicaSet
- 可以滚动升级和回滚应用
- 方便扩容和缩容
- 暂停和继续Deployment
3) 命令的定义方式
命令的定义方式有两种: 一种是命令式定义, 另一种是声明式定义
命令式定义
什么是命令式定义呢? 举个例子: 我想要吃冰激凌, 并找要好的同事帮我去买. 我告诉同事,第一步下楼,第二步右转,第三步直走, 第四步看到冰欺凌店, 进入购买. 第五步返回. 这样每一个步骤都定义的很清楚. 比如我们的脚本命令. 就是命令式定义. 如果这时, 我不想吃冰激凌了, 想换成奶茶. 就要重新下命令. 但是同事已经去执行第一个命令了, 如果这时候在下达第二条命令会怎么样呢? 有两种可能: 第一种: 命令正在执行中, 不能重复执行 第二种: 直接重复执行第二条命令.
声明式定义
什么是声明式定义呢? 举个例子: 还是吃冰欺凌, 并找同事去买. 这次不同的是, 我告诉同事去买冰淇淋,就完事了. 至于怎么买去哪里买, 同事自己决定. 我告诉同事不想吃并欺凌了要改成奶茶, 那他就直接去买奶茶. 这就是声明式定义. 我只声明要干什么.
对于数据库来说, 创建数据库就是声明式定义, 我告诉数据库给我创建一个, 他就去创建了. 至于怎么创建, 我不用担心. 批处理任务就是命令式, 第一步怎么做, 第二步怎么做,第三步怎么做等等. 如果执行到一半, 要把需求修改一下, 不好意思, 不可以.
rc/rs是基于命令式定义的. 而deployment是基于声明式定义的.
在k8s中, 基于命令式定义的资源在创建的时候使用create
kubectl create -f ***.yaml
而基于声明式命令定义的资源使用apply
kubectl apply -f ***.yaml
这两个有什么区别呢? 来看一个例子. 首先使用create创建资源 第一步: 创建一个test.yaml文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: wangyanglinux/myapp:v1
ports:
- containerPort: 80
第二步: 创建pod
kubectl create -f test.yaml
第三步: 再次创建pod 会提示pod已经存在, 如下图提示内容.
接下来, 使用apply创建资源 第一步: 创建一个test.yaml文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: wangyanglinux/myapp:v2
ports:
- containerPort: 80
这里就改了一下镜像的版本号 第二步: 创建pod
kubectl create -f test2.yaml
第三步: 修改脚本 将脚本的镜像v1改为v2版本
第四步:再次执行创建pod脚本
如下图, 我们发现会提示deployment.apps/nginx-deployment configured. 表示重新装载成功.
5) 什么时候使用create, 什么时候使用apply
命令式资源, 使用create创建 声明式资源, 使用apply创建.
4) deployment的使用案例
在下面这个案例中, 我们将了解到如下内容
- 如何创建 deployment
- deployment 如何自动扩缩容
- 如果集群支持HPA, 可以为deployment设置自动扩缩容
- 为deployment更新镜像
- 回滚
第一步:定义资源清单
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-controller
namespace: chapter08
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
# namespace: chapter08
labels:
app: nginx
spec:
containers:
- name: nginx
image: wangyanglinux/myapp:v2
ports:
- containerPort: 80
第二步: 创建deployment
kubectl create -f deployment-pod.yaml
如上图, 我们从图中可以得到一下几点
- 创建deployment, 会自动创建rs, rs在创建pod
- deployment的名称是我们在配置文件中定义的名字deployment-pod
- rs的名称是 deployment的名字+rs的随即编号
- pod的名字是 deployment的名字+rs的随机编号+pod的随机编号
第三步: 自动扩缩容
kubectl scale deployment deployment名字 --replicas 副本数
kubectl scale deployment deployment-pod --replicas 10
原来deployment-pod有3个副本, 现在我们将其扩容器10个副本
我们看到副本数有3个扩展成了10个. 扩缩容并没有改变镜像版本, 所以rs还是原来的rs, 只是rs管理的pod变多了
同样, 如果想缩容, 就将副本数变小就可以了
第四步: 如果集群支持HPA, 可以为deployment设置自动扩缩容
kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80
这里的含义是, 比如cpu的使用率超过了80%, 那么就自动扩容. 这里没有集成HPA, 所以暂时测试不了. 后面回来补充
第五步: 更新镜像
kubectl set image 资源类型/资源名 容器名=镜像:版本
kubectl set image deployment/deployment-pod nginx=nginx:1.9.1
我们可以直接设置修改哪个deployment的镜像, 以及镜像的版本号是多少.
从结果中,我们看出, 修改了镜像的版本号, 会重新构建一个新的rs, pod 会逐个替换为新的pod
第六步: 回滚
只有一个历史版本的情况
如果只有一个历史版本, 我们直接回滚就可以了.
kubectl rollout undo deployment/deployment-pod
通过时间,我们可以看出图中的pod是刚刚创建的, 因为时间都是新的。
当有多个历史版本的时候 当我们有多个历史版本的收, 这时候想回滚到某一个版本, 应该怎么办呢?
只要 Deployment 的 rollout 被触发就会创建一个 revision。也就是说当且仅当 Deployment 的 Pod template(如.spec.template)被更改,例如更新template 中的 label 和容器镜像时,就会创建出一个新的 revision。其他的更新,比如扩容 Deployment 不会创建 revision——因此我们可以很方便的手动或者自动扩容。这意味着当您回退到历史 revision 时,只有 Deployment 中的 Pod template 部分才会回退
查看当前deployment的回滚历史
命令:
kubectl rollout history 资源类型/资源名称 -n 命名空间
案例:
kubectl rollout history deployment/nginx-deployment
查询回滚的结果
命令:
kubectl rollout status 资源类型 资源名称 -n 命名空间
案例:
kubectl rollout status deployments nginx-deployment
您可以用kubectl rollout status命令查看 Deployment 是否完成。如果 rollout 成功完成,kubectl rollout status将返回一个0值的 Exit Code
当我们在创建deployment的时候增加参数--record=true, 那么在查询部署的历史的时候, 就会看到每次执行的命令. 下面我们举例说明
下面来举个例子 还是上面的deployment. 我们在创建控制器的时候增加--record=true. 并且更新3次nginx的版本. nginx:1.20.2 nginx:1.21.6 nginx:1.9.1
第一步: 创建控制器
kubectl apply -f deployment-pod.yaml --record=true
注意: 这里加了参数--record
当在创建的时候增加--record=true参数的时候, 我们查询历史版本的时候, 在CHANGE-CAUSE列会显示执行的命令
第二步: 修改控制器中nginx的版本号为1.9.1
kubectl set image deployment/deployment-pod nginx=nginx:1.9.1
上图一共有三个部分
- 第一部分: 是执行修改nginx版本的命令, 修改nginx版本为1.9.1
- 第二部分: 是获取当前的deployment, rs, pod. 我们看到新建了一个rs, 新的rs又创建了pod
- 第三部分: 查看当前可以回滚的历史版本, 一共有两个. 但是, 这里有一点问题, 问题是在执行更新命令的时候没有加--record=true指令. 导致第一次和第二次执行的命令是相同的.
所以, 我们在修改版本的时候, 需要增加版本号
kubectl set image deployment/deployment-pod nginx=nginx:1.9.1 --record=true
这次我们来看看结果
这次就对了, history中的两个版本是不一样的.
第三步: 再次修改nginx的版本号为1.20.2
kubectl set image deployment/deployment-pod nginx=nginx:1.20.2 --record=true
第四步: 再次修改nginx的版本为nginx:1.21.6
kubectl set image deployment/deployment-pod nginx=nginx:1.21.6 --record=true
来看最后一张图, 可以很清晰的看到一共经历了4个版本. 当我们想要回滚的时候, 可以指定版本号进行回滚
第五步: 回滚到nginx:1.9.1版本
kubectl rollout undo deployment/deployment-pod --to-revision=2
这时, 我们的版本恢复到了1.9.1版本, 同时发版历史也更新了, 从数字2更新为数字5. 当再次回滚,我们回滚到第3个版本,这时候会怎么样呢?
可以看到,这次更新到了原来REVISION为3的版本, 然后更新了数字为6。假如这次要你又要更新, 这次更新为版本5,那么5会变成数字7.
第七步: 更新策略
更新策略指的是什么呢? 当deployment更新镜像版本的时候, 会再创建一个rs, rs在创建pod. 这个新的pod怎么创建呢? 有两种方案
- 第一种--重建: 先删除一个原来的, 在创建一个新的. ---这种情况, 每次最多会少1个pod
- 第二种--滚动更新: 先创建一个新的, 再删除一个原来的. ---这种情况, 每次最多会多出来一个pod. k8s默认是第二种--滚动更新. 可以通过参数设置
kubectl explain deploy.spec.strategy.type
[root@master chapter08]# kubectl explain deploy.spec.strategy.type
KIND: Deployment
VERSION: apps/v1
FIELD: type <string>
DESCRIPTION:
Type of deployment. Can be "Recreate" or "RollingUpdate". Default is
RollingUpdate.
因此, k8s有了下面这段话
Deployment 可以保证在升级时只有一定数量的 Pod 是 down 的。默认的,它会确保至少有比期望的Pod数量少一个是up状态(最多一个不可用)\n
Deployment 同时也可以确保只创建出超过期望数量的一定数量的 Pod。默认的,它会确保最多比期望的Pod数量多一个的 Pod 是 up 的(最多1个 surge )**
未来的 Kuberentes 版本中,将从1-1变成25%-25%
前面两句话分别表达了上面的第一种可能和第二种可能. 第三句话是什么意思呢? 原来在升级的时候只能是多一个或者少一个. 但是在未来的k8s版本中将允许每次25%的变化.也就是每次多25%或者少25%. 为什么会设置为25%呢? 因为, 如果有1000个pod, 一个一个替换, 效率太低了. 那么, 当前的k8s到底是什么更新策略呢? 使用命令查看
$ kubectl describe deployment 控制器名
我当前版本的滚动升级策略是25%. 每次允许最多多25%的pod, 每次允许少最多少25%.
如果我们想要修改更新策略可不可以呢? 当然可以, 有两种方法:
第一种方法: 使用edit命令修改
kubectl edit deployment deployment-pod
执行了这个命令, 是进入到etcd中修改.
我们可以在这里修改, 修改完成以后, 保存退出即可. 如果保存退出后没生效, 说明修改的某些属性不允许被修改.
第二种方法: 使用命令修改
kubectl patch deployment nginx-deployment -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'
patch是打补丁的意思.
第八步: 金丝雀部署
什么是金丝雀部署, 简单说一下. 就是现在集群中有4台服务, 开发了新版本. 在集群中增加一台服务, 这台服务作为新版本的测试服务器使用. 当测试通过后, 将集群所有服务都替换为新版本, 撤掉增加的服务.
在k8s中, 实现金丝雀部署非常容易. 两条命令搞定
kubectl set image deploy nginx-deployment nginx=wangyanglinux/myapp:v2 && kubectl rollout pause deploy nginx-deployment
kubectl rollout resume deploy nginx-deployment
上面这两个命令如何使用呢? 下面用案例来介绍:
为了方便, 我们删除之前修改后的deployment-pod, 然后重新创建
kubectl apply -f deployment-pod.yaml --record=true
可以看到集群中有1个deployment, 1个rs, 3个pod. 下面开始金丝雀部署
第一步:配置滚动更新策略
在更新策略的时候, 我们可以设置每次最多可以多出几台服务, 或者最多减少几台服务. 我们测试的时候设置作为灰度的服务器为1台。 所以设置每次最多多出1台服务, 最多减少的服务为0. 通过下面的命令修改。
kubectl edit deployment deployment-pod
然后执行命令,新增服务。
kubectl set image deployment deployment-pod nginx=wangyanglinux/myapp:v2 && kubectl rollout pause deploy deployment-pod
这个命令的含义是什么呢?
- 首先,要修改deployment中nginx镜像的版本号为v2版本
- 但同时,我们设置了暂停策略.
这时候的效果是什么呢? 如下图:
资源已经被更新. 接下来查看pod
我们看到这里多了一个pod. 多出来的这个pod就是用来做灰度的.
第二步: 配置svc
接下来我们创建一个service, 这个service的作用是用来做负载均衡.
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
注意: 这里匹配的pod的标签是app=nginx. 访问端口port是80, pod中的目标端口也是80
kubectl create -f myservice.yaml
然后这时候我们来通过curl 访问nginx的页面
curl 10.111.138.215
多数情况下请求到的是v1版本, 但也会请求到v2版本, 说明我们的金丝雀部署生效了.
第九步: 多版本更新
Rollover多个rollout并行,假如您创建了一个有5个niginx:1.7.9 replica的 Deployment,但是只有3个nginx:1.7.9的副本被创建出来了, 还有两个没有被创建出来.这时候, 又开始更新含有5个nginx:1.9.1副本 的 Deployment。在这种情况下,Deployment 会立即杀掉已创建的3个nginx:1.7.9的 Pod,并开始创建nginx:1.9.1的 Pod。它不会等到所有的5个nginx:1.7.9的 Pod 都创建完成后才开始改变航道
第十步: 清理策略
当我们更新镜像版本的时候, 每次都会创建一个新的rs, 并且会保留原来的rs, 以备回滚恢复使用. 但是随着时间的迁移, 发布的版本次数会越来越多, rs也会越来越多, 而rs这样的信息都是保存在etcd里面的. 量大了, 是很浪费资源的.
对于恢复版本, 我们通常采用另一种做法. 第一次部署的时候, 控制器文件名叫做deployment-pod.yaml 第二次部署的时候叫deployment-pod-2022-03-10-17-17-00.yaml 然后执行kubectl apply -f deployment-pod-2022-03-10-17-17-00.yaml 来更新镜像.
这样我们就不需要通过rollout进行回滚了. 直接找到对应的文件就可以了.
为了避免资源的浪费, 我们通常会将rs的副本数进行控制. 既然有文件作为备份了, 那么也就不需要rs副本了, 直接将其设置为0就可以了
可以通过设置.spec.revisionHistoryLimit项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment 就不允许回退了.
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-pod
spec:
revisionHistoryLimit: 0 # 设置rs的副本数为0
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: wangyanglinux/myapp:v2
ports:
- containerPort: 80