暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

kubernetes - 控制器 - Deployment - V2

小朱哥的技术人生 2020-05-27
259


暂停和继续 Deployment

你可以先暂停一个 Deployment 然后在触发一个或多个更新,最后在继续 (resume)该 Deployment ,这种做法使得你可以在暂停和继续中间对 Deployment 做多次更新,而无需触发不必要的滚动更新

  • 创建 Deployment(将文件命名为 nginx-deployment.yaml)
  apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
apps: nginx
spec:
replicas: 3
selector:
matchLabels:
apps: nginx
template:
metadata:
labels:
apps: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

创建 Deployment

  kubectl apply -f nginx-deployment.yaml

  • 执行命令查看 Deployment
  kubectl get deployment

执行结果

  NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment 3/3 3 3 5s

  • 执行命令查看 ReplicaSet 信息
  kubectl get rs

输出结果

  NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-5bdb998678 3 3 3 56s

  • 执行命令暂停 Deployment
  kubectl rollout pause deployment.v1.apps/nginx-deployment

输出结果

  deployment.apps/nginx-deployment paused

  • 执行命令更新镜像版本
  kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.18.0

输出结果

  deployment.apps/nginx-deployment image updated

  • 执行命令查看更新版本
  kubectl rollout history deployment.v1.apps/nginx-deployment

输出的结果

  deployment.apps/nginx-deployment 
REVISION CHANGE-CAUSE
1 <none>

可以查看到并没有生成 Deployment 的新版本

  • 执行命令查看 ReplicaSet 是否更新
  kubectl get rs

输出结果

  NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-5bdb998678 3 3 3 7m36s

跟上执行的 ReplicaSet 信息一样 说明更新并没有执行

  • 如果需要,你可以针对 Deployment 执行多次更新,比如,更新 Deployment 的 resource 限制
  kubectl set resources deployment.v1.apps/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi

输出的结果

  deployment.apps/nginx-deployment resource requirements updated

  • 执行命令,确保 Deployment 信息被修改
  kubecrl describe deployment nginx-deployment

输出结果

  Name:                   nginx-deployment
Namespace: default
CreationTimestamp: Wed, 20 May 2020 14:55:57 +0800
Labels: apps=nginx
Annotations: deployment.kubernetes.io/revision: 1
kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"apps":"nginx"},"name":"nginx-deployment","namespace":"...
Selector: apps=nginx
Replicas: 3 desired | 0 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: apps=nginx
Containers:
nginx:
Image: nginx:1.18.0
Port: 80/TCP
Host Port: 0/TCP
Limits:
cpu: 200m # 已经修改
memory: 512Mi # 已经修改
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing Unknown DeploymentPaused
OldReplicaSets: nginx-deployment-5bdb998678 (3/3 replicas created)
NewReplicaSet: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 12m deployment-controller Scaled up replica set nginx-deployment-5bdb998678 to 3

暂停 Deployment 之前的信息当前仍然在起作用,而暂停 Deployment 之后,修改的 Deployment 信息尚未生效,因为该 Deployment 被暂停了

  • 最后执行命令,将暂停的 Deployment 继续 (resume) 运行起来,可使前面的所有的变更,一次性生效
  kubectl rollout resume deployment.v1.apps/nginx-deployment

输出结果

  deployment.apps/nginx-deployment resumed

  • 执行命令 观察滚动更新的进展
  kubectl get rs -w

输出结果

  NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-5bdb998678 3 3 3 3m15s
nginx-deployment-6d4df58459 1 1 0 1s
nginx-deployment-6d4df58459 1 1 1 3s
nginx-deployment-5bdb998678 2 3 3 3m17s
nginx-deployment-6d4df58459 2 1 1 3s
nginx-deployment-5bdb998678 2 3 3 3m17s
nginx-deployment-6d4df58459 2 1 1 3s
nginx-deployment-5bdb998678 2 2 2 3m17s
nginx-deployment-6d4df58459 2 2 1 3s
nginx-deployment-6d4df58459 2 2 2 3s
nginx-deployment-5bdb998678 1 2 2 3m17s
nginx-deployment-6d4df58459 3 2 2 3s
nginx-deployment-5bdb998678 1 2 2 3m17s
nginx-deployment-6d4df58459 3 2 2 3s
nginx-deployment-5bdb998678 1 1 1 3m17s
nginx-deployment-6d4df58459 3 3 2 3s
nginx-deployment-6d4df58459 3 3 3 5s
nginx-deployment-5bdb998678 0 1 1 3m19s
nginx-deployment-5bdb998678 0 1 1 3m19s
nginx-deployment-5bdb998678 0 0 0 3m19s

  • 查看 ReplicaSet 的最终状态
  kubectl get rs

