一、创建 pod 的工作流程
概念
- node 上所有组件(kubelet/kube-proxy)都是与 apiserver 通信
- master 上两个组件(scheduler/controller-manager)都是与 apiserver 通信
- apiserver 将其他组件通信产生的事件、状态都保存到了与 etcd 数据库中
- 其他组件与 apiserver 周期性 watch 事件。
图片解释:
kubectl 通过读取集群配置文件(~/.kube/config
)将请求发给 apiserver
,之后 apiserver
将创建 pod 的属性信息写入到 etcd 中,etcd 会响应一个状态给 apiserver,保存 etcd 成功会在控制台显示 pod/<pod名称> created
,之后调度组件(scheduler
)上线,负责将新的 pod 分配到合适的节点上,调度之后,将结果响应给 apiserver
,apiserver
再将结果保存到 etcd
中,kubelet
当发现有个 pod 被绑定到自己的节点上时,就会调用 docker
的 api
去创建容器,容器创建之后,docekr
会返回一个状态给 kubelet
,创建成功之后,kubelet
再通知 apiserver
容器状态,之后 apiserver
再将状态写入到 etcd
中,之后就可以使用 kubelet get pod
去查看 pod
的状态了
注意:有人会注意到,上图中少了两个组件,分别是
node
上的kube-proxy
和master
上的controller-manager
,其中kube-proxy
主要负责 pod 的服务发现和负载均衡,在图片中的位置就是介于kubelet
与Docker
之间,它的很多工作与kubelet
是并行完成的,主要负责提供pod
对外访问的一种形式。controller-manager
组件主要是完成后台的一些任务,例如 deployment 与 daemonset 控制器等,而图片中是不涉及到创建控制器的步骤的,故与controller-manager
就没什么关系,如果要创建控制器的话,角色位置介于Scheduler
与kubelet
之间,它负责创建多少个副本,启动多少个副本,滚动更新等更高级的功能。
总结:
kubectl( .kube/config) -> apiserver -> write etcd -> scheduler -> 调度结果响应给 apiserver -> kubelet 发现有分配到我的节点 pod -> 调用 docker api 创建容器 -> 通知 apiserver 容器状态
二、影响 Pod 调度的因素
参数解释:
resources
:pod 占用的硬件资源(资源调度依据)schedulerName
:默认调度器nodeName
:Scheduler 控制器调度绑定的节点nodeSelector
:标签选择器affinity
:节点亲和性tolerations
:污点容忍
注意:调度器(
schedulerName
)除了使用自己的一些默认行为和默认策略之外,也会参考其他调度策略的值(主要参考)。
2.1 resources:资源限制
Pod 和 Container 的资源请求和限制:
- spec.containers[].resources.limits.cpu
- spec.containers[].resources.limits.memory
- spec.containers[].resources.requests.cpu
- spec.containers[].resources.requests.memory
参数解释:
- requests:资源请求值,部署资源的最小配合,是调度依据,会根据 requests 的值去判定当前集群中有无节点去满足请求的量
- limits:资源最大使用
requests 必须小于 limits 的值!
扩展:查看当前节点的资源信息:
kubectl describe node <node-name>
注意: 若 pod 没有配置 resources 值,则 pod 可以使用宿主机所有资源,并且调度不参考配额
建议:requests
与 limits
不要相差太多
2.2 nodeSelector & nodeAffinity
2.2.1 nodeSelector
nodeSelector
:用于将 Pod 调度到匹配 Label
的 Node 上
给节点打标签并查看对应节点的标签:
kubectl label nodes <node-name> key=value
kubectl get nodes <node-name> --show-labels
应用场景:适用于多节点,且不同节点配置不一,功能不一的情况。
yaml 示例:
apiVersion: v1
kind: Pod
metadata:
name: pod-example
spec:
nodeSelector:
disktype: ssd
containers:
- name: nginx
image: nginx:1.15
注意:若没有匹配到任何标签,则 pod 会显示 pending 状态,节点都不可用
2.2.2 nodeAffinity
nodeAffinity
:节点亲和性,类似于 nodeSelector
,可以根据节点上的标签来约束 Pod 可以调度到哪些节点。
相比 nodeSelector
:
- 匹配有更多的逻辑组合,不只是字符串的完全相等(
nodeSelector
是绝对相等的匹配) - 调度分为软策略和硬策略,而不是硬性要求
a.硬(required):必须满足
b.软(preferred):尝试满足,但不保证
操作符:In、NotIn、Exists、DoesNotExist、Gt、Lt
yaml 示例:
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu
operator: In
values:
- nvidia-tesla
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: group
operator: In
values:
- ai
containers:
- name: web
image: nginx
参数解释:
requiredDuringSchedulingIgnoredDuringExecution
:硬策略,节点必须满足的条件operator
:操作符preferredDuringSchedulingIgnoredDuringExecution
:软策略,尝试满足,不是必须满足的条件weight
:权重值,范围 1-100,值越大,权重越大,pod 调度到对应标签的节点概率越高
注意:若硬限制(
requiredDuringSchedulingIgnoredDuringExecution
)没有匹配到任何标签,则 pod 会显示 pending 状态,节点都不可用,当打完标签之后,pending
会变为running
状态
补充:
节点亲和性
:希望调度到指定标签的节点上;
反亲和性
:不希望调度到指定标签的节点上,如使用NotIn
,DoesNotExist
等
2.3 Taint(污点)
Taints
:避免 Pod 调度到特定 Node 上
与 nodeSelector & nodeAffinity
区别:
nodeSelector & nodeAffinity
:将 pod 分配到某些节点。pod 属性Taints
:节点不允许分配 pod。节点属性
应用场景:
- 专用节点
- 配备了特殊硬件的节点
- 基于 Taint 的驱逐
查看节点污点:
kubectl describe node <node-name> | grep Taint
设置污点:
kubectl taint node <node-name> key=value:<effect>
其中 <effect>
可取值:
NoSchedule
:一定不能被调度(已经调度的不会被驱逐)。PreferNoSchedule
:尽量不要调度(软性)。NoExecute
:不仅不会调度,还会驱逐 Node 上已有的 Pod(若 pod 为设置污点容忍)。
去掉污点:
kubectl taint node <node-name> key:<effect>-
2.4 Tolerations(污点容忍)
Tolerations
:允许 Pod 调度到有特定 Taints 的 Node 上
yaml 示例:
apiVersion: v1
kind: Pod
metadata:
name: pod-taints
spec:
containers:
- name: pod-taints
image: busybox:latest
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
node 根据状态也会自动打一些污点:
node.kubernetes.io/not-ready
没有准备好(kubectl get node
)node.kubernetes.io/unreachable
不可调度(kubectl cordon <node-name>
)
容忍污点:不是强制性分配到具有污点的节点上,配置了容忍污点,在调度时忽略节点污点
2.5 nodeName
nodeName
:用于将 Pod 调度到指定的 Node 上,不经过调度器(default-scheduler
)
应用场景:
- 调度组件故障,希望临时救急
yaml 示例:
apiVersion: v1
kind: Pod
metadata:
name: pod-example
labels:
app: nginx
spec:
nodeName: k8s-node2
containers:
- name: nginx
image: nginx:1.15
2.6 DaemonSet(守护进程集)
DaemonSet
功能:
- 在每一个 Node 上运行一个 Pod
- 新加入的 Node 也同样会自动运行一个 Pod
应用场景:
- 网络插件
- Agent(zabbix-agent 监控)
- 日志采集(filebeat)
- C/S 架构软件
yaml 示例:
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: web
name: filebeat
spec:
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- image: nginx
name: nginx
resources: {}
通过 kubectl get pod
可以发现 filebeat 在每个 node 上都部署了一个:
[root@k8s-master k8s]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
filebeat-9kc9d 1/1 Running 0 8s 10.244.36.118 k8s-node1 <none> <none>
filebeat-wsbxb 1/1 Running 0 8s 10.244.169.183 k8s-node2 <none> <none>
查看 daemonset:
[root@k8s-master k8s]# kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
filebeat 2 2 2 2 2 <none> 3m2s
三、调度失败原因分析
- 查看调度结果:
kubectl get pod <NAME> -o wide
- 查看调度失败原因:
kubectl describe pod <NAME>
错误解析:
-
没有匹配到标签的提示信息:
0/3 nodes are available: 3 node(s) didn't match node selector. -
没有足够的 cpu 资源分配:
0/3 nodes are available: 3 Insufficient cpu. -
3 个节点有污点,没有配置污点容忍:
0/3 nodes are available: 3 node(s) had taints that the pod didn't tolerate.
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于