跳到主要内容

【2】K8S核心对象之控制器

参考 k8s-tutorial-cn

控制器概念

控制器是一种来管理Pod的单元,它能够用更细粒度的控制Pod的运行方式,比如副本数量、部署位置等。 控制器包含下面几种:

  • Replication控制器(以及ReplicaSet控制器):负责保证Pod副本数量符合预期(涉及对Pod的启动、停止等操作);
  • Deployment控制器:是高于Replication控制器的对象,也是最常用的控制器,用于管理Pod的发布、更新、回滚等;
  • StatefulSet控制器:与Deployment同级,提供排序和唯一性保证的特殊Pod控制器。用于管理有状态服务,比如数据库等。
  • DaemonSet控制器:与Deployment同级,用于在集群中的每个Node上运行单个Pod,多用于日志收集和转发、监控等功能的服务。并且它可以绕过常规Pod无法调度到Master运行的限制;
  • Job控制器:与Deployment同级,用于管理一次性任务,比如批处理任务;
  • CronJob控制器:与Deployment同级,在Job控制器基础上增加了时间调度,用于执行定时任务。

使用Deployment

# deployment模板 deployment-go-http.yaml
apiVersion: apps/v1
kind: Deployment # 资源类型=Deployment
metadata:
# deployment 唯一名称
name: hellok8s-go-http
spec:
replicas: 2 # 副本数量
selector:
matchLabels:
app: hellok8s # 管理template下所有 app=hellok8s的pod,(要求和template.metadata.labels完全一致!!!否则无法部署deployment)
template: # template 定义一组容器
metadata:
labels:
app: hellok8s
spec:
containers:
- image: leigg/hellok8s:v1
name: hellok8s

部署和查看控制器

# 部署
kubectl apply -f deployment-go-http.yaml
# 查看部署的控制器
kubectl get deployments -o wide
# 查看pod运行情况
> kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hellok8s-go-http-6f48f959c-hqcm4 1/1 Running 0 2m57s 10.42.1.14 k8s-node1 <none> <none>
hellok8s-go-http-6f48f959c-sp548 1/1 Running 0 2m57s 10.42.0.13 k8s-master <none> <none
# 删除一个pod(hqcm4),测试deployment恢复能力
> kubectl delete pod hellok8s-go-http-6f48f959c-hqcm4
# 过一会再次查看pod情况,可以看到新的pod(9wq7z)已经启动了
> kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hellok8s-go-http-6f48f959c-sp548 1/1 Running 0 50m 10.42.0.13 k8s-master <none> <none>
hellok8s-go-http-6f48f959c-9wq7z 1/1 Running 0 51s 10.42.1.15 k8s-node1 <none> <none>

修改deployment

deployment修改方式和pod一样,仍然支持修改模板文件和使用patch命令两种。

# 修改deployment副本数量为3
kubectl patch deployment hellok8s-go-http -p '{"spec":{"replicas": 3}}'
# 过一会再次查看pod情况,可以看到三个pod正在运行
> kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hellok8s-go-http-6f48f959c-sp548 1/1 Running 0 55m 10.42.0.13 k8s-master <none> <none>
hellok8s-go-http-6f48f959c-9wq7z 1/1 Running 0 5m45s 10.42.1.15 k8s-node1 <none> <none>
hellok8s-go-http-6f48f959c-zvghn 1/1 Running 0 32s 10.42.1.16 k8s-node1 <none> <none>

更新deployment

模拟更新操作,升级镜像从leigg/hellok8s:v1leigg/hellok8s:v2

# 更新deployment
kubectl set image deployment/hellok8s-go-http hellok8s=leigg/hellok8s:v2
# 查看更新过程(如果镜像已经拉取,此过程会很快,你可能只会看到最后一条输出)
kubectl rollout status deployment/hellok8s-go-http
# 查看更新成功的容器使用的镜像名称
> kubectl get deploy -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
hellok8s-go-http 3/3 3 3 3h26m hellok8s leigg/hellok8s:v2 app=hellok8s

Deployment的镜像更新或回滚都是通过创建新的Replicaset和终止旧的Replicaset来完成的
在更新完成后,应当看到新旧Replicaset是同时存在的