输出结果

  NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-5bdb998678 0 0 0 3m31s
nginx-deployment-6d4df58459 3 3 3 17s

你不能回滚 (rollback) 一个已经暂停 (pause)的 Deployment,除非你继续 (resume)Deployment

查看 Deployment 状态

Deployment 生命周期中将会进入不同的状态,这些状态有可能是

  • Progressing 正在执行滚动更新
  • complete  已经完成
  • fail to progress Deployment 失败

Progressing状态

当下任何一个任务正在执行时,kubernetes 将 Deployment 的状态标记为 progressing

  • Deployment 正在创建一个先的 ReplicaSet
  • Deployment 正在 scale up 其最新的 ReplicaSet
  • Deployment 正在 scale down 其旧的 ReplicaSet
  • 新的 Pod 变为 就绪(ready) 或者 可用 (available)

你可以使用 kubectl rollout status
监控 Deployment 滚动更新的状态

complete 状态

如果 Deployment 符合以下条件,kubernetes 将 Deployment 的状态标记为 complete

  • 该 Deployment 中的所有 Pod 副本都已经被更新到指定的最新版本
  • 该 Deployment 中的所有 Pod 副本都处于可用(available)状态
  • 该 Deployment 中没有旧的 ReplicaSet 运行

你可以使用 kubectl rollout status
查看 Deployment 是否已经处于 complete 状态,如果是则命令的退出码为 0 ,例如:

# 执行命令
kubectl rollout status deployment.v1.apps/nginx-deployment

#
查看命令退出码
echo $?

#
结果
0

Failed 状态

Deployment 在更新其最新的 ReplicaSet 时,可能会卡住而不能达到一个 complete 状态,如下原因可能会导致这个状况发生:

  • 集群资源不够用
  • 就绪检查(readliness probe) 失败
  • 镜像抓取失败
  • 权限不够
  • 资源限制
  • 应用程序的配置错误导致启动失败

指定 Deployment 定义中的.spec.progressDeadlineSeconds
字段,Deployment Controller 在等待时长后,将Deployment 标记为处理失败,例如

# 执行命令
kubectl path deployment.v1.apps/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":60}}'

#
输出结果
deployment.apps/nginx-deployment patched

等待时间到达后,Deployment Controller 将在 Deployment 的.status.conditions
字段添加如下:

DeploymentCondition

  • Reason=ProgressDeadlineExceeded
  • Status=False
  • Type=Progressing

除了添加一个 Reason=ProgressDeadlineExceeded 的 DeploymentCondition 到 .status.conditions
字段以外,kubernetes 不会对卡住的 Deployment 做任何更改,你可以执行 kubectl rollout undo
命令,回滚到上一个版本

如果你暂停了 Deployment kubernetes 将不会检查.spec.progressDeadlineSeconds

如果你设定的 .spec.progressDeadlinSeconds
太短了,或者其他原因,你可能发现 Deployment 的状态改变出错。例如,假设你的集群缺乏足够的资源,执行命令 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
,Deployment 的 Status 结果如下所示:

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 的 .spec.progressDeadlinSeconds
超时,Kubernetes 将更新 Deployment 的 Processing condition 如下:

Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing False ProgressDeadlineExceeded
ReplicaFailure True FailedCreate

为了解决资源不足的问题,你可以尝试:

  • scale down 您的 Deployment
  • scale down 其他的 Deployment
  • 向集群中添加计算节点

如果资源足够,并且 Deployment 完成了其滚动更新,您将看到 Deployment 中出现一个成功的 condition (status=True 且 Reason=NewReplicaSetAvailable)

Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable


  • Type=Available
    Status=True
    代表您的 Deployment 具备最小可用的 Pod 数(minimum availability)Minimum availability 由 Deployment 中的 strategy 参数决定
  • Type=Progressing
    Status=True
    代表您的 Deployment 要么处于滚动更新的过程中,要么已经成功完成更新并且 Pod 数达到了最小可用的数量

命令 kubectl rollout status
可用于检查 Deployment 是否失败,如果该命令的退出码不是 0,则该 Deployment 已经超出了 .spec.progressDeadlinSeconds
指定的等候时长,例如,执行命令 kubectl rollout status deployment.v1.apps/nginx-deployment
,输出结果如下所示:

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline
$ echo $?
1

操作处于 Failed 状态的 Deployment

您可以针对 Failed 状态下的 Deployment 执行任何适用于 Deployment 的指令,例如:

  • scale up scale down
  • 回滚到前一个版本
  • 暂停(pause)Deployment,以对 Deployment 的 Pod template 执行多处更新

清理策略

可以在 Deployment 中设置 .spec.revsionHistoryLimit
,指定该 Deployment 保留多少个旧的 ReplicaSet ,超出这个数字在后台进行垃圾回收,默认为10

