1、Kubernetes内置资源
1)、Pod
Pod是Kubernetes进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中
Pod可以认为是容器的封装,一个Pod中可以存在一个或者多个容器
1)Pod=进程组
在Kubernetes里面,Pod实际上正是Kubernetes抽象出来的一个可以类比为进程组的概念
由四个进程共同组成的一个应用Helloworld,在Kubernetes里面,实际上会被定义为一个拥有四个容器的Pod
就是说现在有四个职责不同、相互协作的进程,需要放在容器里去运行,在Kubernetes里面并不会把它们放到一个容器里,Kubernetes会把四个独立的进程分别用四个独立的容器启动起来,然后把它们定义在一个Pod里面
所以当Kubernetes把Helloworld给拉起来的时候,实际上会看到四个容器,它们共享了某些资源,这些资源都属于Pod,所以我们说Pod在Kubernetes里面只是一个逻辑单位,没有一个真实的东西对应说这个就是Pod。真正起来在物理上存在的东西,就是四个容器。这四个容器或者说是多个容器的组合就叫做Pod
Pod是Kubernetes分配资源的一个单位,因为里面的容器要共享某些资源,所以Pod也是Kubernetes的原子调度单位
2)为什么Pod必须是原子调度单位?
假如现在有两个容器,它们是紧密协作的,所以它们应该被部署在一个Pod里面。具体来说,第一个容器叫做App,就是业务容器,它会写日志文件;第二个容器叫做LogCollector,它会把刚刚App容器写的日志文件转发到后端的ElasticSearch中
两个容器的资源需求是这样的:App容器需要1G内存,LogCollector需要0.5G内存,而当前集群环境的可用内存是这样一个情况:Node_A:1.25G内存、Node_B:2G内存
假如说现在没有Pod概念,就只有两个容器,这两个容器要紧密协作、运行在一台机器上。可是,如果调度器先把App调度到了Node_A上面,接下来会怎么样呢?这时会发现:LogCollector实际上是没办法调度到Node_A上的,因为资源不够。其实此时整个应用本身就已经出问题了,调度已经失败了,必须去重新调度
在Kubernetes里,就直接通过Pod这样一个概念去解决了。因为在Kubernetes里,这样的一个App容器和LogCollector容器一定是属于一个Pod的,它们在调度时必然是以一个Pod为单位进行调度,所以这个问题是根本不存在的
3)Pod里面的容器是超亲密关系
Pod里面的容器是超亲密关系,大概分为以下几类:
- 比如说两个进程之间会发生文件交换,比如一个写日志,一个读日志
- 两个进程之间需要通过localhost或者说是本地的Socket去进行通信,这种本地通信也是超亲密关系
- 这两个容器或者是微服务之间,需要发生非常频繁的RPC调用,出于性能的考虑,也希望它们是超亲密关系
- 两个容器或者是应用,它们需要共享某些Linux Namespace。最简单常见的一个例子,就是我有一个容器需要加入另一个容器的Network Namespace。这样我就能看到另一个容器的网络设备,和它的网络信息
4)Infra container(也叫Pause容器)
每个Pod中都可以包含一个或者多个容器,这些容器可以分为两类:
-
业务容器(用户程序所在的容器):数量可多可少
-
Infra container:每个Pod都会有的一个根容器
共享网络:
如上图所示,这个Pod里有两个用户容器A和B,还有一个Infra container。Infra container是一个非常小的镜像,大概100~200KB左右,是一个汇编语言写的、永远处于暂停状态的容器
整个Pod里Infra container第一个启动,在Infra container Hold住Network Namespace后,用户容器就可以加入到Infra container的Network Namespace当中了
所以说一个Pod里面的所有容器,它们看到的网络视图是完全一样的。即:它们看到的网络设备、IP地址、Mac地址等等,跟网络相关的信息,其实全是一份,这一份都来自于Pod第一次创建的这个Infra container。这就是Pod解决网络共享的一个解法
这也就意味着,对于Pod里的容器A和容器B来说:
- 它们可以直接使用localhost进行通信
- 它们看到的网络设备跟Infra container看到的完全一样
- 一个Pod只有一个IP地址,也就是这个Pod的Network Namespace对应的IP地址
- 其他的所有网络资源,都是一个Pod一份,并且被该Pod中的所有容器共享
- Pod的生命周期只跟Infra container一致,而与容器A和B无关
而对于同一个Pod里面的所有用户容器来说,它们的进出流量,也可以认为都是通过Infra container完成的
共享存储:
有了Infra container这个设计之后,共享Volume就简单多了:Kubernetes只要把所有Volume的定义都设计在Pod层级即可
这样,一个Volume对应的宿主机目录对于Pod来说就只有一个,Pod里的容器只要声明挂载这个Volume,就一定可以共享这个Volume对应的宿主机目录。比如下面这个例子:
apiVersion: v1
kind: Pod
metadata:name: two-containers
spec:restartPolicy: Nevervolumes:- name: shared-datahostPath: path: /datacontainers:- name: nginx-containerimage: nginxvolumeMounts:- name: shared-datamountPath: /usr/share/nginx/html- name: debian-containerimage: debianvolumeMounts:- name: shared-datamountPath: /pod-datacommand: ["/bin/sh"]args: ["-c", "echo Hello from the debian container > /pod-data/index.html"]
在这个例子中,debian-container和nginx-container都声明挂载了shared-data这个Volume。而shared-data是hostPath类型。所以,它对应在宿主机上的目录就是:/data
。而这个目录,其实就被同时绑定挂载进了上述两个容器当中
这就是nginx-container可以从它的/usr/share/nginx/html
目录中,读取到debian-container生成的index.html文件的原因
Infra container的作用:
- 整个Pod的生命周期就等同于Infra container的生命周期,可以以它为依据,评估整个Pod的健康状态
- Pod里的多个业务容器共享Infra container的IP,共享Infra container挂载的Volume,既简化了密切关联的业务容器之间的通讯问题,也很好地解决了它们之间的文件共享问题
5)Pod生命周期
我们一般将Pod对象从创建至终的这段时间范围称为Pod的生命周期,它主要包含下面的过程:
- Pod创建过程
- 运行初始化容器(init container)过程
- 运行主容器(main container)
- 容器启动后钩子(post start)、容器终止前钩子(pre stop)
- 容器的存活性(liveness probes)、就绪(readiness probes)、启动(startup probes)探针
- Pod终止过程
6)容器探测
Kubernetes提供了三种探针来实现容器探测,分别是:
- liveness probes:存活性探针,用于判定是否需要重启容器,通常情况下主要用于检查容器是否无响应、死锁等,从而通过重启来提高应用的可用性
- readiness probes:就绪性探针,用来判定容器是否准备就绪,是否可以接受流量。当Pod内所有容器均就绪,则Pod将被认为已ready,如果没有,那么将从Service的Loader Blance中剔除该Pod
- startup probes:启动探针,Kubernetes v.1.18后支持,用于判定应用程序容器什么时候启动了,而启用这个探针主要目的是希望容器在启动成功后再进行存活性和就绪检查,确保这些存活、就绪探测器不会影响应用程序的启动。这可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉
启动探针案例:
ports:
- name: liveness-portcontainerPort: 8080hostPort: 8080
livenessProbe:httpGet:path: /healthzport: liveness-portfailureThreshold: 1periodSeconds: 10
startupProbe:httpGet:path: /healthzport: liveness-portfailureThreshold: 30periodSeconds: 10
有了启动探针后,应用将会有最多5分钟(30 * 10 = 300s)的时间来完成其启动过程。一旦启动探测成功一次,存活探测任务就会接管对容器的探测,对容器死锁作出快速响应。如果启动探测一直没有成功,容器会在300秒后被杀死,并且根据restartPolicy来执行进一步处理
相关资料:
配置存活、就绪和启动探针
2)、Deployment
1)ReplicaSet
ReplicaSet的主要作用是保证一定数量的Pod正常运行,它会持续监听这些Pod的运行状态,一旦Pod发生故障,就会重启或重建。同时它还支持对Pod数量的扩缩容和镜像版本的升降级
ReplicaSet的资源清单文件:
apiVersion: apps/v1 # 版本号
kind: ReplicaSet # 类型
metadata: # 元数据name: # rs名称 namespace: # 所属命名空间 labels: # 标签controller: rs
spec: # 详情描述replicas: 3 # 副本数量selector: # 选择器,通过它指定该控制器管理哪些podmatchLabels: # Labels匹配规则app: nginx-podmatchExpressions: # Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80
在这里面,需要新了解的配置项就是spec下面几个选项:
-
replicas:指定副本数量,其实就是当前RS创建出来的Pod的数量,默认为1
-
selector:选择器,它的作用是建立Pod控制器和Pod之间的关联关系,采用的Label Selector机制,在Pod模板上定义Label,在控制器上定义选择器,就可以表明当前控制器能管理哪些Pod了
-
template:模板,就是当前控制器创建Pod所使用的模板,就是Pod的定义
2)Deployment
Deployment不直接管理Pod,而是通过管理ReplicaSet来间接管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。所以Deployment比ReplicaSet功能更加强大
Deployment主要功能有下面几个:
- 支持ReplicaSet的所有功能
- 支持发布的停止、继续
- 支持滚动升级和回滚版本
Deployment的资源清单文件:
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据name: # rs名称 namespace: # 所属命名空间 labels: # 标签controller: deploy
spec: # 详情描述replicas: 3 # 副本数量revisionHistoryLimit: 3 # 保留历史版本paused: false # 暂停部署,默认是falseprogressDeadlineSeconds: 600 # 部署超时时间(s),默认是600strategy: # 策略type: RollingUpdate # 滚动更新策略rollingUpdate: # 滚动更新maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数maxUnavailable: 30% # 最大不可用状态的Pod的最大值,可以为百分比,也可以为整数selector: # 选择器,通过它指定该控制器管理哪些podmatchLabels: # Labels匹配规则app: nginx-podmatchExpressions: # Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80
3)控制器模型
Kubernetes所有的控制器都遵循一个通用编排模式,即:控制循环(control loop),这里有一段Go语言风格的伪代码,描述这个控制循环:
for {实际状态 := 获取集群中对象X的实际状态(Actual State)期望状态 := 获取集群中对象X的期望状态(Desired State)if 实际状态 == 期望状态{什么都不做} else {执行编排动作,将实际状态调整为期望状态}
}
在具体实现中,实际状态往往来自于Kubernetes集群本身。比如,kubelet通过心跳汇报的容器状态和节点状态,或者监控系统中保存的应用监控数据,或者控制器主动收集的它自己感兴趣的信息,这些都是常见实际状态的来源
而期望状态一般来自于用户提交的YAML文件。比如,Deployment对象中Replicas字段的值,这些信息往往都保存在etcd中
以Deployment为例,描述下它对控制器模型的实现:
- Deployment控制器从etcd中获取到所有携带了
app: nginx
标签的Pod,然后统计它们的数量,这就是实际状态 - Deployment对象的Replicas字段的值就是期望状态
- Deployment控制器将两个状态做比较,然后根据比较结果,确定是创建Pod,还是删除已有Pod(实际是通过ReplicaSet实现对Pod的操作)
类似Deployment这样的一个控制器,实际上都是由上半部分的控制器定义(包括期望状态),加上下半部分的被控制对象的模板组成的
控制器对象本身,负责定义被管理对象的期望状态。比如,Deployment里的replicas=2这个字段
而被控制对象的定义,则来自于一个模板。比如,Deployment里的template字段。可以看到,Deployment这个template字段里的内容,跟一个标准的Pod对象的API定义丝毫不差。而所有被这个Deployment管理的Pod实例,其实都是根据这个template字段的内容创建出来的
3)、DaemonSet
DaemonSet类型的控制器可以保证在集群中的每一台(或指定)节点上都运行一个副本。一般适用于日志收集、节点监控等场景。也就是说,如果一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建
DaemonSet创建的Pod有如下三个特征:
- 这个Pod运行在Kubernetes集群里的每一个节点(Node)上
- 每个节点上只有一个这样的Pod实例
- 当有新的节点加入Kubernetes集群后,该Pod会自动地在新节点上被创建出来;而当旧节点被删除后,它上面的Pod也相应地会被回收掉
DaemonSet的资源清单文件:
apiVersion: apps/v1 # 版本号
kind: DaemonSet # 类型
metadata: # 元数据name: # rs名称 namespace: # 所属命名空间 labels: # 标签controller: daemonset
spec: # 详情描述revisionHistoryLimit: 3 # 保留历史版本updateStrategy: # 更新策略type: RollingUpdate # 滚动更新策略rollingUpdate: # 滚动更新maxUnavailable: 1 # 最大不可用状态的Pod的最大值,可以为百分比,也可以为整数selector: # 选择器,通过它指定该控制器管理哪些podmatchLabels: # Labels匹配规则app: nginx-podmatchExpressions: # Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80
4)、Job、CronJob
1)Job
Job主要用于负责**批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)**任务。Job特点如下:
- 当Job创建的Pod执行成功结束时,Job将记录成功结束的Pod数量
- 当成功结束的Pod达到指定的数量时,Job将完成执行
Job的资源清单文件:
apiVersion: batch/v1 # 版本号
kind: Job # 类型
metadata: # 元数据name: # rs名称 namespace: # 所属命名空间 labels: # 标签controller: job
spec: # 详情描述completions: 1 # 指定job需要成功运行Pods的次数。默认值:1parallelism: 1 # 指定job在任一时刻应该并发运行Pods的数量。默认值:1activeDeadlineSeconds: 30 # 指定job可运行的时间期限,超过时间还未结束,系统将会尝试进行终止backoffLimit: 6 # 指定job失败后进行重试的次数。默认是6manualSelector: true # 是否可以使用selector选择器选择pod,默认是falseselector: # 选择器,通过它指定该控制器管理哪些podmatchLabels: # Labels匹配规则app: counter-podmatchExpressions: # Expressions匹配规则- {key: app, operator: In, values: [counter-pod]}template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: counter-podspec:restartPolicy: Never # 重启策略只能设置为Never或者OnFailurecontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"]
关于重启策略设置的说明:
- 如果指定为OnFailure,则Job会在Pod出现故障时重启容器,而不是创建Pod,failed次数不变
- 如果指定为Never,则Job会在Pod出现故障时创建新的Pod,并且故障Pod不会消失,也不会重启,failed次数加1
- 如果指定为Always的话,就意味着一直重启,意味着Job任务会重复去执行了,当然不对,所以不能设置为Always
2)CronJob
CronJob控制器以Job控制器资源为其管控对象,并借助它管理Pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行Job任务
CronJob的资源清单文件:
apiVersion: batch/v1beta1 # 版本号
kind: CronJob # 类型
metadata: # 元数据name: # rs名称 namespace: # 所属命名空间 labels: # 标签controller: cronjob
spec: # 详情描述schedule: # cron格式的作业调度运行时间点,用于控制任务在什么时间执行concurrencyPolicy: # 并发执行策略,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业failedJobHistoryLimit: # 为失败的任务执行保留的历史记录数,默认为1successfulJobHistoryLimit: # 为成功的任务执行保留的历史记录数,默认为3startingDeadlineSeconds: # 启动作业错误的超时时长jobTemplate: # job控制器模板,用于为cronjob控制器生成job对象;下面其实就是job的定义metadata:spec:completions: 1parallelism: 1activeDeadlineSeconds: 30backoffLimit: 6manualSelector: trueselector:matchLabels:app: counter-podmatchExpressions: 规则- {key: app, operator: In, values: [counter-pod]}template:metadata:labels:app: counter-podspec:restartPolicy: Never containers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 20;done"]
需要重点解释的几个选项:
schedule: cron表达式,用于指定任务的执行时间
*/1 * * * *<分钟> <小时> <日> <月份> <星期>分钟值从0到59小时值从0到23日值从1到31月值从1到12星期值从0到6,0代表星期日多个时间可以用逗号隔开,范围可以用连字符给出,*可以作为通配符,/表示每...
concurrencyPolicy:
- Allow:允许Jobs并发运行(默认)
- Forbid:禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行
- Replace:替换,取消当前正在运行的作业并用新作业替换它
5)、StatefulSet
1)工作原理
首先,StatefulSet的控制器直接管理的是Pod。这是因为,StatefulSet里的不同Pod实例,不再像ReplicaSet中那样都是完全一样的,而是有了细微区别的。比如,每个Pod的hostname、名字等都是不同的、携带了编号的。而StatefulSet区分这些实例的方式,就是通过在Pod的名字里加上事先约定好的编号
其次,Kubernetes通过Headless Service,为这些有编号的Pod,在DNS服务器中生成带有同样编号的DNS记录。只要StatefulSet能够保证这些Pod名字里的编号不变,那么Service里类似于web-0.nginx.default.svc.cluster.local
这样的DNS记录也就不会变,而这条记录解析出来的Pod的IP地址,则会随着后端Pod的删除和再创建而自动更新。这当然是Service机制本身的能力,不需要StatefulSet操心
最后,StatefulSet还为每一个Pod分配并创建一个同样编号的PVC。这样,Kubernetes就可以通过Persistent Volume机制为这个PVC绑定上对应的PV,从而保证了每一个Pod都拥有一个独立的Volume
在这种情况下,即使Pod被删除,它所对应的PVC和PV依然会保留下来。所以当这个Pod被重新创建出来之后,Kubernetes会为它找到同样编号的PVC,挂载这个PVC对应的Volume,从而获取到以前保存在Volume里的数据
2)partition
partition这种更新策略的含义是,若当前StatefulSet的副本数为5个,则Pod名为pod-0~pod-4,那么此时定义partition=4,就意味着我要更新大于等于4的Pod,而只有pod-4的ID是大于等于4的,所以只有pod-4会被更新,其它不会,这就是金丝雀更新。若后期发现pod-4更新后,工作一切正常,那么就可以调整partition=0,这样只要ID大于等于0的pod都将被更新
6)、Service
Service会对提供同一个服务的多个Pod进行聚合,并且提供一个统一的入口地址。通过访问Service的入口地址就能访问到后面的Pod服务
Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个kube-proxy服务进程。当创建Service的时候会通过API Server向etcd写入创建的Service的信息,而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将最新的Service信息转换成对应的访问规则
1)kube-proxy工作模式
kube-proxy目前支持三种工作模式:
userspace模式
userspace模式下,kube-proxy会为每一个Service创建一个监听端口,发向Cluster IP的请求被Iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据LB算法选择一个提供服务的Pod并和其建立链接,以将请求转发到Pod上
该模式下,kube-proxy充当了一个四层负载均衡器的角色。由于kube-proxy运行在userspace中,在进行转发处理时会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低
iptables模式
iptables模式下,kube-proxy为Service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP
该模式下kube-proxy不承担四层负责均衡器的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试
ipvs模式
ipvs模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高。除此以外,ipvs支持更多的LB算法
2)Service类型
Service有以下4种类型:
- ClusterIP:用于在集群内部互相访问的场景,通过ClusterIP访问Service
- NodePort:用于从集群外部访问的场景,通过节点上的端口访问Service
- LoadBalancer:用于从集群外部访问的场景,其实是NodePort的扩展,通过一个特定的LoadBalancer访问Service,这个LoadBalancer将请求转发到节点的NodePort,而外部只需要访问LoadBalancer
- Headless Service:用于Pod间的互相发现,该类型的Service并不会分配单独的ClusterIP, 而且集群也不会为它们进行负载均衡和路由。通过指定
spec.clusterIP
为None来创建Headless Service
3)Endpoints
Endpoints是Kubernetes中的一个资源对象,存储在etcd中,用来记录一个Service对应的所有Pod的访问地址,它是根据Service配置文件中selector描述产生的
一个Service由一组Pod组成,这些Pod通过Endpoints暴露出来,Endpoints是实际服务的端点集合。换句话说,Service和Pod之间的联系是通过Endpoints实现的
只有处于Running状态,且readinessProbe检查通过的Pod,才会出现在Service的Endpoints列表里。并且,当某一个Pod出现问题时,Kubernetes会自动把它从Service里摘除掉
# 查看endpoints
[root@k8s-master ~]# kubectl get endpoints -n dev
NAME ENDPOINTS AGE
service-clusterip 10.244.1.22:80,10.244.2.15:80,10.244.2.16:80 5m10s
7)、Ingress
Service对集群之外暴露服务的主要方式有两种:NotePort和LoadBalancer,但是这两种方式,都有一定的缺点:
- NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显
- LB方式的缺点是每个service需要一个LB,并且需要Kubernetes之外设备的支持
基于这种现状,Kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。工作机制大致如下图表示:
实际上,Ingress相当于一个7层的负载均衡器,是Kubernetes对反向代理的一个抽象,它的工作原理类似于Nginx,可以理解成在Ingress里建立诸多映射规则,Ingress Controller通过监听这些配置规则并转化成Nginx的反向代理配置 , 然后对外部提供服务。在这里有两个核心概念:
- Ingress:Kubernetes中的一个对象,作用是定义请求如何转发到Service的规则
- Ingress Controller:具体实现反向代理及负载均衡的程序,对Ingress定义的规则进行解析,根据配置的规则来实现请求转发,实现方式有很多,比如Nginx、Haproxy等等
Ingress(以Nginx为例)的工作原理如下:
- 用户编写Ingress规则,说明哪个域名对应Kubernetes集群中的哪个Service
- Ingress控制器动态感知Ingress服务规则的变化,然后生成一段对应的Nginx反向代理配置
- Ingress控制器会将生成的Nginx配置写入到一个运行着的Nginx服务中,并动态更新
- 到此为止,其实真正在工作的就是一个Nginx了,内部配置了用户定义的请求转发规则
一个Nginx Ingress Controller提供的服务,其实是一个可以根据Ingress对象和被代理后端Service的变化,来自动进行更新的Nginx负载均衡器
2、Kubernetes Device Plugin机制
Kuberentes通过Extended Resource来支持自定义资源,比如GPU。为了让调度器知道这种自定义资源在各Node上的数量,需要的Node里添加自定义资源的数量。实际上,这些信息并不需要人工去维护,所有的硬件加速设备的管理都通过Device Plugin插件来支持,也包括对该硬件的Extended Resource进行上报的逻辑
上报资源信息:
Device Plugin通过gRPC与本机kubelet连接 -> Device Plugin定期向kubelet汇报设备信息,比如GPU的数量 -> kubelet向API Server发送的心跳中,以Extended Reousrce的方式加上这些设备信息,比如GPU的数量
Pod分配一个GPU的流程:
Pod申明需要一个GPU -> 调度器找到GPU数量满足条件的Node -> Pod绑定到对应的Node上 -> kubelet发现需要拉起一个Pod,且该Pod需要GPU -> kubelet向Device Plugin发起Allocate()
请求 -> Device Plugin根据kubelet传递过来的需求,找到这些设备对应的设备路径和驱动目录,并返回给kubelet -> kubelet将这些信息追加在创建Pod所对应的CRI请求中 -> 容器创建完成之后,就会出现这个GPU设备(设备路径+驱动目录)
推荐阅读:
《Kubernetes权威指南 第4版》
深入剖析 Kubernetes
Kubernetes学习笔记(一):Pod详解:Pod配置、Pod生命周期、Pod调度、容器设计模式
Kubernetes学习笔记(二):Pod控制器详解:资源元信息、ReplicaSet、Deployment、DaemonSet、Job、CronJob
Kubernetes学习笔记(三):Service与Ingress
Kubernetes学习笔记(四):持久化存储与安全认证
18 | 深入理解StatefulSet(一):拓扑状态
19 | 深入理解StatefulSet(二):存储状态
20 | 深入理解StatefulSet(三):有状态应用实践