kubia-rc-and-service.yaml
---
apiVersion: v1
kind: ReplicationController
metadata:
name: kubia-v1
labels:
app: kubia
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
注意:pod在创建时拉取镜像的策略,如果镜像指定了tag为 latest,则imagePullPolicy 默认策略为 always,如果是其他的tag 如 v1,则策略默认为 IfNotPresent。
节点拉取镜像后,就会将镜像存储在本地,如果所需要镜像的tag已存在,则不会再去拉取。所以更新镜像后不要直接替换原有的tag(latest除外),避免创建新pod的时候使用了旧的的镜像
使用 rolling-update命令对ReplicationController 进行升级,需要指定需要升级的ReplicationController及新的ReplicationController,并指定想要升级的镜像。
kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
升级过程:
1、kubectl 通过复制kubia-v1 的ReplicationController,并将其pod模板中镜像替换成新的镜像。
2、修改新旧ReplicationController控制器的标签选择器,添加deployment标签,且旧的pod与新创建的pod都会添加各自不一样的deployment标签。
3、通过伸缩两个ReplicationController,将旧的pod替换成新的pod,在这过程中kubia-v2.replicas由0加到3,而kubia-v1.replicas由3减到0。
整个过程中服务一直保持可用状态。但是rolling-update 已经过时。当触发更新时,使用 --v 6 选项会提高日志级别,使得所有kubectl发起到API服务器的请求,全部打印出来。这时可用看到一个PUT请求:/api/v1/namespace/defalut/replicationcontroller/kubia-v1 它表示 kubia-v1 ReplicationController资源的RESTFul URL。这些请求减少了ReplicationController 的副本数,这表明伸缩的请求是由kubectl客户端执行的,而不是 kubernetes master执行的。
在上述升级的过程当中,如果kubectl执行升级过程中网络断开,升级过程将中断,pod和ReplicationController最终会保持这个状态。
2、使用Deployment 声明式地升级应用
Deployment 是一种更高阶的资源,用于部署应用程序并以声明的方式升级应用,而不是通过rc、rs进行部署,它们都被认为是更底层的概念。
当创建Deployment时,ReplicaSet资源也会随之创建,在使用Deployment时,实际的pod是由Deployment的ReplicaSet创建和管理的,而不是Deployment直接创建和管理的。
Deployment 与rc比较类似,都是由标签选择器、期望副本数及pod模板组成。它还包含另一个字段,指定部署策略,该策略定义在修改Deployment 资源时应该如何执行更新。
deployment-v1.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubian
spec:
replicas: 3
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v1
name: nodejs
创建Deployment: kubectl create -f deployment-v1.yaml --recoed
加上 --record 选项,这个选项会记录历史版本号,在以后的操作中非常有用
可以直接使用 kubectl describe deployment 或 kubectl get deployment 来查看Deployment的详细消息,也有专门的的命令,专门用于查看部署状态:
kubectl rollout status deployment kubia
Deployment 如何创建ReplicaSet 和 Pod
通过kubectl get rs及 kubectl get pod 分别获取到此时创建的ReplicaSet和Pod的状态,可以看到它们的名称中都包含了一串相同的随机数。
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
kubian-66b4657d7b 3 3 3 14m
$ kubectl get pod
kubian-66b4657d7b-6n24p 1/1 Running 0 15m
kubian-66b4657d7b-w2pfm 1/1 Running 0 15m
kubian-66b4657d7b-wtghp 1/1 Running 0 15m
其实 “66b4657d7b” 是对应Deployment与ReplicaSet中的pod模板的哈希值。Deployment会创建多个ReplicaSet,用来对应和管理一个版本的模板。像这样使用pod模板的哈希值,可以让Deployment始终对给定版本的pod模板创建相同的ReplicaSet。
因为新的pod标签与前面创建的service 标签选择器相匹配,所以可以通过之前创建的服务来访问他们。
Deployment升级
只要修改Deployment资源中定义的pod模板,Kubernetes会自动的将实际的系统状态收敛为资源中定义的状态。升级所需要做的就是在部署的pod模板中修改镜像tag,Kubernetes会收敛系统,匹配期望的状态。
Deployment有不同的升级策略,默认是RollingUpdate,执行滚动策略。另一种是Recreate,它会一次性删除所有旧版本的pod,删除完之后再创建新的pod。如果应用支持多个版本同时提供服务,则推荐使用默认的策略RollingUpdate,如果不支持的话,则使用Recreate策略。
减慢滚动升级速度,以便观察跟演示
kubectl patch deployment kubian -p ‘{“spec”: {“minReadySeconds”: 10}}’
patch 对修改单个或少量资源属性非常方便。使用patch命令修改Deployment的自有属性,并不会导致pod的任何更新,因为pod的模板没有被更新。修改Deployment其他属性,比如升级策略等,现有在运行的pod不会受到影响。
触发升级
要触发升级,需要将pod的镜像修改为luksa/kubia:v2 。和直接编辑Deployment资源的YAML文件或使用patch命令更改镜像有所不同,将使用kubectl set image 命令来修改任何容器资源的镜像(rc、rs、Deployment等)
kubectl set image deployment kubian nodejs=luksa/kubia:v2
当执行完命令后,kubian Deployment的pod模板内的镜像将会更改为luksa/kubia:v2。
可以通过describe 查看deployment的描述详情,发现nodejs的镜像已经变成v2了
注意:如果Deployment中的pod模板引用了一个ConfigMap或Secret,当ConfigMap这些资源发生变化的时候,引用他们的pod的升级是不会被触发的。如果真的需要应用程序的配置并想触发更新的话,可以通过创建一个新的ConfigMap并修改pod模板来引用它们
整个升级的过程,是由运行在Kubernetes上的一个控制器处理和完成的,而不再是运行kubectl rolling-update 命令的kubectl客户端控制的。Deployment完成升级的过程与kubectl rolling-update命令其实很相似,一个新的ReplicaSet被创建,并慢慢的扩展,而旧的ReplicaSet 会慢慢缩容至0。
与rc不同的是,升级完后,旧的rc会被删除,而旧的rs会被保留。并且因为没有直接的去创建rs,所以这里些rs也不要用户去维护。
Deployment 回滚
kubectl rollout undo deployment kubian
回滚到上一个版本,即使是在升级过程中,也能停止升级,并回滚到上一版本
之所以能够如此快速的回滚,是因为Deployment 始终保留着升级记录,历史版本号会保存在ReplicaSet中。可以使用命令看到升级记录。
kubectl rollout history deployment kubian
在升级记录中,可以看到CHANGE-CAUSE 一栏的信息,有些存在有些为空。这就是在升级时带有 --record 参数的作用,它会将升级时的参数被Kubernetes保留在ectd中,并持久化,以便于我们后面查看。
回滚到指定版本
kubectl rollout undo deployment kubian --to-revision=1
在升级记录中每个版本都会有对应的版本号,回滚时可用根据该版本号,回滚到该版本。
过多的ReplicaSet的旧版本,会导致ReplicaSet列表过于混乱,并且也浪费资源。我们可以指定Deployment中 revisionHistoryLimit 属性来限制历史版本的数量。
控制滚动升级的速率
在滚动升级过程中由两个属性会决定一次替换多少个pod:maxSurge、maxUnavailable。可以通过设置Deployment中strategy字段下rollingUpdate的子属性。
spec:
strtegy:
Type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
maxSurge: 决定了Deployment配置中期望pod数之外,最多允许超出的pod实例数量。默认值为Replica数量的25%,然后取四舍五入。
maxUnavailable:表示在滚动升级过程中,相对于期望副本数能允许多少pod实例处于不可用状态。默认值也是25%,所以可用pod实例数不得低于期望值得75%。
需要注意的是,不同的Deployment版本,这两个默认值可能不一样。
暂停、恢复滚动更新
滚动升级时,我们要确认更新后的效果怎么样,是否达到预期的效果。我们可以先升级一部分,当达到预期结果后,我们再全部更新。可以直接通过运行一个额外新的pod来完成测试,也可以通过rc、rs、Deployment来实现上述的要求。但是Deployment本身就带有这么一个选项,通过暂停升级,方便用户再继续完成升级前来验证新的版本是否符合要求。当达到时,再恢复更新。
kubectl set image deployment kubian nodejs=luksa/kubia:v4 进行升级
立马执行滚动更新暂停 kubectl rollout pause deployment kubian
此时一个新的pod将会创建,原先的版本继续运行,一旦新的pod创建成功,旧版的pod本将会被新的pod替换,服务的一部分将切换到新的pod中,这样就相当于运行了一个金丝雀版本。金丝雀发布是一种可以将应用程序的出错版本
和影响控制到最新的技术。当验证新版本工作之后,再将剩余的pod继续升级或回滚到上一版本。
恢复滚动升级 kubectl rollout resume deployment kubian
在滚动升级过程中,想要在一个确切的位置暂停滚动升级,目前还是无法做到的,有可能以后会有一种新的滚动升级策略。
阻止出错版本的滚动升级
minReadySeconds 属性指定莘创建的Pod至少要成功运行多久以后,才能视为可用。在Pod可用之前,滚动升级不会继续。当所有容器的就绪探针返回成功时,pod就被标记为就绪状态。如果一个新的pod运行出错,就绪探针返回失败,并且在minReadySeconds 时间内就绪探针出现了失败,name新版本滚动升级将被阻止。
使用这个属性可以让Kubernetes在pod就绪之后继续等待一段时间,然后继续执行滚动升级。通常情况下需要将minReadySeconds的值设置的更高,以确保pod在它们真正开始接受实际流量之后可以继续保持就绪状态。
deployment-v3-with-readinesscheck.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubian
spec:
replicas: 3
minReadySeconds: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- name: nodejs
image: luksa/kubia:v3 # 该版本是有问题的,第五次请求会返回错误
readinessProbe:
periodSeconds: 1
httpGet:
path: /
port: 8080
此时通过 kubectl rollout status deployment kubian 查看升级过程
$kubectl rollout status deployment kubian
Waiting for deployment "kubian" rollout to finish: 1 out of 3 new replicas have been updated...
显示已经有一个pod正在更新,通过curl 命令发送请求,结果是没有请求切换到V3 。此时通过kubectl get pod 查看得知,有一个kubia-xxx 的pod没有处于就绪状态,所有流量并没有切换过来。
当新的pod创建后,就绪探针会每秒发送一次请求(readinessProbe.periodSeconds=1),在就绪探针第五次发起请求时会返回错误。因此,pod会从service的endpoint中移除。所有新的请求不会被切换到新的pod上。
rollout status 命令显示只有一个新的副本启动,之后滚动升级将不会继续下去。因为新的pod一致处于不可用状态,即使变为就绪状态,也要保持10秒,才是真正的可用。在这之前滚动升级不会再创建任何的pod,因为maxUnavailable为0,所以也不会删除任何旧的pod。
需要注意的是,如果只设置了就绪探针,没有正确的设置minReadySeconds,一旦有一次就绪探针探测成功,便会认为新的pod已经处于可用状态。因此最好适当的设置minReadySeconds的时间。
默认情况下,在10分钟内没有完成滚动升级,将会视为失败。可以使用 describe命令查看,将会有一条 ProgressDeadlineExceeded的记录。判定Deployment 滚动升级失败超时时间,可以通过设置Deployment.spec 中的 progressDeadlineSeconds 来指定。
(本文章是学习《kubernetes in action》 一书时做的笔记,由于本人是刚学习k8s,且自学方式就是把它敲一遍,然后加一些自己的理解。所以会发现很多内容是跟书中内容一模一样,如果本文是对本书的侵权,敬请谅解,告知后会删除。如果引用或转载,请注明出处——谢谢)