文章目录
一、常用负载控制器是什么
工作负载控制器(Workload Controllers)是k8s的一个抽象概念,用于更高级层次对象,负责部署和管理Pod。
常用工作负载控制器:
- Deployment:无状态应用部署
- StatefulSet:有状态应用部署
- DaemonSet:确保所有Node都运行同一个Pod
- Job:一次性任务
- Cronjob:定时任务
控制器的作用:
- 管理Pod对象
- 使用标签与Pod关联
- 控制器实现了Pod的运维,例如滚动更新、伸缩、副本管理、维护Pod状态等
二、Deployment控制器
1.介绍
Deployment的功能:
- 管理Pod和ReplicaSet
- 具有上线部署、副本设定、滚动升级、回滚等功能
应用场景:网站、API、微服务
2.使用流程
通常一个项目都有一个生命周期
- 项目立项,开发人员进行开发;
- 运维负责将项目部署到各个环境;
- 部署到线上后,后续有新增功能、修复bug都会对版本进行升级;
- 在频繁升级的过程中难免会有以往代码忘记更新等导致线上出现问题,就需要进行回滚,回滚到上一个可用版本;
- 最后再将项目进行下线;
3.应用部署
第一步:部署镜像
- kubectl apply -f xxx.yaml
或 - kubectl create deployment web --image=nginx:1.15 --replicas=3
web.yaml清单内容如下
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
#Pod副本的预期数量
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
#Pod副本的标签
app: web
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
运行此清单创建deployment
[root@k8s-master ~]# kubectl apply -f web.yaml
[root@k8s-master ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
web 3/3 3 3 5s
[root@k8s-master ~]# kubectl get pod -l app=web
NAME READY STATUS RESTARTS AGE
web-794776687-ms58g 1/1 Running 0 76s
web-794776687-xtc9d 1/1 Running 0 77s
web-794776687-zdtkp 1/1 Running 0 76s
创建service将此deployment暴露出去供浏览器访问
[root@k8s-master ~]# kubectl expose deployment web --port=80 --target-port=80 --type=NodePort --dry-run=client -o yaml > service.yaml
[root@k8s-master ~]# cat service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: web
name: web
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: web
type: NodePort
[root@k8s-master ~]# kubectl apply -f service.yaml
[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 132d
web NodePort 10.102.94.33 <none> 80:32012/TCP 13s
浏览器访问k8s任意节点IP:32012即可访问到nginx首页
4.应用升级
更新镜像有三种方式,默认自动触发滚动升级
- kubectl apply -f xxx.yaml
- kubectl set image deployment/web <容器名>=nginx:1.17
- kubectl edit deployment/web #使用系统编辑器打开
滚动升级:滚动发布是指每次只升级一个或多个服务,升级完成后加入生产环境,不断执行这个过程,直至集群中的全部的旧版本升级为新版本;
例如上一步我们部署了镜像版本为1.14的nginx,现在我们将镜像升级为1.17
第一种方式:
其实就是手动编辑yaml清单将镜像修改为1.17
[root@k8s-master ~]# vim web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
#Pod副本的预期数量
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
#Pod副本的标签
app: web
spec:
containers:
- name: web
image: nginx:1.17
ports:
- containerPort: 80
[root@k8s-master ~]# kubectl apply -f web.yaml
第二种方式:
直接通过命令行修改deployment资源使用的镜像,jenkins常用这种方式
kubectl set image deployment/<deployment名称> <容器名称>=镜像
[root@k8s-master ~]# kubectl set image deployment/web nginx=nginx:1.17
第三种方式:
使用当前系统默认的编辑器临时打开deployment控制器,保存后可以立即生效(wq保存之后相当于自动kubectl apply 了一下),可以作用于找不到deployment的yaml清单时使用
[root@k8s-master ~]# kubectl edit deployment/web
# 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
# reopened with the relevant failures.
#
apiVersion: apps/v1
kind: Deployment
...
spec:
containers:
- image: nginx:1.17
imagePullPolicy: IfNotPresent
name: nginx
...
使用上面三种方式升级镜像后,pod变化如下
[root@k8s-master ~]# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
web-777dc5c56f-96dcr 1/1 Running 0 3m59s
web-777dc5c56f-c4h2v 1/1 Running 0 3m56s
web-777dc5c56f-r2zds 1/1 Running 0 3m54s
web-5499465464-rz2hm 0/1 Pending 0 0s
web-5499465464-rz2hm 0/1 ContainerCreating 0 1s
web-5499465464-rz2hm 1/1 Running 0 3s
web-777dc5c56f-r2zds 1/1 Terminating 0 4m3s
可以看出就是使用的滚动升级
,先创建一个新的pod,等running后在干掉一个老的pod,往复循环,直至pod全部变为新的;
去浏览器查看下现在nginx的版本
5.滚动升级实现原理(replicaset控制器)
在了解滚动升级的原理之前,需清楚replicaSet控制器:
replicaSet控制器
- replicaSet:副本集,主要用于维护Pod副本数量,不断对比当前Pod与期望Pod的数量,保证一定数量的Pod在集群中正常运行;
- replicaSet用途:deployment每次升级都会创建一个新的RS作为记录,用于实现滚动升级和回滚
kubectl get rs #查看rs历史的记录,rs会对之前的部署过的记录进行保留,当前在用的RS ready为3,历史不在用的为0
[root@k8s-master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
web-5499465464 3 3 3 24h
web-777dc5c56f 0 0 0 24h
kubectl rollout history deployment web #每个版本对应的RS记录
[root@k8s-master ~]# kubectl rollout history deployment web
deployment.apps/web
REVISION CHANGE-CAUSE
1 <none>
2 <none>
在k8s中更新镜像就会触发滚动升级,滚动升级是利用了1个deployment和2个replicaSet来实现的
- 滚动更新一次只升级一小部分Pod,成功后,再升级一部分Pod,最终完成所有Pod升级,整个过程始终有Pod在运行,从而保证了业务的连续性
如上图:
在1个deployment控制器中包含2个replicaset控制器,replicaset主要管理pod的副本数,继而通过replicaset管理副本数的能力来实现滚动升级;例如将replicaset2的副本数调整为3时,那么replicaset1的副本数就回被调整为1,
6.滚动升级实现流程
接下来查看1.14.2镜像版本升级到1.17镜像版本的过程
如上图,我们可以看到deployment在升级的时候是对两个RS(web-777dc5c56f、web-5499465464) 进行操作的,对两个RS来回的进行一个扩容一个缩容从而就实现了副本数的滚动升级;
注:如果升级失败(pod没有运行成功),将不再继续进行升级,等待升级正确的镜像或者回滚;
7.滚动升级策略
- maxSurge:用来指定 在滚动更新过程中,可以创建的超出期望Pod个数(replicas)的Pod数量,此选项可以是个绝对数(例如5)或所需Pod的百分比(例如10%),百分比值会通过向上取整转换为绝对数。;默认为25%
示例一:此选项为 30% 时,replicas副本数设置的为10,那么在滚动升级时,会立即对新的replicaset进行扩容,同时会保证新旧Pod的总数不超过所需Pod的总数(replicas)的130%,一旦旧Pod被杀死,新的Replicaset就可以进一步扩容,同时确保更新期间的任何时候运行中的Pod总数最多为所需Pod总数的130%;
apiVersion: apps/v1
kind: Deployment
...
spec:
#Pod副本的预期数量
replicas: 10
strategy:
rollingUpdate:
#滚动更新时,maxSurge=新Pod的数量=(replicas*30%),此时Pod的总数量=replicas + maxSurge
maxSurge: 30%
selector:
matchLabels:
app: web
...
示例二:此选项为 5 时,replicas副本数设置的为10,那么在滚动升级时,会立即对新的replicaset进行扩容,同时会保证新旧Pod的总数不超过所需Pod的总数15(replicas + maxSurge),一旦旧Pod被杀死,新的Replicaset就可以进一步扩容,同时确保更新期间的任何时候运行中的Pod总数最多为所需Pod的总数15;
apiVersion: apps/v1
kind: Deployment
...
spec:
#Pod副本的预期数量
replicas: 10
strategy:
rollingUpdate:
#滚动更新时,一次性更新的Pod数量,此时Pod的总数量=replicas + maxSurge
maxSurge: 5
selector:
matchLabels:
app: web
...
- maxUnavailable:用来指定 在滚动更新过程中,不可用Pod的个数上限,也就是在更新时最多有多少Pod不可用,此选项可以是个绝对数(例如5)或所需Pod的百分比(例如10%),百分比值会转换成绝对数并去除小数部分。默认为25%
示例一:此选项为 30% 时,replicas副本数设置的为10,那么在滚动升级时,会立即对旧的repliaset进行缩容至70%,也就是7个旧的pod,当新pod就绪后,会继续缩容旧的replicaset,然后对新的replicaset进行扩容,确保在更新期间可用的pod总数在任何时候都至少为所需pod个数的70%;
apiVersion: apps/v1
kind: Deployment
...
spec:
#Pod副本的预期数量
replicas: 10
strategy:
rollingUpdate:
#滚动更新时,不可用pod的数量;也就是会会将旧的replicaset缩容30%
maxUnavailable: 30%
selector:
matchLabels:
app: web
template:
metadata:
labels:
#Pod副本的标签
app: web
...
示例二:此选项为 3 时,replicas副本数设置的为10,那么在滚动升级时,会立即对旧的repliaset进行缩容至7个副本,当新pod就绪后,会继续缩容旧的replicaset,然后对新的replicaset进行扩容,确保在更新期间可用的pod总数在任何时候都不少于7个;
apiVersion: apps/v1
kind: Deployment
...
spec:
#Pod副本的预期数量
replicas: 10
strategy:
rollingUpdate:
#滚动更新时,不可用pod的数量为3个;也就是会会将旧的replicaset缩容至7个
maxUnavailable: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
#Pod副本的标签
app: web
...
通过上面的列子可以总结出:
- maxSurge:和期望状态为reday的副本数(replicas)相比,这个值越大,副本更新速度越快,当然在更新时吃的资源也越大;
- maxUnavailable:和期望状态为reday的副本数(replicas)相比,这个值越小,越能保证服务稳定,更新更平滑;
建议配置为:
- maxSurge = 1
- maxUnavailable = 0
apiVersion: apps/v1
kind: Deployment
...
spec:
#Pod副本的预期数量
replicas: 10
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: web
template:
metadata:
labels:
#Pod副本的标签
app: web
...
这也算是在生产环境中最稳定的配置,即“一上一下,先上后下”最平滑原则,一个新的pod状态为reday后,再将旧版本的pod进行销毁;这个配置的使用场景是平滑更新、保证服务稳定,但也有缺点,就是更新太慢了;
自定义策略:
切记:当服务的副本数为1时,推荐使用上面的"建议配置"或使用k8s默认的配置即可,千万不要自定义将"maxUnavailable"这个值设为1或100%,否则将造成更新期间服务直接挂掉!!
Deployment controller调整replicaset数量时,严格通过以下公式来控制发布节奏。所以,如需快速发布,可根据实际情况去调整这两个值:
(目标副本数-maxUnavailable) <= 线上实际Ready副本数 <= (目标副本数+maxSurge)
举例:如果期望副本数是10,期望能有至少80%数量的副本能稳定工作,所以:maxUnavailable = 2,maxSurge = 2 (可自定义,建议与maxUnavailable保持一致)
8 <= 线上实际Ready副本数 <= 12
这样,更新过程中,线上能够正常提供服务的pod数总会保持在这个区间内。
现象(maxUnavailable = 1,maxSurge = 1)
8.应用实例扩容和缩容
-
扩容:当用户的请求数增高时,例如双十一、618这种活动,这时就需要增加我们服务的副本数(replicas)来支撑高并发;
-
缩容:缩容就是减少服务的副本数以最小的负载运行我们的服务,缩容可以用来节省服务器资源;
水平扩缩容解决方案:
- 修改yaml文件里的replicas值,然后再apply yaml文件
- kubectl scale deployment web --replicas=10
注:
web为要扩缩容的deployment名称
replicas参数用来控制Pod副本数量
9.应用发布失败回滚
当我们的应用发布后因某种问题导致了线上事故,业务不能正常访问;此时就需要对应用进行回滚;
注:回滚是重新部署某一次部署时的状态,即当时版本所有配置
#查看历史发布版本
kubectl rollout history deployment/web
#回滚至上个版本
kubectl rollout undo deployment/web
#回滚至指定历史版本
kubectl rollout undo deployment/web --to-revision=2
回滚的原理其实是基于replicaset控制来实现的,因为deployment每次升级都会创建一个新的RS作为记录,这个记录就相当于是每个版本迭代的备份,如下所示;
[root@k8s-master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
web-5cd5f844db 0 0 0 73s
web-65c48b5b5 0 0 0 87s
web-6886c44bb7 0 0 0 51s
web-69ddb96fb8 0 0 0 31s
web-7fbc4b76bd 3 3 3 10s
[root@k8s-master ~]# kubectl rollout history deployment/web
deployment.apps/web
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 <none>
4 <none>
5 <none>
其实通过kubectl rollout history这个命令查看回滚历史并不能看到每个版本所使用的镜像,此时如果想指定回滚的版本的话可以通过查看RS的记录来回滚,步骤如下:
(1)先通过RS获取对应发布版本号及镜像版本
注:RS详情里的revision这个字段匹配的就是"kubectl rollout history"的版本号
[root@k8s-master ~]# kubectl describe rs | egrep "revision:|Name:|Image:"
Name: web-5cd5f844db
deployment.kubernetes.io/revision: 2
Image: nginx:1.17
Name: web-65c48b5b5
deployment.kubernetes.io/revision: 1
Image: nginx:1.16
Name: web-6886c44bb7
deployment.kubernetes.io/revision: 3
Image: nginx:1.18
Name: web-69ddb96fb8
deployment.kubernetes.io/revision: 4
Image: nginx:1.19
Name: web-7fbc4b76bd
deployment.kubernetes.io/revision: 5
Image: nginx:1.20
(2)根据查看到的镜像版本及对应发布版本号进行回滚
例如这里我要回滚到nginx1.19版本,则需要指定revision=4
[root@k8s-master ~]# kubectl rollout undo deployment/web --to-revision=4
deployment.apps/web rolled back
(3)会使用对应的rs记录,回滚到该镜像版本
注:其实回滚也可以直接使用正确的镜像重新部署下即可;
10.应用下线
当确认项目下线不再使用时,可以进行删除deployment及所使用的其它资源;
[root@k8s-master ~]# kubectl delete deployment/web
deployment.apps "web" deleted
[root@k8s-master ~]# kubectl delete svc/web
三、DaemonSet控制器
DaemonSet功能:
- 在每一个Node上运行一个Pod
- 新加入的Node也同样会自动运行一个Pod
应用场景:网络插件(kube-proxy、calico) 、日志、监控等其它Agent
示例:
部署余个日志采集程序
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: filebeat
namespace: kube-system
labels:
app: filebeat
spec:
selector:
matchLabels:
app: filebeat
template:
metadata:
labels:
app: filebeat
spec:
tolerations:
#添加对master节点对容忍,如果不需要master运行的话可以删除此容忍
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: log
image: elastic/filebeat:7.3.2
运行此yaml后并验证
[root@k8s-master ~]# kubectl get pod -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
...
filebeat-89lrs 1/1 Running 0 4m51s 10.244.36.89 k8s-node1 <none> <none>
filebeat-8rbw4 1/1 Running 0 4m51s 10.244.235.193 k8s-master <none> <none>
filebeat-9gn54 1/1 Running 0 4m51s 10.244.169.137 k8s-node2 <none> <none>
filebeat-h72s2 1/1 Running 0 4m51s 10.244.107.238 k8s-node3 <none> <none>
...
可以看到daemonset会在每个节点都帮我们启一个filebeat的pod
四、Job控制器
Job分为普通任务(Job)和定时任务(CronJob)
- 普通的Job控制器为一次性执行,用于执行一些临时任务;
应用场景:离线数据处理,视频解码等业务;
示例:
使用perl镜像计算圆周率,它负责计算 π 到小数点后 2000 位,并将结果打印出来。
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
#允许执行失败的pod数量,当执行状态status为Failed时,会重新起一个pod运行,默认是6; 0代表不允许pod失败,失败了就不执行了;
backoffLimit: 0
运行此yaml后验证
[root@k8s-master goodgood_study]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pi-n6c54 0/1 Completed 0 21s
[root@k8s-master goodgood_study]# kubectl get job
NAME COMPLETIONS DURATION AGE
pi 1/1 6s 27m
[root@k8s-master goodgood_study]# kubectl describe job/pi
Name: pi
...
Pods Statuses: 0 Running / 1 Succeeded / 0 Failed
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 27m job-controller Created pod: pi-n6c54
Normal Completed 27m job-controller Job completed
#查看这个job对pod运行日志
[root@k8s-master goodgood_study]# kubectl logs -f pod/pi-n6c54
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901
# 五、CronJob控制器 CronJob用于实现定时任务,像linux的Crontab一样
- 周期性的执行任务
应用场景:通知、备份操作等;
示例:
一分钟打印一次当前时间和问候信息
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "* * * * *"
#每周的周一到周五的凌晨1点执行
#schedule: "0 1 * * 1-5"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.28
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello fandaoshuai;
restartPolicy: OnFailure
运行此yaml后验证
[root@k8s-master ~]# kubectl get cronjob
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello * * * * * False 0 <none> 11s
[root@k8s-master goodgood_study]# kubectl get pod
NAME READY STATUS RESTARTS AGE
hello-1692695040-rc5v7 0/1 Completed 0 24s
[root@k8s-master goodgood_study]# kubectl logs -f pod/hello-1692695040-rc5v7
Tue Aug 22 09:10:05 UTC 2023
Hello fandaoshuai