Deployment 声明式地升级应用
现在你已经知道如何将应用程序组件打包进容器,将他们分组到pod中,并为它们提供临时或者持久存储,将密钥或配置文件注入,并可以使pod之间互相通信。这就是微服务化:如何将一个大规模系统拆分成各个独立运行的组件。之后,你将会需要升级你的应用程序。如何升级再kubernetes集群中运行的程序,以及kubernetes如何帮助你实现真正的零停机升级过程。升级操作可以通过使用replicationController 或者 replicaSet实现,但Kubernetes提供了另一种基于ReplicaSet的资源Deployment,并支持声明式地更新应用程序。
看一个简单的例子:
现有一个应用 ,版本为V1 ,运行在Kubernetes的pod中,现在应用的镜像有更新,标记为v2,那么如何将新版本运行的pod替换掉V1版本的pod.
有以下两种方法更新pod:
1. 直接删除所有现有的pod,然后创建新pod
2. 新创建一组pod,等待运行后删除旧pod. 可以一次性创建,一次性删除,也可以一部分一部分操作。
这两种各有优缺点:第一种方法将会导致应用程序在一定的时间内不可用。第二种方法会导致新旧版本同时在线,如果对统一个数据的处理方式不一样,将会给系统带来数据损坏。
暂且不管优缺点,先来看看如何实现。
我们用replicationController来托管pod v1,可以直接通过将pod模版修改为v2,然后再删除旧pod,这时候rc就会按照新模版创建pod.
如果你可以接受短暂的服务不可用,那这是最简单更新pod的方法。
接下来我们用第二种方法。首先要保持两个版本同时在线,所以要双倍的硬件资源。
前提我们是用service来暴露pod的。保持rc v1不变,然后创建 rc v2 ,rc v2的pod模版选择新镜像标签V2. 创建rc v2 ,rc v2将运行pod v2 。等待pod v2运行正常后,我们使用 kubectl set selector 命令来修改service的pod选择器。
这种一次性从v1切到v2的方法即为蓝绿部署,主要问题就是硬件资源要两倍。如果我们不是一次切换,而是一点点切,资源将不需要两倍。比如你创建rc v2,成功后,缩容 rc v1 ,更改service的pod选择器。然后再扩容rc v2,在缩容 v1 这就是滚动更新。那么这个就要求你的应用允许存在两个不同版本,具体要根据实际需求来操作了。
以上第二种方法的两种情况,我们大概知道是怎么回事了。但我们在实际操作中,要创建两个rc,并且要修改service的pod selector. 这要是用滚动更新,pod副本越多,我们手动操作的次数就越多,手动操作越多越容易出现问题。那么问题来了,难道kubernetes没有提供自动完成这些操作的方法吗?答案是提供了。k8s中使用kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
我们来通过一个例子了解下:
用rc创建一个应用v1 ,并使用service的 loadbalancer将服务对外暴露。
apiVersion: v1
kind: ReplicationController
metadata:
name: kubia-v1
spec:
replicas: 3
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v1
name: nodejs
---
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
type: LoadBalancer
selector:
app: kubia
ports:
- port: 80
targetPort: 8080
查看loaderbalancer的IP
#kubectl get svc kubia
访问测试
# while true; do curl http://IP; done
this is v1 running in pod 主机名
接下来用v2版本升级,this is v2 running in pod 主机名
kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
使用 kubia v2版本应用替换运行着kubia-v1 的replicationController,将新的复制控制器命名为kubia-v2,并使用luksa/kubia:v2作为容器镜像。
kubectl 通过复制kubia-v1 的replicationController并在其pod模版中改变镜像版本。如果仔细观察控制器的标签选择器,会阿贤它也被做了修改。它不仅包含一个简单的app=kubia标签,而且还包含一个额外的deployment标签,为了由这个ReplicationController管理,pod必须具备这个标签。这也是为了避免使用新的和旧的RC来管理同一组Pod.但是即使新创建的pod添加了deployment标签,pod中还是有app=kubia标签,所以不仅新的RC要加deployment标签,旧的RC同样也要加上deployment标签,标签的值为 一个镜像hash(先这样理解)。要保证旧的RC添加deployment标签后依然可以管理之前创建的pod,因此也要修改旧pod,进行添加标签,而实际上正是在修改旧RC之前,kubectl修改了旧pod的标签:
kubectl get po --show-labels 进行查看标签
设置完成后,就开始执行更新操作了,过程就是我们上面描述的滚动更新过程。
为什么 kubectl rolling-update已经过时
我们可以在使用rolling-update命令的时候添加 --v 6 来提供日志级别,使得所有kubectl 发起的到API服务器的请求都会被输出。
你会看到一个put请求:
/api/v1/namespace/default/replicationcontrollers/kubia-v1
它是表示kubia-v1 ReplicationController资源的RESTful URL.这些请求减少了RC的副本数,这表明伸缩的请求是由kubectl 客户端执行的,而不是kubernetes master执行的。那么当kubectl执行升级时失去了网络连接,升级过程就会中断。对于中断后的结果处理起来将很麻烦。所以我们想尽量把这个过程让master负责。这就引出了DeployMent资源。
Deployment是一个高阶资源,replicationController和replicaSet都被认为是底层的概念。当创建一个Deployment时,ReplicaSet资源就会被创建,实际的pod是由Deployment的Replicaset创建和管理的,而不是由Deployment直接创建和管理的。
接下来我们创建一个简单的deployment,将上面的replicationController稍作修改:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubia
spec:
replicas: 3
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v1
name: nodejs
因为之前RC只维护和管理了一个特定的版本的pod,并需要命名为kubia-v1,而一个deployment资源高于版本本身。可以同时管理多个版本的pod,所以在命名时不需要指定应用的版本号。
kubectl create -f kubia-deployment-v1.yaml --record
使用 --record选项会记录历史版本号,在之后操作中非常有用。
kubectl get deployment kubia
kubectl describe deployment kubia
还有一个命令,专门用于查看部署状态:
kubectl rollout status deployment kubia
查看pod,会发现使用deployment资源部署的pod名称有一定的规律,
deployment的名字 + replicaset pod模版的hash + 随机数,名称我曾怀疑是template模版的名字 + 模版的hash+随机数。经过验证发现不是,甚至可以不给模版定义name.
如:
kubia-15098375-otnsd
kubia-15098375-djc6s
而rc创建出来的名称是 : rc名称 + 随机数
kubia-v1-kgysg
可以通过查看replicaSet来确认pod模版hash
kubectl get replicasets
kubia-15098375 ...
deployment正是根据pod模版的hash值,对给定版本的pod模版创建相同的(或使用已有的)ReplicaSet.
Deployment的高明之处
有不同的更新策略
Recreate 策略在删除旧的Pod之后才开始创建新的Pod。如果不允许多个版本共存,使用这个,但会有短暂的不可用。
RollingUpdate 策略会渐进地删除旧的pod,于此同时创建新的pod.这是deployment默认使用的策略。如果支持多个版本共存,推荐使用这个。
我们来演示下deployment滚动升级的过程。
在演示之前我们先减慢滚动升级的速度,以方便我们观察
kubectl path deployment kubia -p '{"spec": {"minReadySeconds": 10} }'
使用path对于修改单个或少量资源属性非常有用,不需要在通过编辑器编辑,使用minReadySeconds 配置正常检查后多少时间才属于正常。
注:使用patch命令更改Deployment的自有属性,并不会导致pod的任何更新,因为pod模版并没有被修改。更改其他Deployment的属性,比如所需要的副本数或部署策略,也不会触发滚动升级,现有运行中的pod也不会受影响。
触发滚动升级
kubectl set image deployment kubia nodejs=luksa/kubia:v2
执行完成后pod模版的镜像会被更改为luksa/kubia:v2
deployment的优点
使用控制器接管整个升级过程,不用担心网络中断。
仅仅更改pod模版即可。
注:如果Deployment中的pod模版引用了一个ConfigMap(或secret),那么更改ConfigMap字眼本身将不会触发升级操作。如果真的需要修改应用程序的配置并想触发更新的话,可以通过创建一个新的ConfigMap并修改pod模版引用新的ConfigMap.
Deployment背后完成的升级过程和kubectl rolling-update命令相似。
一个新的ReplicaSet会被创建然后慢慢扩容,同时之前版本的Replicaset会慢慢缩容至0
deployment的另外一个好处是可以回滚
kubectl rollout undo deployment kubia
deployment会被回滚到上一个版本
undo命令也可以在滚动升级过程中运行,并直接停止滚动升级。在升级过程中一创建的pod会被删除并被老版本的pod替代。
显示deployment的滚动升级历史
kubectl rollout history deployment kubia
reversion change-cause
2 kubectl set image deployment kubia nodejs=luksa/kubia:v2
3 kubectl set image deployment kubia nodejs=luksa/kubia:v3
还记得创建Deployment的时候--record 参数吗?如果不给这个参数,版本历史中的Change-cause这一栏会空。这也会使用户很难辨别每次的版本做了哪些修改。
回滚到一个特定的Deployment版本
kubectl rollout undo deployment kubia --to-reversion=1
这些历史版本的replicaset都用特定的版本号保存Deployment的完整的信息,所以不应该手动删除ReplicaSet。如果删除会丢失Deployment的历史版本记录而导致无法回滚。
ReplicaSet默认保留最近的两个版本,可以通过制定Deployment的reversionHistoryLimit属性来限制历史版本数量。apps/v1beta2 版本的Deployment默认值为10.
控制滚动更新的速率
maxSurge 最多超过期望副本数多少个pod,默认为25%,可以设置为整数
maxUanavailable 最多允许期望副本数内多少个pod为不可用状态。
看图比较容易理解
暂停滚动更新:
kubectl rollout pause deployment kubia
恢复滚动更新:
kubectl rollout resume deployment kubia
阻止出错版本的滚动升级
使用minReadySeconds属性指定至少要成功运行多久之后,才能将其视为可用。在pod可用之前,滚动升级的过程不会继续。是由maxSurge属性和maxUanavailable属性决定。
例子:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubia
spec:
replicas:3
minReadySeconds: 10
strategy:
rollingUpdate:
maxSurge: 1
maxUnavilable: 0
type: RollingUpdate
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v3
name: nodejs
readinessProbe:
periodSeconds: 1 1秒探测一次
httpGet:
path: /
port: 8080
kubectl apply -f kubia-deployment-v3-with-readinesscheck.yaml
kubectl rollout status deployment kubia
就绪探针是如何阻止出错版本的滚动升级的
首先luksa/kubia:v3镜像应用前5个应用是正常反馈,后面会500状态返回。因此pod会从service的endpoint中移除。当执行curl时,pod已经被标记为未就绪。这就解释了为什么请求不会到新的pod上了。
使用kubectl rollout status deployment kubia查看状体,发现只显示一个新副本启动,之后滚动升级便没有进行下去,因为新的pod一直处于不可用状态。即使变为就绪状态后,也至少需要保持10秒,才是真正可用。在这之前滚动升级过程将不再创建任何新的pod,因为当前maxUnavailable属性设置为0,所以不会删除任何原始的pod。如果没有定义minReadySeconds,一旦有一次就绪探针调用成功,便会认为新的pod已经处于可用状态。因此最好适当的设置minReadySeconds.另外就绪探针默认间隔为1秒。
deployment资源介绍完结。
- deployment:声明式的升级应用
9.1.使用RC实现滚动升级 #kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2 使用kubia-v2版本应用来替换运行着 ...
- Kubernetes学习笔记(八):Deployment--声明式的升级应用
概述 本文核心问题是:如何升级应用. 对于Pod的更新有两种策略: 一是删除全部旧Pod之后再创建新Pod.好处是,同一时间只会有一个版本的应用存在:缺点是,应用有一段时间不可用. 二是先创建新Pod ...
- Spring声明式事务管理与配置详解
转载:http://www.cnblogs.com/hellojava/archive/2012/11/21/2780694.html 1.Spring声明式事务配置的五种方式 前段时间对Spring ...
- Spring声明式事务管理与配置介绍
转至:http://java.9sssd.com/javafw/art/1215 [摘要]本文介绍Spring声明式事务管理与配置,包括Spring声明式事务配置的五种方式.事务的传播属性(Propa ...
- 云原生下基于K8S声明式GitOps持续部署工具ArgoCD实战-上
@ 目录 概述 定义 工作原理 主要组件 核心概念 环境准备 概述 安装Kubekey 创建K8S 安装K9S OpenLB 安装ArgoCD 安装 ArgoCD CLI 从Git库中创建一个应用程序 ...
- 声明式API replica controller vs replica set 对比
1.在命令式API中,你可以直接发出服务器要执行的命令,例如: “运行容器”.“停止容器”等. 在声明性API中,你声明系统要执行的操作,系统将不断向该状态驱动. 可以想象成手动驾驶和自动驾驶系统.( ...
- 【Kubernetes】深入解析声明式API
在Kubernetes中,一个API对象在Etcd里的完整资源路径,是由:Group(API组).Version(API版本)和Resource(API资源类型)三个部分组成的. 通过这样的结构,整个 ...
- AngularJS应用开发思维之1:声明式界面
这篇博客之前承接上一篇:http://www.cnblogs.com/xuema/p/4335180.html 重写示例:模板.指令和视图 AngularJS最显著的特点是用静态的HTML文档,就可以 ...
- 【Dalston】【第三章】声明式服务调用(Feign)
当我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下,并且显得好傻.那 ...
随机推荐
- 【转】Redis为什么用跳表而不用平衡树?
Redis里面使用skiplist是为了实现sorted set这种对外的数据结构.sorted set提供的操作非常丰富,可以满足非常多的应用场景.这也意味着,sorted set相对来说实现比较复 ...
- 解决栏登 F~~~秋~~~之后只有火狐能上网的问题
我发现F~~~完秋~~~只有火狐可以上网,惊了,后来发现是没有关代理服雾气 1.打开Chrome->设置 2.翻到最后点击显示高级设置 3.找到网络标签,点击更改代理服务器按钮 4.点击连接标签 ...
- 洛谷P1578 奶牛牧场(悬线法思想)
题目 悬线法的思想--即扫描线的思想,每个矩阵必定是由两个障碍来构成左右边界或者上下边界. 如果此两个障碍组成了左右边界,枚举这两个障碍中途更新这两个障碍之间的矩阵上下边界,并且更新最大值. 考虑如何 ...
- iptables 表和链的对应关系
filter表 主要用于对数据包进行过滤,根据具体的规则决定是否放行该数据包(如DROP.ACCEPT.REJECT.LOG).filter 表对应的内核模块为iptable_filter,包含三个规 ...
- 第02组 Alpha冲刺(2/6)
队名:無駄無駄 组长博客 作业博客 组员情况 张越洋 过去两天完成了哪些任务 任务分配.进度监督 提交记录(全组共用) 接下来的计划 沟通前后端成员,监督.提醒他们尽快完成各自的进度 还剩下哪些任务 ...
- 图解 Java 垃圾回收机制,写得非常好!
阅读本文大概需要 3.7 分钟. 翻译:Rhys_Lee, AzureSora, 溪边九节, 小小菜鸟鸡 blog.csdn.net/zl1zl2zl3/article/details/9090408 ...
- 分布式系统全局唯一ID生成
一 什么是分布式系统唯一ID 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识. 如在金融.电商.支付.等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息, ...
- leetcode 763. 划分字母区间
题目描述: 字符串 S 由小写字母组成.我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段.返回一个表示每个字符串片段的长度的列表. 示例 1: 输入: S = "a ...
- github打开慢,甚至打不开,怎么办,解决方案方法
有人使用github后,在某些网络下发现打开慢,甚至打不开,这都是因为他是国外站:目前互联网的连接机制导致超过一定的路由节点的连接就会出现这个问题,解决办法就是直接告诉本机ip.不要先层层询问域名转i ...
- 从零开始学C语言
从零开始学C语言 @阆苑祁寒 更新时间:2019-09-13 写在前面:本文从一个初学者的角度,给出了对C语言的简单理解.如有谬误,敬请指出! Week1——基本语法 #include <std ...