> kubectl get rs -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
hellok8s-go-http-f69878698 3 3 3 3m38s hellok8s leigg/hellok8s:v2 app=hellok8s,pod-template-hash=f69878698
hellok8s-go-http-6f48f959c 0 0 0 3h28m hellok8s leigg/hellok8s:v1 app=hellok8s,pod-template-hash=6f48f959c
# 通过以下命令设置当前运行版本(REVISION)的备注
kubectl annotate deployment/hellok8s-go-http kubernetes.io/change-cause="升级hellok8s版本到v2"
# 查看上线历史
> kubectl rollout history deployment/hellok8s-go-http
deployment.apps/hellok8s-go-http
REVISION CHANGE-CAUSE
1 <none>
2 升级hellok8s版本到v2

K8S使用旧的Replicaset作为Deployment的更新历史,回滚时会用到,所以请不要手动删除旧的replicaset。
只有当Deployment的.spec.template部分发生变更时才会创建新的REVISION,如果只是Pod数量变化或Deployment标签修改,则不会创建新的REVISION。

回滚部署

# 模拟更新了一个有bug的版本镜像,容器无法启动
kubectl set image deployment/hellok8s-go-http hellok8s=leigg/hellok8s:v2_problem
kubectl annotate deployment/hellok8s-go-http kubernetes.io/change-cause="升级hellok8s版本到v2_problem"
# 观察更新过程,由于有异常,更新会停滞,按 Ctrl-C 停止观察
kubectl rollout status deployment/hellok8s-go-http
# 观察pod状态
> kubectl get pods
NAME READY STATUS RESTARTS AGE
hellok8s-go-http-f69878698-wn7tb 1/1 Running 0 17m
hellok8s-go-http-f69878698-6886t 1/1 Running 0 17m
hellok8s-go-http-f69878698-qwfzg 1/1 Running 0 16m
hellok8s-go-http-744c5c7f9d-dsrrj 0/1 CrashLoopBackOff 1 (15s ago) 43s
# CrashLoopBackOff状态表示重启次数过多,过一会儿再试,这表示pod内的容器无法正常启动,或者启动就立即退出了
# 查看每个副本集每次更新的pod情况,DESIRED-预期数量,CURRENT-当前数量,READY-可用数量,-l 进行标签筛选
> kubectl get rs -l app=hellok8s -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
hellok8s-go-http-6f48f959c 0 0 0 3h44m hellok8s leigg/hellok8s:v1 app=hellok8s,pod-template-hash=6f48f959c
hellok8s-go-http-f69878698 3 3 3 19m hellok8s leigg/hellok8s:v2 app=hellok8s,pod-template-hash=f69878698
hellok8s-go-http-744c5c7f9d 1 1 0 2m39s hellok8s leigg/hellok8s:v2_problem app=hellok8s,pod-template-hash=744c5c7f9d

开始回滚操作

# 查看deployment更新记录
> kubectl rollout history deployment/hellok8s-go-http
deployment.apps/hellok8s-go-http
REVISION CHANGE-CAUSE
1 <none>
2 升级hellok8s版本到v2
3 升级hellok8s版本到v2_problem
# 现在回到revision 2,可以先查看它具体信息(主要确认用的哪个镜像tag)
kubectl rollout history deployment/hellok8s-go-http --revision=2
# 确认后再回滚(若不指定--to-revision=N,则是回到上个版本)
kubectl rollout undo deployment/hellok8s-go-http --to-revision=2
# 再次检查副本集状态
kubectl get rs -l app=hellok8s -o wide
# 检查启动的pod状态
kubectl get deployments hellok8s-go-http

滚动更新(Rolling Update)

在 deployment 的资源定义中, spec.strategy.type 有两种选择:

  • RollingUpdate: 逐渐增加新版本的 pod,逐渐减少旧版本的 pod。(常用)
  • Recreate: 在新版本的 pod 增加前,先将所有旧版本 pod 删除(针对那些不能多进程部署的服务)

另外,还可以通过以下字段来控制升级 pod 的速率:

  • maxSurge: 最大峰值,用来指定可以创建的超出期望 Pod 个数的 Pod 数量。
  • maxUnavailable: 最大不可用,用来指定更新过程中不可用的 Pod 的个数上限。

