控制器模式解析
k8s 项目通过一个称作"控制器模式"(controller pattern)的设计方法,来统一地实现对各种不同的对象或者资源进行的编排操作。#k8s核心就是用一个东西去控制另一个东西,所有的内容都是被控制的,
容器镜像虽然好用,但是容器这样一个"沙盒"的概念,对于描述应用来说太过简单。好比,集装箱固然好用,如果它四面都光秃秃的,吊车还怎么把这个集装箱吊起来并摆放好呢?
所以,Pod 对象,其实就是容器的升级版。它对容器进行了组合,添加了更多的属性和字段。这就好比给集装箱四面安装了吊环,使得 Kubernetes 这架"吊车",可以更轻松地操作它。
而 k8s 操作这些"集装箱"的逻辑,都由控制器(Controller)完成
回顾 Deployment 这个最基本的控制器对象。之前讲过一个 nginx-deployment 的例子:
例1:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
这个 Deployment 定义的编排动作为:
确保携带了 app=nginx 标签的 Pod 的个数,永远等于 spec.replicas 指定的个数,即 2 个。如果在这个集群中,携带 app=nginx 标签的 Pod 的个数大于 2 的时候,就会有旧的 Pod 被删除;反之,就会有新的 Pod 被创建。
究竟是 Kubernetes 项目中的哪个组件,在执行这些操作呢?
kube-controller-manager 组件:这个组件,就是一系列控制器的集合
所有控制器:
deployment job podautoscaler
cloud disruption namespace
replicaset serviceaccount volume
cronjob garbagecollector nodelifecycle replication statefulset daemon
上面的每一个控制器,都以独有的方式负责某种编排功能。而Deployment,正是这些控制器中的一种。
而被控制对象的定义,则来自于一个"模板"。比如,Deployment 里的 template 字段。
Deployment 这个 template 字段里的内容,跟一个标准的 Pod 对象的 API 定义,丝毫不差。而所有被这个 Deployment 管理的 Pod 实例,都是根据这个 template 字段的内容创建出来的。
对 Deployment 以及其他类似的控制器,做一个总结:
如图,类似 Deployment 的一个控制器,都是由两部分组成:
上半部分的控制器定义(包括期望状态)
下半部分的被控制对象的模板组成的。
很多不同类型的容器编排功能,比如 StatefulSet、DaemonSet 等等,都有这样一个甚至多个控制器的存在,并遵循控制循环(control loop)的流程,完成各自的编排逻辑。这些控制循环最后的执行结果,要么就是创建、更新一些 Pod(或者其他的 API 对象、资源),要么就是删除一些已经存在的 Pod(或者其他的 API 对象、资源)。
也正是在这个统一的编排框架下,不同的控制器可以在具体执行过程中,设计不同的业务逻辑,从而达到不同的编排效果。
这个实现思路,正是 k8s 进行容器编排的核心原理。
滚动更新
概念:
将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是"滚动更新"。
实验:
[root@kub-k8s-master prome]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 2/2 2 2 4h41m
nginx-deployment 2/2 2 2 5h13m
我们将nginx-deploument的副本数量变成4个,现在2个
[root@kub-k8s-master prome]# vim deployment.yaml #修改如下内容
将replicas: 2
修改为:
replicas: 4
创建上节儿的:nginx-deployment
[root@kub-k8s-master prome]# kubectl apply -f deployment.yaml --record
deployment.apps/nginx-deployment configured
--record 记录下每次操作所执行的命令,以方便后面查看
检查nginx-deployment 创建后的状态信息:
[root@kub-k8s-master prome]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 2/2 2 2 4h53m
nginx-deployment 4/4 4 4 5h25m
返回结果中四个状态字段含义:
DESIRED:
如果有就表示用户期望的 Pod 副本个数(spec.replicas 的值);
CURRENT:
当前处于 Running 状态的 Pod 的个数;
UP-TO-DATE:
当前处于最新版本的 Pod 的个数,所谓最新版本指的是 Pod 的 Spec 部分与 Deployment 里 Pod 模板里定义的完全一致;
AVAILABLE:
当前已经可用的 Pod 的个数,即:既是 Running 状态,又是最新版本,并且已经处于 Ready(健康检查正确)状态的 Pod 的个数。只有这个字段,描述的才是用户所期望的最终状态。
修改 Deployment:
[root@kub-k8s-master prome]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 2/2 2 2 4h59m
nginx-deployment 4/4 4 4 5h32m
将dep01的副本将2变为3个
[root@kub-k8s-master prome]# kubectl edit deployment/dep01
# reopened with the relevant failures.
#
apiVersion: apps/v1
...
spec:
progressDeadlineSeconds: 600
replicas: 3 #将这里原来的2改为3
revisionHistoryLimit: 10
selector:
matchLabels:
...
保存退出,vim的方式
[root@kub-k8s-master prome]# kubectl edit deployment/dep01
deployment.apps/dep01 edited
[root@kub-k8s-master prome]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 3/3 3 3 5h16m
nginx-deployment 4/4 4 4 5h48m
进行版本的升级
创建一个新的deploy
[root@kub-k8s-master prome]# cp nginx-depl.yml nginx-depl02.yml
[root@kub-k8s-master prome]# vim nginx-depl02.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep02 #注意修改
spec:
selector:
matchLabels:
app: web1
replicas: 2
template:
metadata:
name: testnginx9
labels:
app: web1
spec:
containers:
- name: testnginx9
image: daocloud.io/library/nginx:1.14 #注意修改
ports:
- containerPort: 80
[root@kub-k8s-master prome]# kubectl apply -f nginx-depl02.yml
deployment.apps/dep02 created
[root@kub-k8s-master prome]# kubectl get pods
NAME READY STATUS RESTARTS AGE
dep01-58f6d4d4cb-997jw 1/1 Running 0 16m
dep01-58f6d4d4cb-g6vtg 1/1 Running 0 5h32m
dep01-58f6d4d4cb-k6z47 1/1 Running 0 5h32m
dep02-78dbd944fc-47czr 1/1 Running 0 44s
dep02-78dbd944fc-4snsj 1/1 Running 0 25s
将nginx的版本从1.14升级到1.16
[root@kub-k8s-master prome]# kubectl edit deployment/dep02
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
...
spec:
containers:
- image: daocloud.io/library/nginx:1.16 #将这里原来的nginx:1.14修改为nginx:1.16
imagePullPolicy: Always
name: testnginx9
ports:
- containerPort: 80
...
保存退出,vim的方式
[root@kub-k8s-master prome]# kubectl edit deployment/dep01
deployment.apps/dep01 edited
这时可以通过查看 Deployment 的 Events,看到这个"滚动更新"的流程:
[root@kub-k8s-master prome]# kubectl describe deployment dep02
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 50s deployment-controller Scaled up replica set dep02-846bf8775b to 2
Normal ScalingReplicaSet 9s deployment-controller Scaled up replica set dep02-58f8d5678 to 1
Normal ScalingReplicaSet 8s deployment-controller Scaled down replica set dep02-846bf8775b to 1
Normal ScalingReplicaSet 8s deployment-controller Scaled up replica set dep02-58f8d5678 to 2
Normal ScalingReplicaSet 5s deployment-controller Scaled down replica set dep02-846bf8775b to 0
如此交替进行,新 ReplicaSet 管理的 Pod 副本数,从 0 个变成 1 个,再变成 2 个,最后变成 3 个。而旧的 ReplicaSet 管理的 Pod 副本数则从 3 个变成 2 个,再变成 1 个,最后变成 0 个。这样,就完成了这一组 Pod 的版本升级过程。
验证
[root@kub-k8s-master prome]# kubectl get pods
NAME READY STATUS RESTARTS AGE
dep02-78dbd944fc-69t8x 1/1 Running 0 11h
dep02-78dbd944fc-7cn86 1/1 Running 0 11h
[root@kub-k8s-master prome]# kubectl exec -it dep02-78dbd944fc-69t8x /bin/bash
root@dep02-78dbd944fc-69t8x:/# nginx -v
nginx version: nginx/1.16.1
root@dep02-78dbd944fc-69t8x:/# exit
"滚动更新"的好处:
在升级刚开始的时候,集群里只有 1 个新版本的 Pod。如果这时,新版本 Pod 有问题启动不起来,那么"滚动更新"就会停止,从而允许开发和运维人员介入。而在这个过程中,由于应用本身还有两个旧版本的 Pod 在线,所以服务并不会受到太大的影响。
版本回滚
查看版本历史
[root@kub-k8s-master prome]# kubectl rollout history deployment/dep02
deployment.apps/dep02
REVISION CHANGE-CAUSE
1 <none>
2 <none>
回滚到以前的旧版本:
把整个 Deployment 回滚到上一个版本:
[root@kub-k8s-master prome]# kubectl rollout undo deployment/dep02
deployment.apps/dep02 rolled back
查看回滚状态
[root@kub-k8s-master prome]# kubectl rollout status deployment/dep02
deployment "dep02" successfully rolled out
验证:
[root@kub-k8s-master prome]# kubectl get pods
NAME READY STATUS RESTARTS AGE
dep02-8594cd6447-pqtxk 1/1 Running 0 55s
dep02-8594cd6447-tt4h4 1/1 Running 0 51s
[root@kub-k8s-master prome]# kubectl exec -it dep02-8594cd6447-tt4h4 /bin/bash
root@dep02-8594cd6447-tt4h4:/# nginx -v
nginx version: nginx/1.14.2
回滚到更早之前的版本:
- 使用 kubectl rollout history 命令查看每次 Deployment 变更对应的版本。
[root@kub-k8s-master prome]# kubectl rollout history deployment/dep02
deployment.apps/dep02
REVISION CHANGE-CAUSE
2 <none>
3 <none>
由于在创建这个 Deployment 的时候,指定了--record 参数,所以创建这些版本时执行的 kubectl 命令,都会被记录下来。
查看每个版本对应的 Deployment 的 API 对象的细节:
[root@kub-k8s-master prome]# kubectl rollout history deployment/dep02 --revision=3
deployment.apps/dep02 with revision #3
Pod Template:
Labels: app=web1
pod-template-hash=8594cd6447
Containers:
testnginx9:
Image: daocloud.io/library/nginx:1.14
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
2.在 kubectl rollout undo 命令行最后,加上要回滚到的指定版本的版本号,就可以回滚到指定版本了。
[root@kub-k8s-master prome]# kubectl rollout undo deployment/dep02 --to-revision=2
deployment.apps/dep02 rolled back
验证:
[root@kub-k8s-master prome]# kubectl get pods
NAME READY STATUS RESTARTS AGE
dep02-78dbd944fc-8nvxl 1/1 Running 0 86s
dep02-78dbd944fc-sb9sj 1/1 Running 0 88s
[root@kub-k8s-master prome]# kubectl exec -it dep02-78dbd944fc-8nvxl /bin/bash
root@dep02-78dbd944fc-8nvxl:/# nginx -v
nginx version: nginx/1.16.1
课后了解
你听说过金丝雀发布(Canary Deployment)和蓝绿发布(Blue-Green Deployment)吗?你能说出它们是什么意思吗?