如果改字段设置为0 kubernetes 将清理掉该 Deployment 的所有历史版本,因此你将无法对该 Deployment 进行回滚操作 kubectl rollout undo

部署策略

通过 Deployment 中 .spec.strategy
字段,可以指定使用滚动更新 RollingUpdate
的部署策略还是使用重新创建 Recreate
的部署策略

字段名称可选值字段描述
type (类型)滚动更新(RollingUpdate)/重新创建(Recreate)如果选择重新创建,Deployment将先删除原有副本集中的所有 Pod,然后再创建新的副本集和新的 Pod, 此时更新过程中将出现一段应用程序不可用的情况;
maxSurge (最大超出副本数)数字/百分比滚动更新过程中,可以超出期望副本数的最大值。该取值可以是一个绝对值(例如:5),也可以是一个相对于期望副本数的百分比(例如:10%);如果填写百分比,则以期望副本数乘以该百分比后向上取整的方式计算对应的绝对值;当最大超出副本数 maxUnavailable 为 0 时,此数值不能为 0;默认值为 25%。例如:假设此值被设定为 30%,当滚动更新开始时,新的副本集(ReplicaSet)可以立刻扩容,但是旧 Pod 和新 Pod 的总数不超过 Deployment 期待副本数(spec.repilcas)的 130%。一旦旧 Pod 被终止后,新的副本集可以进一步扩容,但是整个滚动更新过程中,新旧 Pod 的总数不超过 Deployment 期待副本数(spec.repilcas)的 130%
maxUnavailable (最大不可用副本数)数字/百分比滚动更新过程中,不可用副本数的最大值。该取值可以是一个绝对值(例如:5),也可以是一个相对于期望副本数的百分比(例如:10%);如果填写百分比,则以期望副本数乘以该百分比后向下取整的方式计算对应的绝对值;当最大超出副本数 maxSurge 为 0 时,此数值不能为 0;默认值为 25%;例如:假设此值被设定为 30%,当滚动更新开始时,旧的副本集(ReplicaSet)可以缩容到期望 < 副本数的 70%;在新副本集扩容的过程中,一旦新的 Pod 已就绪,旧的副本集可以进一步缩容,整个滚动更新过程中,确保新旧就绪副本数之和不低于期望副本数的 70%

金丝雀发布(灰度发布)

如果你想使用 Deployment 将最新的应用程序版本发布给一部分用户(或服务器),你可以为每个版本创建一个 Deployment 此时,应用程序的新旧两个版本都可以同时获得生产上的流量

  • 部署第一个版本

    第一个 Deployment 版本包含 3 个 Pod 副本,Service 通过 label selector apps:nginx
    选择对应的 Pod,nginx的标签为1.7.9

  apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
apps: nginx
spec:
replicas: 3
selector:
matchLabels:
apps: nginx
template:
metadata:
labels:
apps: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
apps: nginx
spec:
selector:
apps: nginx
ports:
- name: nginx-port
protocol: TCP
port: 80
nodePort: 32600
targetPort: 80
type: NodePort

  • 假设此时你要发布新的版本 nginx:1.18.0 ,可以创建另一个 Deployment
  apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-canary
labels:
apps: nginx
track: canary
spec:
replicas: 3
selector:
matchLabels:
apps: nginx
track: canary
template:
metadata:
labels:
apps: nginx
track: canary
spec:
containers:
- name: nginx
image: nginx:1.18.0

因为 Service 的 LabelsSelector 是 apps:nginx
,由nginx-deployment
nginx-deployment-canary
创建的 Pod 都带有标签 apps:nginx
,所以 Service 的流量将会在两个 release 之间分配

在新旧版本之间,流量分配的比例为两个版本副本数的比例,此处为 1:3

  • 当你确定了新的版本没有问题了之后,可以将 nginx-deployment
    的镜像标签改为新版本的镜像标签,并在完成对nginx-deployment
    滚动更新之后,删除 nginx-deployment-canary
    这个 Deployment

局限性:

安装 kubernetes 默认支持的这种方式进行金丝雀发布,有一定的局限性:

  • 不能根据用户注册时间,地区等请求中的内容属性进行流量分配
  • 同一个用户如果多次调用同一个 Service,有可能第一次请求到旧的 Pod,第二次请求到新的 Pod

在 kubernetes 中不能解决上述局限性的问题

kubernetes Service 只在 TCP 层面解决负载均衡的问题,并不对请求响应的消息内容做解析和识别,如果想要更完善的实现金丝雀发布,可以考虑如下三种方式:

  • 业务代码编排实现
  • Spring Cloud 灰度发布
  • Istio 灰度发布

文章转载自小朱哥的技术人生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论