如果不设置,可以通过kubectl describe -f deployment.yaml查看deployment的RollingUpdateStrategy默认配置 重新编辑配置文件

# deployment-go-http.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hellok8s-go-http
spec:
replicas: 3 # 副本数量
strategy:
rollingUpdate: # 滚动更新配置
maxSurge: 1 # 最多会出现多少个pod同时在运行,最大数量等于 replicas+maxSurge,这里为4个
maxUnavailable: 1 # 最少会保留多少个pod同时在运行,最小数量等于 replicas-maxUnavailable,这里为2个
selector:
matchLabels:
app: hellok8s
template:
metadata:
labels:
app: hellok8s
spec:
containers:
- image: leigg/hellok8s:v1
name: hellok8s

通过kubectl apply命令应用更新策略,再次使用kubectl describe -f查看配置

控制Pod水平伸缩

# 指定副本数量
kubectl scale deployment/hellok8s-go-http --replicas=10
# 查看pod情况
> kubectl get deployments hellok8s-go-http
NAME READY UP-TO-DATE AVAILABLE AGE
hellok8s-go-http 10/10 10 10 5h9m

存活探针 (livenessProb)

# deployment-liveness.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
# deployment唯一名称
name: hellok8s-liveness
spec:
replicas: 2 # 副本数量
selector:
matchLabels:
app: hellok8s # 管理template下所有 app=hellok8s的pod,(要求和template.metadata.labels完全一致!!!否则无法部署deployment)
template: # template 定义一组pod
metadata:
labels:
app: hellok8s
spec:
containers:
# https://github.com/chaseSpace/k8s-tutorial-cn/blob/main/main_liveness.go
# liveness镜像,在服务启动的15秒之后接口会返回异常
- image: leigg/hellok8s:liveness
name: hellok8s
# 存活探针
livenessProbe:
# http get 探测指定pod提供HTTP服务的路径和端口
httpGet:
path: /healthz
port: 3000
# 3s后开始探测
initialDelaySeconds: 3
# 每3s探测一次
periodSeconds: 3

部署deployment

