1. Kubernetes控制器模式
1.1 声明式API
API设计方法
- 命令式API
- 也称为指令式API,用户需要一步步地告诉机器该如何做(How),机器自身不具有任何“智能”,只被动接受指令
- 高度依赖用户自身理解和达成目标的能力和处理各类异常问题的经验,实现的是“命令式编程(Imperative Programming)”
- 声明式API
- 也称为申明式API,用户只需要告诉机器想要的结果(What),机器自身需要确定如何达成该目标
- 机器需要一定的“智能”,但通常只能支持事先预设和可被其理解的特定任务
- 实现的是“声明式编程(Declarative Programming)
1.2 声明式API和控制器模式
相较于命令式编程,声明式编程是一个更高的层次上的编程
- 声明式API允许用户以给出最终期望目标的方式编写代码,但具体的执行过程(即机器智能的那部分代码),最终仍然需要以命令式编程实现,只不过,它们可由不同的人群完成
- 类比来说,声明式编程的用户类似于企业的高管,只用关心和交待最终目标;而命令式编程的用户类似于企业部门经理,他需要理解目标的达成路径,并组织人力完成目标
Kubernetes的声明式API
- 用户能够以声明式定义资源对象的目标状态(spec)
- 由控制器代码(机器智能组件)负责确保实际状态(status)与期望状态一致
- 控制器即“部门经理”,负责确保部门内的各项具体任务得以落地
- 控制器通常由API的提供者负责编写
- 用户需要做的是根据资源类型及其控制器提供的DSL进行声明式编程
控制器模型(控制回路)
- 初始,Controller负责根据Input(目标状态)控制System,并生成Output(结果状态)
- Feedback根据Output生成Feedback Signal,而后由ErrorDetector基于Feedback Signal和Input来判定是否存在错误,并
- 在有错误时生成Error Signal
- Error Signal将驱动Controller生成Actuating Signal,并控制System的行为与Input要求相同
Kubernetes Controller的控制回路
- Controller根据spec,控制System生成Status
- Controller借助于Sensor持续监视System的Spec和Status,在每一次控制回路中都会对二者进行比较,并确保System的Status不断逼近或完全等同Status
1.3 Kubernetes的控制器
Kubernetes的控制器类型
- 打包于Controller Manager中内置提供的控制器,例如Service Controller、Deployment Controller等
- 基础型、核心型控制器
- 打包运行于kube-controller-manager中
- 插件或第三方应用的专用控制器,例如Ingress插件ingress-nginx的Controller,网络插件Project Calico的Controller等
- 高级控制器,通常需要借助于基础型控制器完成其功能
- 以Pod形式托管运行于Kubernetes之上,而且这些Pod很可能会由内置的控制器所控制
以编排Pod化运行的应用为核心的控制器,通常被统称为工作负载型控制器
- 无状态应用编排:ReplicaSet、Deployment
- 有状态应用编排:StatefulSet、第三方专用的Operator
- 系统级应用:DaemonSet
- 作业类应用:Job和CronJob
1.4 应用编排
定义工作负载型控制器资源对象以编排应用
- 内置的各工作负载型控制器都有对应的资源类型,控制器专用于管控其对应类型的资源对象
- 例如,Deployment控制器对应有Deployment资源类型
- 这些资源类型都是Kubernetes上标准的API资源,并遵循资源规范的通用格式
- 用户需要编排某应用时,需要事先确认好应用的运行目标模式,例如实例数据及结束条件等,并根据模式选出对应的工作负载控制器类型
- 而后根据相应控制器的资源类型的规范,编写资源配置,并提交给API Server即可
- 相应控制器监视到API Server上新建的资源后,即会运行代码确保对象实例的实际状态(Status)与期望状态(Spec)一致
注意:控制器对象仅负责确保API Server上有相应数量的符合标签选择器的Pod对象的定义,至于Pod对象的Status如何与Spec保持一致,则要由相应节点上的kubelet负责保证
2. Deployment资源及控制器
2.1 Deployment控制器简介
负责编排无状态应用的基础控制器是ReplicaSet,相应的资源类型通过三个关键组件定义如何编排一个无状态应用
- replicas:期望运行的Pod副本数
- selector:标签选择器
- podTemplate:Pod模板
Deployment是建立在ReplicaSet控制器上层的更高级的控制器
- 借助于ReplicaSet完成无状态应用的基本编排任务
- 它位于ReplicaSet更上面一层,基于ReplicaSet提供了滚动更新、回滚等更为强大的应用编排功能
- 是ReplicaSet资源的编排工具
- Deployment编排ReplicaSet
- ReplicaSet编排Pod
但是,应该直接定义Deployment资源来编排Pod应用,ReplicaSet无须显式给出
2.2 ReplicaSet和Deployment资源规范
2.2.1 ReplicaSet资源示例
apiVersion: apps/v1
kind: ReplicaSet
metadata:name: rs-example
spec:replicas: 2selector:matchLabels:app: demoapprelease: stabletemplate: metadata:labels: app: demoapprelease: stable #各Pod名字以ReplicaSet名称为前缀,后缀随机生成。spec:containers:- name: demoapp image: ikubernetes/demoapp:v1.0
2.2.2 Deployment资源示例
apiVersion: apps/v1
kind: Deployment
metadata:name: deploy-example
spec:replicas: 2selector:matchLabels:app: demoapprelease: stabletemplate:metadata:labels:app: demoapp #ReplicaSet对象名称以Deployment名称为前缀,后缀是Pod模板的hash码release: stablespec:containers: #各Pod的名字以Deployment名称为前缀,中间是Pod模板的hash码,后缀随机生成- name: demoappimage: ikubernetes/demoapp:v1.0
2.3获取资源状态
2.4 更新策略
Deployment控制器支持两种更新策略
- 滚动式更新(rolling updates)
- 逐批次更新Pod的方式,支持按百分比或具体的数量定义批次规模,默认策略
- 触发条件:podTemplate的hash码变动
- 仅podTemplate的配置变动才会导致hash码改变
- replicas和selector的变更不会导致podTemplate的hash变动
- 重建式更新(recreate)
- 在Pod资源被删除时,使用新的模板定义被足缺失的Pod数量,完成更新
- 触发条件:现有Pod被删除
2.5 触发更新
触发滚动更新
- 最为常见的更新需求是应用升级,即镜像文件更新,常用方法
- ~$ kubectl set image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ...
- ~$ kubectl patch (-f FILENAME | TYPE NAME) [-p PATCH|--patch-file FILE] [options]
- 直接更新原配置文件,而后使用“kubectl apply”命令触发更新
- 上面第二和第三种方法可用于模板中任何必要配置的更新
查看更新
- 更新状态:kubectl rollout status (TYPE NAME | TYPE/NAME) [flags] [options]
- 更新历史:kubectl rollout history (TYPE NAME | TYPE/NAME) [flags] [options]
回滚
- 回滚到前一版本: kubectl rollout undo (TYPE NAME | TYPE/NAME)
- 回滚到指定版本: kubectl rollout undo (TYPE NAME | TYPE/NAME) --to-revision=X
2.6 配置滚动更新策略
Deployment的滚动更新支持使用如下两个字段来配置相关的策略
- maxSurge:指定升级期间存在的总Pod对象数量最多可超出期望值的个数,其值可以是0或正整数,也可以是相对于期望值的一个百分比
- maxUnavailable:升级期间正常可用的Pod副本数(包括新旧版本)最多不能低于期望值的个数,其值可以是0或正整数,也可以是相对于期望值的一个百分比,默认值为1
配置示例
strategy:rollingUpdate:maxSurge: 25%maxUnavailable: 25%type: RollingUpdate
maxSurge参数:
- 当前最多可以存在的pod, 例如replicas=4 maxSurge=25% 那么最多可以存在5个
maxUnavailable参数:
- 可用的pod数量, 例如replicas=4 maxUnavailable=25% 那么最少可用的pod数量是3 也就是3、4、5都可以
存在的问题1:
- 必须以Pod为原子单位切割规模比例,且无法精准控制流量路由的比例,无法精准控制新老pod接收的流量比例,service是按照pod数量进行的切割
解决方案:
- 使用七层负载均衡
存在的问题2:
- replicas=4 maxUnavailable=25% 更新过程中可能只存在3个pod可用 可能出现负载高的问题
解决方案:
- 更新过程中 不允许减少pod maxSurge=1 maxUnavailable=0
2.7 扩容和缩容
容量管理类型
- 横向伸缩:增加或减少Pod数量
- 纵向(垂直)伸缩:调整Pod上的资源需求和资源限制
变动Deployment资源对象中的replicas字段的值即会触发应用规模的扩容或缩容
- 扩容和缩容是ReplicaSet的功能,具体操作由ReplicaSet完成
- 根据应用规模的需要进行手动配置
操作方法
- patch命令
- kubectl patch (-f FILENAME | TYPE NAME) [-p PATCH|--patch-file FILE] [options]
- 例如:kubectl patch deployment deploy-example -p '{"spec":{"replicas":3}}'
- 直接更新原配置文件,而后使用“kubectl apply”命令触发更新
扩展:自动扩容和缩容
- HPA(Horizontal Pod Autoscaler)控制器
- VPA(Vertical Pod Autoscaler)控制器
2.8 Deployment示例
2.8.1 滚动更新示例
查看demo名称空间下pod和deployment、replicaset资源
使用更新镜像的方式尝试修改ReplicaSet的pod模板,会实现滚动更新。get ReplicaSet发现多出来一个新的
查看pod名称中间部分均更新为新ReplicaSet的名称
回滚示例
查看历史版本,当前版本号为数字最大
使用undo回滚,默认回滚到前一个版本,使用--to-revision,可以指定回滚到哪个历史版本
回滚过程
回滚后,老版本的历史版本号由1变为3,之前新镜像的历史版本号保持不变
2.8.2 Deployment和ReplicaSet各字段解释
#NAME:Deployment 的名称.
#READY:表明就绪状态的 Pod 数量。格式为 X/Y:X 表示当前就绪的 Pod 数量。Y 表示 Deployment 期望的 Pod 数量(也就是指定的副本数量)。
#UP-TO-DATE:表示已经更新到最新版本的 Pod 数量。Deployment 会通过滚动更新(Rolling Update)逐步将旧版本的 Pod 替换为新版本。这里显示的是已经更新到最新版本的 Pod 数量。
#AVAILABLE:表示当前可用的 Pod 数量。可用的 Pod 既需要就绪,也需要满足最小可用性要求。
#AGE:Deployment 运行的时长。
#NAME:ReplicaSet 的名称
#DESIRED:ReplicaSet 希望运行的 Pod 数量。即 Deployment 中定义的 replicas 数量。这里 4 表示它期望有 4 个副本 Pod。
#CURRENT:ReplicaSet 当前实际拥有的 Pod 数量。
#READY:当前处于“就绪”状态的 Pod 数量。
#AGE:ReplicaSet 运行的时长。
#CONTAINERS:ReplicaSet 中管理的容器名称。
#IMAGES:ReplicaSet 中使用的容器镜像。
#SELECTOR:ReplicaSet 使用的标签选择器。pod-template-hash标签是自动添加的,与ReplicaSet名称后缀一致
2.8.3 创建deployment示例
[root@k8s-master01 ~]#kubectl create deployment nginx --image=nginx:1.24-alpine --replicas=3 --dry-run=client -o yaml -n demo > deployment-nginx.yaml
[root@k8s-master01 ~]#cat deployment-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: nginxname: nginxnamespace: demo
spec:replicas: 3selector:matchLabels:app: nginxversion: v1.24template:metadata:labels:app: nginxversion: v1.24spec:containers:- image: nginx:1.24-alpinename: nginx
默认创建Deployment后会自动添加如下字段
spec:progressDeadlineSeconds: 600 #表示 Kubernetes 等待更新完成的最长时间,单位为秒。在这个例子中是 600 秒(即 10 分钟)。如果在这个时间内,更新没有成功完成(即新 Pod 没有变为就绪状态),Deployment 会被标记为失败。replicas: 5revisionHistoryLimit: 10 #这个字段定义了 Kubernetes 会保留的旧 ReplicaSet 的数量(历史版本)。在这个例子中,Kubernetes 将保留 10 个历史版本。如果超过这个数量,最早的历史版本会被删除。selector:matchLabels:app: nginxversion: v1.20strategy: # Deployment在更新时使用的策略rollingUpdate:maxSurge: 100%maxUnavailable: 0type: RollingUpdate
删除deployment
2.8.4 扩缩容示例
注意:
- 使用命令可以扩缩容,但是为了避免使用命令扩容后,配置文件中数量未变动,再下一次应用配置文件时的数量与使用命令扩容后的数量不一致,建议直接修改配置文件扩容。
- 修改replicas哈希码不会变,不会触发滚动更新,滚动更新是在修改template时才会触发。
[root@k8s-master01 ~]#vim deployment-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: nginxname: nginxnamespace: demo
spec:replicas: 5 #修改数量 selector:matchLabels:app: nginxversion: v1.20template:metadata:creationTimestamp: nulllabels:app: nginxversion: v1.20spec:containers:- image: nginx:1.20-alpinename: nginx
查看pod数量为扩容后数量
2.8.5 滚动更新示例
[root@k8s-master01 ~]#vim deployment-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: nginxname: nginxnamespace: demo
spec:strategy:type: RollingUpdaterollingUpdate:maxSurge: 1maxUnavailable: 0replicas: 5selector:matchLabels:app: nginxversion: v1.20 #deployment创建后更新时标签选择器不能修改,否则无法应用template:metadata:creationTimestamp: nulllabels:app: nginxversion: v1.20 spec:containers:- image: nginx:1.22-alpine #修改镜像版本name: nginx
查看更新状态,提示副本已更新
查看pod状态,发现新版本正在创建
2.8.6 更新暂停示例
[root@k8s-master01 ~]#vim deployment-nginx.yaml name: nginxnamespace: demo
spec:strategy:type: RollingUpdaterollingUpdate:maxSurge: 1maxUnavailable: 0replicas: 5selector:matchLabels:app: nginxversion: v1.20template:metadata:creationTimestamp: nulllabels:app: nginxversion: v1.20spec:containers:- image: nginx:1.23-alpine #修改镜像版本name: nginx
修改镜像版本重新应用,使用pause暂定,用status查看更新状态
发现更新一个pod后则暂停更新了。该情况可以模拟金丝雀发布,先更新一个新版本暂停,测试发现没问题后再继续后续更新操作
使用resume继续完成更新
查看更新状态,全部更新完成
2.8.7 示例:模拟蓝绿发布效果
[root@k8s-master01 ~]#vim deployment-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: nginxname: nginxnamespace: demo
spec:strategy:type: RollingUpdaterollingUpdate:maxSurge: 100% #设置为100%,一次行全部创建完新版本再删除旧版本maxUnavailable: 0replicas: 5selector:matchLabels:app: nginxversion: v1.20template:metadata:creationTimestamp: nulllabels:app: nginxversion: v1.20spec:containers:- image: nginx:1.24-alpinename: nginx
查看pod发现一次性更新到新版本后,删除旧版本的pod
2.8.8 demoapp金丝雀和蓝绿发布示例
[root@k8s-master01 ~]#kubectl create deployment demoapp --image=ikubernetes/demoapp:v1.0 --replicas=5 --dry-run=client -o yaml -n demo > deployment-demoapp.yaml#创建service追加到文件中
[root@k8s-master01 ~]#kubectl create service loadbalancer demoapp --tcp=80:80 --dry-run=client -o yaml >> deployment-demoapp.yaml#修改yaml文件
[root@k8s-master01 ~]#cat deployment-demoapp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: demoappname: demoappnamespace: demo
spec:replicas: 5selector:matchLabels:app: demoappversion: stablestrategy: type: RollingUpdaterollingUpdate:maxSurge: 1maxUnavailable: 0template:metadata:labels:app: demoappversion: stablespec:containers:- image: ikubernetes/demoapp:v1.0name: demoapp
---
apiVersion: v1
kind: Service
metadata:labels:app: demoappname: demoappnamespace: demo
spec:ports:- name: 80-80port: 80protocol: TCPtargetPort: 80selector:app: demoappversion: stabletype: LoadBalancer[root@k8s-master01 ~]#kubectl apply -f deployment-demoapp.yaml
查看service的EXTERNAL-IP
使用curl命令访问service外部ip测试
修改镜像版本为1.1,执行pause暂停模拟金丝雀发布。
查看curl请求发现有个别响应为1.1的了。此时应有监控系统将v1.1版本纳入监控中观察一段时间,查看响应状态码为200无明显错误,则可以直接执行rollout resume继续后面的滚动更新,如果状态码为4xx 5xx则可以执行rollout undo回滚到历史版本。
执行resume恢复更新,再次查看curl请求响应结果反现过段时间响应均为1.1版本
蓝绿发布场景
修改滚动更新策略为100%,并应用配置生效。随后修改镜像版本为1.2。
修改后应用配置,查看pod发现创建新pod删除老pod
查看curl测试发现响应陆续切换为v1.2。
注意:该蓝绿操作不能实现真正意义上的蓝绿发布,需要配合应用层负载均衡器来实现
3. DaemonSet
3.1 DaemonSet的应用编排机制
使用DaemonSet编排应用
- 同Deployment相似,DaemonSet基于标签选择器管控一组Pod副本;
- 但是,DaemonSet用于确保所有或选定的工作节点上都运行有一个Pod副本
- 提示:DaemonSet的根本目标在于让每个节点一个 Pod
- 有符合条件的新节点进入时,DaemonSet会将Pod自动添加至相应节点;而节点的移出,相应的Pod副本也将被回收;
常用场景
- 特定类型的系统化应用,例如kube-proxy,以及Calico网络插件的节点代理calico-node等
- 集群存储守护进程、集群日志收集守护进程以及节点监控守护进程等
3.2 DaemonSet的资源规范
与Deployment相似,DaemonSet对象也使用标签选择器和Pod模板,区别之处在于,DaemonSet不需要定义replicas(Pod副本数量),其Pod数量随节点数量而定;
apiVersion: apps/v1 # API群组及版本;
kind: DaemonSet # 资源类型特有标识;
metadata:name <string> # 资源名称,在作用域中要惟一;namespace <string> # 名称空间;DaemonSet资源隶属名称空间级别;
spec:minReadySeconds <integer> # Pod就绪后多少秒内任一容器无crash方可视为“就绪”;selector <object> # 标签选择器,必须匹配template字段中Pod模板中的标签;template <object> # Pod模板对象;revisionHistoryLimit <integer> # 滚动更新历史记录数量,默认为10;updateStrategy <Object> # 滚动更新策略type <string> # 滚动更新类型,可用值有OnDelete和RollingUpdate;rollingUpdate <Object> # 滚动更新参数,专用于RollingUpdate类型;maxUnavailable <string> # 更新期间可比期望的Pod数量缺少的数量或比例;
apiVersion: apps/v1
kind: DaemonSet
metadata:name: daemonset-demonamespace: default
spec:selector:matchLabels:app: prometheuscomponent: node-exportertemplate:metadata:name: prometheus-node-exporterlabels:app: prometheuscomponent: node-exporterspec:containers:- image: prom/node-exporter:v1.2.0name: prometheus-node-exporterports:- name: prom-node-expcontainerPort: 9100hostPort: 9100hostNetwork: true #共享宿主机网络空间和PID空间hostPID: true
3.3 查看DaemonSet状态
使用kubectl get daemonset命令
~$ kubectl get daemonsets/daemonset-demo NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset-demo 2 2 2 2 2 2m ~$ kubectl get pods -l app=prometheus -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES daemonset-demo-694mw 1/1 Running 0 3m 172.29.1.11 k8s-node01.magedu.com daemonset-demo-lmnpd 1/1 Running 0 3m 172.29.1.12 k8s-node02.magedu.com
状态描述(示例集群中共有两个工作节点):
- DESIRED:期望存在的Pod副本数
- AVAILABLE:可用的Pod副本数
- CURRENT:当前已存在的Pod副本数
- NODE SELECTOR:节点选择器,表示未使用选择器,因而将适配到所有节点
- READY:当前已经转为就绪状态的Pod副本数
- AGE:资源已经创建的时长
- UP-TO-DATE :已经更新到期望版本的Pod副本数
使用kubectl describe daemonsets命令
3.4 更新策略
更新策略
- rollingUpdate:滚动更新,自动触发
- onDelete:删除时更新,手动触发
滚动更新
-
配置策略:rollingUpdate更新策略支持使用maxUnavailable参数来定义单批次允许更新的最大副本数量
-
更新方式
- ~$ kubectl set image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ...
- ~$ kubectl patch (-f FILENAME | TYPE NAME) [-p PATCH|--patch-file FILE] [options]
- 直接更新原配置文件,而后使用“kubectl apply”命令触发更新
DaemonSet示例
[root@k8s-master01 daemonsets]#cat daemonset-demo.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:name: daemonset-demonamespace: defaultlabels:app: prometheuscomponent: node-exporter
spec:selector:matchLabels:app: prometheuscomponent: node-exportertemplate:metadata:name: prometheus-node-exporterlabels:app: prometheuscomponent: node-exporterspec:containers:- image: prom/node-exporter:v1.5.0name: prometheus-node-exporterports:- name: prom-node-expcontainerPort: 9100hostPort: 9100livenessProbe:tcpSocket:port: prom-node-expinitialDelaySeconds: 3readinessProbe:httpGet:path: '/metrics'port: prom-node-expscheme: HTTPinitialDelaySeconds: 5hostNetwork: truehostPID: true[root@k8s-master01 daemonsets]#kubectl apply -f daemonset-demo.yaml
daemonset.apps/daemonset-demo created
默认只部署到了工作节点,控制节点没有,因为控制节点有污点。
查看工作节点9100端口开启
使用curl命令访问任一工作节点的9100端口的/mrteics路径都可以获取到prometheus格式的指标
滚动更新示例
[root@k8s-master01 daemonsets]#cat daemonset-demo.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:name: daemonset-demonamespace: defaultlabels:app: prometheuscomponent: node-exporter
spec:updateStrategy: #添加更新策略,最多不可用为1个type: RollingUpdaterollingUpdate:maxUnavailable: 1selector:matchLabels:app: prometheuscomponent: node-exportertemplate:metadata:name: prometheus-node-exporterlabels:app: prometheuscomponent: node-exporterspec:containers:- image: prom/node-exporter:v1.5.0name: prometheus-node-exporterports:- name: prom-node-expcontainerPort: 9100hostPort: 9100livenessProbe:tcpSocket:port: prom-node-expinitialDelaySeconds: 3readinessProbe:httpGet:path: '/metrics'port: prom-node-expscheme: HTTPinitialDelaySeconds: 5hostNetwork: truehostPID: true#应用策略生效
[root@k8s-master01 daemonsets]#kubectl apply -f daemonset-demo.yaml
daemonset.apps/daemonset-demo configured
修改版本,应用配置进行更新
rollout status查看状态
查看pod更新过程
4.StatefulSet
4.1 StatefulSet的编排机制
功能:负责编排有状态(Stateful Application)应用
- 有状态应用会在其会话中保存客户端的数据,并且有可能会在客户端下一次的请求中使用这些数据
- 应用上常见的状态类型:会话状态、连接状态、配置状态、集群状态、持久性状态等
- 大型应用通常具有众多功能模块,这些模块通常会被设计为有状态模块和无状态模块两部分
- 业务逻辑模块一般会被设计为无状态,这些模块需要将其状态数据保存在有状态的中间件服务上,如消息队列、数据库或缓存系统等
- 无状态的业务逻辑模块易于横向扩展,有状态的后端则存在不同的难题
StatefulSet控制器自Kubernetes v1.9版本才正式引入,为实现有状态应用编排,它依赖于几个特殊设计
- 各Pod副本分别具有唯一的名称标识,这依赖于一个专用的Headless Service实现
- 基于Pod管理策略(Pod Management Policy),定义创建、删除及扩缩容等管理操作期间,施加在Pod副本上的操作方式
- OrderedReady:创建或扩容时,顺次完成各Pod副本的创建,且要求只有前一个Pod转为Ready状态后,才能进行后一个Pod副本的创建;删除或缩容时,逆序、依次完成相关Pod副本的终止
- Parallel:各Pod副本的创建或删除操作不存在顺序方面的要求,可同时进行
- 各Pod副本存储的状态数据并不相同,因而需要专用且稳定的Volume
- 基于podTemplate定义Pod模板
- 在podTemplate上使用volumeTemplate为各Pod副本动态置备PersistentVolume
Pod副本的专用名称标识
- 每个StatefulSet对象强依赖于一个专用的Headless Service对象
- StatefulSet中的各Pod副本分别拥有唯一的名称标识
- 前缀格式为“$(statefulset_name)-$(ordinal)”
- 后缀格式为“$(service_name).$(namespace).cluster.local”
- 各Pod的名称标识可由ClustrDNS直接解析为Pod IP
volumeTemplate
- 在创建Pod副本时绑定至专有的PVC
- PVC的名称遵循特定的格式,从而能够与StatefulSet控制器对象的Pod副本建立关联关系
- 支持从静态置备或动态置备的PV中完成绑定
- 删除Pod(例如缩容),并不会一并删除相关的PVC
StatefulSet存在的问题
- 各有状态、分布式应用在启动、扩容、缩容等运维操作上的步骤存在差异,甚至完全不同,因而StatefulSet只能提供一个基础的编排框架
- 有状态应用所需要的管理操作,需要由用户自行编写代码完成
4.2 StatefulSet 资源规范及示例
除了标签选择器和Pod模板,StatefulSet必须要配置一个专用的Headless Service,而且还可能要根据需要,编写代码完成扩容、缩容等功能所依赖的必要操作步骤;
4.3 更新策略
更新策略
- rollingUpdate:滚动更新,自动触发
- onDelete:删除时更新,手动触发
滚动更新
-
配置策略
- maxUnavailable:定义单批次允许更新的最大副本数量
- partition <integer>:用于定义更新分区的编号,其序号大于等于该编号的Pod都将被更新,小于该分区号的都不予更新;默认编号为0,即更新所有副本;
-
更新方式
- ~$ kubectl set image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ...
- ~$ kubectl patch (-f FILENAME | TYPE NAME) [-p PATCH|--patch-file FILE] [options]
- 直接更新原配置文件,而后使用“kubectl apply”命令触发更新
4.4 Operator 简介
Operator 是增强型的控制器(Controller),它扩展了Kubernetes API的功能,并基于该扩展管理复杂应用程序
- Operator 是 Kubernetes 的扩展软件, 它利用定制的资源类型来增强自动化管理应用及其组件的能力,从而扩展了集群的行为模式
- 使用自定义资源(例如CRD)来管理应用程序及其组件
- 将应用程序视为单个对象,并提供面向该应用程序的自动化管控操作,例如部署、配置、升级、备份、故障转移和灾难恢复等
OperatorHub
4.5 StatefulSet示例
[root@k8s-master01 statefulsets]#cat demodb.yaml
# demodb ,an educational Kubernetes-native NoSQL data store. It is a distributed
# key-value store, supporting permanent read and write operations.
# Environment Variables: DEMODB_DATADIR, DEMODB_HOST, DEMODB_PORT
# default port: 9907/tcp for clients, 9999/tcp for members.
# Maintainter: MageEdu <mage@magedu.com>
---
apiVersion: v1
kind: Service #定义Headless Service,配置标签选择器
metadata:name: demodbnamespace: demolabels:app: demodb
spec:clusterIP: Noneports:- port: 9907 #服务对外暴露端口9907selector:app: demodb
---
apiVersion: apps/v1
kind: StatefulSet
metadata:name: demodbnamespace: demo
spec:selector:matchLabels:app: demodb serviceName: "demodb" #指定了与该 StatefulSet 关联的 Headless Servicereplicas: 2 #创建2个podtemplate: #定义pod模板metadata:labels:app: demodb #指定标签spec:containers: #定义容器名和镜像- name: demodb-shardimage: ikubernetes/demodb:v0.1ports:- containerPort: 9907 #容器暴露端口name: dbenv:- name: DEMODB_DATADIR #设置环境变量value: "/demodb/data"livenessProbe: #容器的存活探测initialDelaySeconds: 2 #器启动后 2 秒开始检查健康状况periodSeconds: 10 #每 10 秒进行一次检查。httpGet: #通过 HTTP 请求 /status 路径来检查容器的存活状态。path: /statusport: dbreadinessProbe: #容器的就绪探测initialDelaySeconds: 15 #在容器启动后 15 秒开始检查。periodSeconds: 30 #每 30 秒进行一次检查httpGet:path: /status?level=full #通过 HTTP 请求 /status?level=full 路径来检查容器的就绪状态port: db volumeMounts: #容器的存储卷挂载配置- name: datamountPath: /demodb/datavolumeClaimTemplates: # 定义了持久化存储卷的声明- metadata:name: data #卷名spec:accessModes: [ "ReadWriteOnce" ] #卷访问模式,表示卷只能被一个 Pod 挂载为读写storageClassName: "nfs-csi" #使用nfs-csi作为存储类,使用 NFS 作为存储后端。resources:requests:storage: 2Gi #每个 Pod 请求 2 Gi 的存储空间[root@k8s-master01 statefulsets]#kubectl apply -f demodb.yaml
service/demodb created
statefulset.apps/demodb created
按照序号创建pod,先0后1
每个pod都有与之匹配的独立的pvc
修改replicas值扩容,观察pod还是依次创建
缩容是按照序号从大到小进行的,缩容后之前创建的卷还在。
测试解析
更新
[root@k8s-master01 statefulsets]#cat demodb.yaml
# demodb ,an educational Kubernetes-native NoSQL data store. It is a distributed
# key-value store, supporting permanent read and write operations.
# Environment Variables: DEMODB_DATADIR, DEMODB_HOST, DEMODB_PORT
# default port: 9907/tcp for clients, 9999/tcp for members.
# Maintainter: MageEdu <mage@magedu.com>
---
apiVersion: v1
kind: Service
metadata:name: demodbnamespace: demolabels:app: demodb
spec:clusterIP: Noneports:- port: 9907selector:app: demodb
---
apiVersion: apps/v1
kind: StatefulSet
metadata:name: demodbnamespace: demo
spec:updateStrategy: #配置滚动更新策略type: RollingUpdaterollingUpdate:maxUnavailable: 1partition: 3 #只更新序号大于等于3的podselector:matchLabels:app: demodbserviceName: "demodb"replicas: 4template:metadata:labels:app: demodbspec:containers:- name: demodb-shardimage: ikubernetes/demodb:v0.1ports:- containerPort: 9907name: dbenv:- name: DEMODB_DATADIRvalue: "/demodb/data"livenessProbe:initialDelaySeconds: 2periodSeconds: 10httpGet:path: /statusport: dbreadinessProbe:initialDelaySeconds: 15periodSeconds: 30httpGet:path: /status?level=fullport: db volumeMounts:- name: datamountPath: /demodb/datavolumeClaimTemplates:- metadata:name: dataspec:accessModes: [ "ReadWriteOnce" ]storageClassName: "nfs-csi"resources:requests:storage: 2Gi[root@k8s-master01 statefulsets]#kubectl apply -f demodb.yaml
service/demodb unchanged
statefulset.apps/demodb configured
修改版本号更新
因为只有4个pod,最大pod编号为3只更新了3就暂停了。
修改partition为0,再次应用配置,则更新全部pod
从大到小依次更新
4.6 kafka Operator示例
strimzi kafka官网
k8s安装部署strimzi kafka
查看crds
kafka配置字段
k8s部署kafka集群
先创建完zookeeper后创建kafka的pod
查看kafka集群的service
创建topic测试
自定义topic可以参考下图示例
5. Job和Cronjob
5.1 Job控制器的应用编排机制
Job负责编排运行有结束时间的“一次性”任务,而前面的Deployment和DaemonSet主要负责编排始终运行的守护进程类应用;
- 控制器要确保Pod内的进程“正常(成功完成任务)”地退出
- 非正常退出的Pod可以根据需要重启,并在重试一次的次数后终止
- 有些Job是单次任务,也有些Job需要运行多次(次数通常固定)
- 有些任务支持同时创建及并行运行多个Pod以加快任务处理速度,Job控制器还允许用户自定义其并行度
需要周期性运行的Job,则由CronJob控制器负责编排
- CronJob建立在Job的功能之上,是更高层级的控制器
- 它以Job控制器完成单批次的任务编排,而后为这种Job作业提供需要运行的周期定义
5.2 Job资源规范
Job资源同样需要标签选择器和Pod模板,但它不需要指定replicas,而是应该给定completions,即需要完成的作业次数,默认为1次;
- Job资源会为其Pod对象自动添加“job-name=JOB_NAME”和“controller-uid=UID”标签,并使用标签选择器完成对controller-uid标签的关联,因此,selector并非必选字段
- Pod的命名格式:
$(job-name)-$(index)-$(random-string)
,其中的$(index)字段取值与completions和completionMode有关
注意
- Job资源所在群组为“batch/v1”
- Job资源中,Pod的RestartPolicy的取值只能为Never或OnFailure
5.3 Job的状态
使用kubectl get jobs命令
使用kubectl describe jobs命令
5.4 并行式Job
Job对象能够支持多个Pod的可靠、并发执行
- 编排彼此间相互通信的并行进程并非Job的设计目标,它仅用于支撑一组相互独立而又有所关联的工作任务的并行处理
- 常见的场景,有如发送电子邮件、渲染视频帧、编解码文件、NoSQL数据库中扫描主键范围等
并行式Job的关键配置参数
- parallelism:任务并行度,即最大可同行运行的Pod数量,可以将其理解为工作队列的数量
- completions:总共需要完成的任务数量,即总共需要多少个相关的Pod成功完成执行,通常要大于parallelism的值
5.5 CronJob
CronJob控制器用于管理Job资源的运行时间,它允许用户在特定的时间或以指定的间隔运行Job
CronJob控制器的功能类似于linux操作系统的周期性任务作业计划(crontab),用于控制作业运行的时间点及周期性运行的方式:
- 仅在未来某时间点将指定的作业运行一次
- 在指定的周期性时间点重复运行指定的作业
CronJob资源也是标准的API资源类型
注意: 在CronJob中,通配符“?”和“*”的意义相同,它们都表示任何可用的有效值
5.6 示例
5.6.1 Job示例
[root@k8s-master01 jobs_and_cronjobs]#cat job-demo.yaml
apiVersion: batch/v1
kind: Job
metadata:name: job-demo
spec:template:spec:containers:- name: myjobimage: ikubernetes/admin-box:v1.2imagePullPolicy: IfNotPresentcommand: ["/bin/sh", "-c", "sleep 60"]restartPolicy: Never #指定 Pod 的重启策略为 Never,即不论 Pod 如何退出(正常或异常),都不会重启它。这通常用于 Job,因为 Job 本质上是一次性任务,不需要重启 Pod。completions: 2 #指定 Job 成功完成的次数为2ttlSecondsAfterFinished: 3600 #定义 Job 资源在完成后保留的时间(以秒为单位)。backoffLimit: 3 #定义了 Job 失败后允许重试的次数。意味着如果Job失败了,Kubernetes会重试最多3次。如果在3次重试后仍然失败,Job 会被标记为失败。activeDeadlineSeconds: 300 #定义 Job 的最大运行时长(以秒为单位),这里设置为 300 秒(5 分钟)。如果 Job 在 5 分钟内未能完成,Kubernetes 会终止该 Job,即使重试次数还未用完。
5.6.2 并行运行job示例
[root@k8s-master01 jobs_and_cronjobs]#cat job-para-demo.yaml
apiVersion: batch/v1
kind: Job
metadata:name: job-para-demo
spec:template:spec:containers:- name: myjobimage: ikubernetes/admin-box:v1.2imagePullPolicy: IfNotPresentcommand: ["/bin/sh", "-c", "sleep 10"]restartPolicy: Nevercompletions: 7 #指定 Job 成功完成的次数为7次parallelism: 2 #每次并行运行2个ttlSecondsAfterFinished: 3600backoffLimit: 3activeDeadlineSeconds: 1200
过滤pod上的标签,查看pod发现为2个并行执行的
5.6.3 cronjob示例
[root@k8s-master01 jobs_and_cronjobs]#cat cronjob-demo.yaml
apiVersion: batch/v1
kind: CronJob
metadata:name: cronjob-demo #CronJob名称为cronjob-demonamespace: default
spec:schedule: "*/2 * * * *" #每隔2分钟运行一次jobTemplate:metadata:labels:controller: cronjob-demo #为job模板添加标签spec:parallelism: 1 #表示这个 Job 只允许一个 Pod 并行运行completions: 1 #表示这个 Job 只需要完成1个Pod成功运行后就算作完成。ttlSecondsAfterFinished: 600 #Job在完成后被保留的时间,600秒后,Job 将被清理。backoffLimit: 3 #Job允许失败的重试次数。默认情况下,如果 Job 失败了,Kubernetes 会重新尝试运行它,这里定义最多尝试 3 次。activeDeadlineSeconds: 60 #定义了Job最多可以运行的时间限制(60 秒)。如果在这段时间内 Job 没有完成,它将被强制终止template:spec:containers:- name: myjobimage: ikubernetes/admin-box:v1.2command:- /bin/sh- -c- date; echo Hello from CronJob, sleep a while...; sleep 10restartPolicy: OnFailure #Pod 的重启策略为 OnFailure,即只有在 Pod 运行失败的情况下才会重启startingDeadlineSeconds: 300 #定义了调度任务的时间窗口。如果由于某些原因(例如调度冲突、资源不可用等),Kubernetes 没能在规定的时间内启动任务,CronJob 控制器将在接下来的 300 秒内(5 分钟)继续尝试启动该任务,如果300秒内还是没能成功启动任务,那么该次调度将被放弃。
查看job cronjob信息
查看cronjob创建的pod日志