# pod将在15s后一直重启
kubectl apply -f deployment-liveness.yaml
> kubectl get pods
NAME READY STATUS RESTARTS AGE
hellok8s-liveness-7fd8d775cc-l8tvh 1/1 Running 2 (21s ago) 75s
hellok8s-liveness-7fd8d775cc-mkmlb 1/1 Running 2 (21s ago) 75s
# 查看探针失败原因
> kubectl describe pod hellok8s-liveness-7fd8d775cc-l8tv | grep Unhealthy
Warning Unhealthy 3m13s (x9 over 4m7s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 500

官方文档中的其他探测方式

就绪探针 (readiness)

# deployment-readiness.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
# deployment唯一名称
name: hellok8s-readiness
spec:
replicas: 2 # 副本数量
selector:
matchLabels:
app: hellok8s # 管理template下所有 app=hellok8s的pod,(要求和template.metadata.labels完全一致!!!否则无法部署deployment)
template: # template 定义一组pod
metadata:
labels:
app: hellok8s
spec:
containers:
- image: leigg/hellok8s:readiness
name: hellok8s
# 就绪探针
readinessProbe:
# http get 探测pod提供HTTP服务的路径和端口
httpGet:
path: /healthz
port: 3000
initialDelaySeconds: 1 # 1s后开始探测
periodSeconds: 5 # 每5s探测一次
timeoutSeconds: 1 # 单次探测超时,默认1
failureThreshold: 3 # 探测失败时,k8s的重试次数,默认3,达到这个次数后 停止探测,并打上未就绪的标签

等待一段时间后可以通过命令kubectl get pods看到pod一直未处于READY状态

更新的暂停与恢复

何为金丝雀发布?

本节实现的就是金丝雀发布:金丝雀发布,与蓝绿部署不同的是,它不是非黑即白的部署方式,所以又称为灰度发布。它能够缓慢的将修改推广到一小部分用户,验证没有问题后,再推广到全部用户,以降低生产环境引入新功能带来的风险。

操作步骤实战

# 部署服务
kubectl apply -f deployment-go-http.yaml
# 一次性执行两条命令,模拟更新情况
kubectl set image deployment/hellok8s-go-http hellok8s=leigg/hellok8s:v2
kubectl rollout pause deploy hellok8s-go-http
# 现在观察更新情况,因为设置了rollingUpdate,所以会发现有两个已经被更新,两个未更新
kubectl get pods
# 如果此刻想要回滚
kubectl rollout undo deployment hellok8s-go-http --to-revision=N
# 若要继续更新
kubectl rollout resume deploy hellok8s-go-http

使用DaemonSet

  • DaemonSet是一种特殊的控制器,它确保会在集群的每个节点(或大部分)上都运行 一个 Pod副本。
  • 入或退出集群时,DaemonSet也会在相应节点增加或删除Pod,因此常用来部署那些为节点本身提供服务或维护的Pod(如日志收集和转发、监控等)。
# daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: daemonset-hellok8s-go-http
spec:
selector:
matchLabels:
app: hellok8s
template:
metadata:
labels:
app: hellok8s
spec:
tolerations:
# 这些容忍度设置是为了让该守护进程集在控制平面节点上运行
# 如果你不希望自己的控制平面节点运行 Pod,可以删除它们
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- image: leigg/hellok8s:v1
name: hellok8s

DaemonSet基本操作和Deployment没有较大差别,因此忽略。

使用Job和CronJob

使用Job

Job控制器可以执行3种类型的任务。

  • 一次性任务:启动一个Pod(除非启动失败)。一旦Pod成功终止,Job就算完成了。
  • 串行式任务:连续、多次地执行某个任务,上一个任务完成后,立即执行下个任务,直到全部执行完。
  • 并行式任务:可以通过spec.completions属性指定执行次数。
# job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: pods-job
spec:
# completions: 3 # 启用它表示串行执行3次
# parallelism: 3 # 启动它表示并发数,由completions指定总次数
# backoffLimit: 3 # 限制重试次数,默认6,超过次数则不再启动新pod
# activeDeadlineSeconds: 10 # 限制job执行时间,超时还不终止则强制终止,并且稍后执行自动删除(若设置),且不受restartPolicy字段影响
ttlSecondsAfterFinished: 300 # 多少秒后自动删除执行成功的job,避免太多不再需要的job累积
template:
spec:
restartPolicy: Never # or OnFailure, 不能是其他值;推荐Never,因为这个策略下控制会启动新的pod,不会删除失败的pod,有助于排查问题;OnFailure是不断重启旧的pod
containers:
- command: ['sh', '-c', 'echo "Start Job!"; sleep 30; echo "Job Done!"']
image: busybox
name: pods-job-container

使用job.yaml测试一次性任务

# 创建job
kubectl apply -f job.yaml
# 查看job和pod
# DURATION 表示job启动到结束耗时
kubectl get job
# Completed 表示pod正常终止
> kubectl get pods
NAME READY STATUS RESTARTS AGE
pods-job-xz8pf 1/1 Running 0 41s
# 查看pod日志(标准输出和错误)
> kubectl logs pods-job-xz8pf
Start Job!
Job Done!
# 执行结束后,手动删除job,也可在yaml中配置自动删除
> kubectl delete job pods-job
job.batch "pods-job" deleted

使用CronJob

# cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: pods-cronjob
spec:
schedule: "*/1 * * * *" # 最小到min级别,这表示每分钟1次
startingDeadlineSeconds: 3 # 最大启动时间,超时后变成失败
concurrencyPolicy: Forbid # Allow/Forbid/Replace,上个周期的Job未执行结束时,是否允许下个周期的Job开始执行,默认Allow
suspend: false # 是否暂停cronjob的执行,一般通过kubectl edit修改
successfulJobsHistoryLimit: 3 # 保留多少条执行成功的Job记录,默认3
failedJobsHistoryLimit: 1 # 保留多少条执行失败的Job记录,默认1
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- command: [ 'sh', '-c', 'echo "Start Job!"; sleep 30; echo "Job Done!"' ]
image: busybox
name: pods-cronjob-container

操作和job一样,这里就不详解了。